# Update, back up and troubleshoot

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

Source: https://app.getoatmilk.com/docs/self-hosting/operate

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

```bash
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

```bash
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:

```bash
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:

```bash
bun run self-host up
```

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

```bash
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

| 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](https://app.getoatmilk.com/docs/self-hosting/local-models.md), 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 |
