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.
| Command | Does |
|---|---|
bun run self-host status | Each service, and whether the site answers |
bun run self-host doctor | Checks Docker, the settings, the address, the certificate, the site and the model server, and says what to fix |
bun run self-host logs app | Follows one service's logs: app, db, storage, postgrest, caddy, redis, db-init or migrate |
bun run self-host up | Builds after an update and starts everything; new migrations apply on the way |
bun run self-host migrate | Applies new migrations without restarting |
bun run self-host backup | Saves the bundled database and the files to self-host/backups |
bun run self-host cert | Saves the local certificate authority for *.localhost and says how to trust it |
bun run self-host down | Stops everything; your data stays in Docker volumes |
Update
bun run self-host backup
git pull
bun install
bun run self-host upup 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
bun run self-host backupIt writes two files to self-host/backups, readable only by you:
oatmilk-<time>.dump: the bundled database, frompg_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:
crontab -e
# 30 3 * * * cd /home/me/oatmilk && /home/me/.bun/bin/bun run self-host backupRestore
Restore on a machine with the same self-host/.env. First start Oatmilk once, so the database and its roles exist:
bun run self-host upThen stop what uses the data, put the database and files back, and start again:
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 upTest 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/.envis 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
appcontainer. Background work keeps its state on theworkflowsvolume, which copies can't share. - The image is about 5 GB, and the first build takes about 10 minutes.
Troubleshooting
| Problem | Fix |
|---|---|
| The browser says the certificate isn't trusted | bun run self-host cert, then run the command it prints |
Another program can't open oatmilk.localhost | Add 127.0.0.1 oatmilk.localhost to /etc/hosts. Browsers and the CLI don't need it. |
| Ports 80 or 443 are taken | Run bun run self-host setup again and answer No to the standard ports |
| A database step fails | bun 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 read | No 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 server | On 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 account | Sign-up is closed: invite them in Settings › Team, or bun run self-host user add |
| Invitations say they aren't set up | ACCOUNTING_CONTRACTOR_LINK_SECRET in self-host/.env must be at least 40 characters; setup writes one |
| The site doesn't answer after an update | bun run self-host logs app, then bun run self-host up again once the cause is fixed |