Running it yourself

Ontoplano is one Node process and one SQLite file. There is no database server to run, no queue, no cache, and nothing that phones anywhere. A $5 VPS is oversized for one person.

Self-hosting is the whole app. Every feature, no ceilings, no licence key, permanently. What the hosted instance sells is somebody else doing the upgrades and the backups.

What it needs

The quickest start: a package

For a machine you keep, this is the shortest route: one command, a service that starts on boot, and upgrades through the package manager you already use.

Debian, Ubuntu, Mint, Pop!_OS, Raspberry Pi OS

curl -LO https://github.com/ontoplano/ontoplano/releases/latest/download/ontoplano_amd64.deb
sudo apt install ./ontoplano_amd64.deb

Fedora, RHEL and its rebuilds, openSUSE

curl -LO https://github.com/ontoplano/ontoplano/releases/latest/download/ontoplano.x86_64.rpm
sudo dnf install ./ontoplano.x86_64.rpm     # or: sudo zypper install ./ontoplano.x86_64.rpm

Arch, Manjaro, EndeavourOS — from the AUR, built on your machine:

yay -S ontoplano       # or: paru -S ontoplano

Then, on any of them:

sudo ontoplano config                        # set ORIGIN to the address you will type
sudo systemctl enable --now ontoplano

That is the whole installation. It listens on 127.0.0.1:1493 and is not reachable from anywhere else until you put a reverse proxy in front of it — which is where TLS belongs.

What the package sets up

What Where
ontoplano a system account with no shell and no password, which the service runs as
/etc/ontoplano/ontoplano.env your settings — the origin, mail, who may register
/etc/ontoplano/secret.env the session secret, generated once on install
/var/lib/ontoplano/ontoplano.db the database. This file is your data.
ontoplano.service a system unit, enabled by you, restarted on failure

A system service rather than a user one, deliberately. A user service only runs while that user is logged in unless lingering is enabled for them, cannot be enabled by a package on anybody's behalf, and gives the app the run of your home directory. The service account here owns exactly one directory and can reach nothing else on the machine — systemctl cat ontoplano shows what it is allowed to do. A user unit is still the right answer for a machine you develop on, which is what make install-service sets up.

Upgrading is apt install ./ontoplano_amd64.deb again, or yay -Syu. The database is migrated before the new version starts, your settings file is left exactly as you edited it, and the session secret is never regenerated — everybody stays signed in.

Removing the package leaves /var/lib/ontoplano behind. It is your data, not the package's; apt purge ontoplano is the word that means take it too.

The commands it installs

ontoplano status        # is it running, and since when
ontoplano logs          # follow the journal
sudo ontoplano config   # open the settings file
sudo ontoplano migrate  # bring the database up to the installed version
ontoplano version

Why the .deb and the .rpm are 45MB

They carry their own Node. The database driver is a native module compiled against one Node ABI, and these distributions ship four different Node versions between them — a package built against one of them would install cleanly everywhere and start nowhere. The Arch package does not bundle anything, because it is built on the machine that will run it.

Or with Docker

One container, one volume, no database server. Migrations run when it starts, so there is no first-run step.

docker run -d --name ontoplano -p 1493:1493 \
  -v ontoplano-data:/data \
  -e ORIGIN=http://localhost:1493 \
  -e BETTER_AUTH_SECRET="$(openssl rand -base64 32)" \
  ontoplano/ontoplano:latest

Or with compose, which is the same thing written down:

curl -O https://raw.githubusercontent.com/ontoplano/ontoplano/master/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/ontoplano/ontoplano/master/.env.example
# set ORIGIN and BETTER_AUTH_SECRET in .env
docker compose up -d

Register at /login. The first account is always allowed and owns the instance; the first screen asks for your timezone and which day your week starts, and offers a starter week to argue with.

docs/DOCKER.md has the reverse proxy, upgrading and backups.

On Windows

There is no Windows installer yet. Windows is the one platform here with nothing to point at: no MSI, no winget package, no service wrapper.

Docker Desktop works today, and so does WSL2 — inside WSL the .deb above installs exactly as it does on Ubuntu, which is the shortest route if you are on Windows right now.

A proper installer is a good first contribution, and an unusually self-contained one: the app itself is one Node process and one SQLite file, so what is missing is the packaging around it — an MSI or a winget manifest, a service registration, and somewhere sensible to put the database. If you want to build it, CONTRIBUTING.md says how the repository works and how to send it, and opening an issue first is welcome so nobody does the same work twice.

From the source

git clone https://github.com/ontoplano/ontoplano.git
cd ontoplano
yarn
cp .env.example .env      # set ORIGIN and BETTER_AUTH_SECRET
yarn db:migrate           # create the database
yarn dev                  # http://localhost:1493

As a service

For a box you keep, without Docker:

make install-service      # build, migrate, install and start a systemd user unit
make deploy-local         # after changes: rebuild, migrate, redeploy, restart

The unit runs as your own user, keeps running after logout, and snapshots the database before every migration.

Where your data lives

Nothing is inside the repository, and everything is created on first run.

Path What
~/.local/share/ontoplano/ontoplano.db the database — this one file is your data
~/.config/ontoplano/config.toml bind address, port, database path, who may register
~/.config/ontoplano/env ORIGIN, BETTER_AUTH_SECRET, SMTP, backups

Installed from a package, the same three live under the system paths instead — /var/lib/ontoplano/ontoplano.db, and /etc/ontoplano/ for both config files. Docker puts all of them in the volume mounted at /data.

Back up the first one and you have backed up everything. sqlite3 … .backup or a file copy while the process is stopped both work; there is a Litestream setup in docs/BACKUP.md for continuous replication.

Who may register

Closed by default. The first account is always allowed — it is the one that owns the instance — and after that /settings/instance decides: closed, by invitation, or open. An invitation is a code that works once, and that page makes and revokes them.

An instance on the open internet with sign-up left open is one that somebody else will use.

The weekly review, by mail

On Monday morning ontoplano can send one message saying what last week was: how much of what you planned you did, where most of it went, and what is still sitting there unanswered. It links to /tasks/review, which is where you do something about a week.

It is on for an account whose address has been confirmed, and every message carries a link that turns it off in one click with nothing to sign in to. Nobody is written to about a week they did not plan, and nobody is written to twice — the week last written about is remembered per account.

Nothing is sent unless the instance has SMTP configured, so a self-hosted install with no mail transport is simply an install with no Monday mail.

It needs SMTP, and its own switch. Reminders come installed everywhere — the packages and make install-service bring the timer, the Docker image runs the clock itself. The weekly mail is opt-in, because it can send nothing without SMTP configured: from source, make install-mail-service; on a packaged install, a systemd timer that POSTs /api/jobs/weekly-reviews hourly with the health token (the from-source unit in systemd/ontoplano-weekly-review.service is the template). The Docker image asks the endpoint regardless — harmless without SMTP. Settings → Instance shows the jobs and when they last ran.

Anything else that can make an hourly HTTP request works too — the timers are only asking the app's own endpoint:

5 * * * * curl -fsS -m 30 -X POST -H "x-health-token: $ONTOPLANO_HEALTH_TOKEN" http://127.0.0.1:1493/api/jobs/weekly-reviews

Hourly rather than daily because the hour belongs to the account: seven in the morning is a different instant for everybody, and each account is checked against its own timezone. Twenty-three of those runs do nothing, and running it twice is safe.