Self-hosting

Update, back up and troubleshoot

Keep a self-hosted Oatmilk healthy: updates, backups, restores, logs, security and common fixes.

Every command runs from the Oatmilk folder.

CommandDoes
bun run self-host statusEach service, and whether the site answers
bun run self-host doctorChecks Docker, the settings, the address, the certificate, the site and the model server, and says what to fix
bun run self-host logs appFollows one service's logs: app, db, storage, postgrest, caddy, redis, db-init or migrate
bun run self-host upBuilds after an update and starts everything; new migrations apply on the way
bun run self-host migrateApplies new migrations without restarting
bun run self-host backupSaves the bundled database and the files to self-host/backups
bun run self-host certSaves the local certificate authority for *.localhost and says how to trust it
bun run self-host downStops everything; your data stays in Docker volumes

Update

Shell
bun run self-host backup
git pull
bun install
bun run self-host up

up rebuilds the image, applies any new database migrations, then starts the new version. The site is down for the minute or so the app takes to start.

Back up

Shell
bun run self-host backup

It writes two files to self-host/backups, readable only by you:

  • oatmilk-<time>.dump: the bundled database, from pg_dump -Fc. With your own database, use your provider's backups instead.
  • files-<time>.tar.gz: the files on the machine. With an S3-compatible bucket, use the bucket's versioning or replication instead.

Keep a copy of self-host/.env with your backups, somewhere only you can read. It holds every secret of the installation, including the keys that encrypt connector credentials and contractor details: a restored database is no use without it.

Run backups on a schedule and copy them off the machine:

Shell
crontab -e
# 30 3 * * * cd /home/me/oatmilk && /home/me/.bun/bin/bun run self-host backup

Restore

Restore on a machine with the same self-host/.env. First start Oatmilk once, so the database and its roles exist:

Shell
bun run self-host up

Then stop what uses the data, put the database and files back, and start again:

Shell
docker compose -f self-host/compose.yaml stop app storage postgrest
docker compose -f self-host/compose.yaml exec -T db pg_restore -U postgres -d oatmilk --clean --if-exists < self-host/backups/oatmilk-….dump
docker compose -f self-host/compose.yaml run --rm --no-deps -T --entrypoint tar storage xzf - -C /var/lib/storage < self-host/backups/files-….tar.gz
bun run self-host up

Test a restore on another machine now and then, so you know it works before you need it.

Security

  • Only ports 80 and 443 are published. The database, its API and storage's admin endpoints are never reachable from outside, and files reach browsers only through short-lived signed links.
  • self-host/.env is written so only you can read it. Keep it that way.
  • On a server, keep sign-up closed unless you mean to run an open service, and turn on two-step sign-in for administrators.
  • Keep the server's own updates on, for example with Ubuntu's unattended upgrades, and update Oatmilk regularly.

Limits

  • Run one app container. Background work keeps its state on the workflows volume, which copies can't share.
  • The image is about 5 GB, and the first build takes about 10 minutes.

Troubleshooting

ProblemFix
The browser says the certificate isn't trustedbun run self-host cert, then run the command it prints
Another program can't open oatmilk.localhostAdd 127.0.0.1 oatmilk.localhost to /etc/hosts. Browsers and the CLI don't need it.
Ports 80 or 443 are takenRun bun run self-host setup again and answer No to the standard ports
A database step failsbun run self-host logs db-init migrate names the step; a managed database's user may lack the right to create roles or extensions
Documents aren't readNo AI models are set up: follow Use local AI models, or add AI_GATEWAY_API_KEY
doctor says the app can't reach the model serverOn Linux, start Ollama with OLLAMA_HOST=0.0.0.0, or turn on Serve on Local Network in LM Studio
A teammate can't make an accountSign-up is closed: invite them in Settings › Team, or bun run self-host user add
Invitations say they aren't set upACCOUNTING_CONTRACTOR_LINK_SECRET in self-host/.env must be at least 40 characters; setup writes one
The site doesn't answer after an updatebun run self-host logs app, then bun run self-host up again once the cause is fixed