Sage

Your portfolio. Your server. Your data.

A self-hosted portfolio tracker built around dividends. The front page shows what your holdings will pay you over the next year, not what they're worth today.

AGPL-3.0·Docker · Postgres·Public beta
sage.yourdomain.com/overview⌘K
Income · next twelve months
€1,501.70 +€111.88 on last year24 payments ahead
ASML goes ex-dividend in 4 days · Realty Income pays on the 15th
OCTJAN2026APRJULOCTJAN2027APRJULTODAY
Paid Confirmed Estimated
Book value €48,239.12 Today +€214.36 · +0.45% 5 holdings
NONOVONovo Nordisk€9,412.20+1.24%
ASASMLASML Holding€7,861.04+0.82%
ALALVAllianz SE€6,240.50−0.31%
OORealty Income€4,988.75+0.47%
VWVWRLVanguard FTSE All-W€12,304.88+0.58%
Sample data — an interface preview, not a real portfolio.

Income first, not net worth

Forward income, every payment behind and ahead of you, yield on cost, a calendar and a goal to track. Not a net-worth app with a dividend tab bolted on.

Postgres on your own machine

There is no Sage server. Your holdings, cost basis and transactions never leave the box you run it on. By default your browser talks to nothing but your own server.

AGPL-3.0, all of it

Read the code, change it, run it as long as you like. No account and no paid tier. The importer, the maths and the tests are all in the same repo.

Run it on your own box

The web app, the API and Postgres come up together under one compose file. Import a broker CSV and you have your book. Set ALLOW_SIGNUP=false afterwards to keep the instance to yourself.

$ git clone https://github.com/bjarkeef/sage.git
$ cd sage && cp .env.example .env
# set BETTER_AUTH_SECRET to a long random string
$ docker compose up -d --build

It's a public beta. I run it on my own book every day, and the maths is tested. A few things to know before you install it: the default price source is unofficial, so it can break. You import from a broker CSV, not a bank connection. There's no cash account yet.