# Run Oatmilk on this computer

> Keep your books, files and AI models on your own computer with oatmilk setup --local, then run it day to day.

Source: https://app.getoatmilk.com/docs/terminal/local

The CLI can set up a whole Oatmilk on your own computer and look after it for you. You choose **Keep everything on this computer** once. After that, `oatmilk local`, `oatmilk status` and `oatmilk models` do the rest.

To set it up by hand from a copy of Oatmilk's source instead, follow [Run it on your computer](https://app.getoatmilk.com/docs/self-hosting/your-computer.md).

## What you get

- **All of Oatmilk on this computer.** The web app, Ask AI and its 16 agents, your books, your files and the five-minute background jobs all run here, in Docker.
- **Oatmilk in your browser at `https://oatmilk.localhost`.** It uses the usual web ports, 443 and 80, so the address has no port number. `oatmilk local open` opens it.
- **The terminal app on the same books.** `oatmilk` opens them, already signed in.
- **No account anywhere else.** Your account lives on this computer. Its email is your computer's user name at `oatmilk.localhost`, such as `ada@oatmilk.localhost`. The terminal signs itself in, and setup makes you a password for the browser.
- **It works with the Wi-Fi off.** After the first setup, it needs no internet. See [Working offline](#working-offline).

## What it needs

- **Docker.** Docker Desktop on macOS and Windows, or Docker Engine with the Compose plugin on Linux, with Compose 2.20 or newer. Give Docker at least 4 GB of memory.
- **[Bun](https://bun.sh)**, which runs Oatmilk's own setup commands: `curl -fsSL https://bun.sh/install | bash`.
- **Git**, to download Oatmilk's source. If you already have a copy, `oatmilk setup --source <folder>` uses it instead.
- **[Ollama](https://ollama.com) or [LM Studio](https://lmstudio.ai)**, to run the AI models.
- **Memory.** About 4 GB for Oatmilk, and more for the models. 16 GB is good for real books. With less than 14 GB, setup picks smaller models, which are slower and less accurate.
- **Disk.** About 15 GB for Oatmilk, and a few GB for each model.
- **The internet, the first time,** to download Oatmilk's source, build it and download the models.

The setup screens check for Docker, Bun and git before they ask anything, and say how to get what's missing. They don't check memory or disk.

## Set it up

Run `oatmilk`. The first time, it asks how you want to use Oatmilk. Choose **Keep everything on this computer**, then:

1. **It checks this computer.** If something is missing, get it and press ⏎ to check again.
2. **Choose the model server:** Ollama or LM Studio. If one is installed but not running, `s` starts it.
3. **Check the models.** Setup picks them for you. `←` `→` changes one, and `t` checks that the chat model answers.
4. **Type your name, then your company's name.** That's all it asks about you.
5. **Take a last look.** ⏎ sets it up.

Each question shows where you are, like `2/5`. `oatmilk setup` opens these screens again later.

### Without the screens

```bash
oatmilk setup --local                                               # asks only before it downloads
oatmilk setup --local --name "Ada Lovelace" --company "Acme" --yes  # asks nothing
```

`oatmilk setup --local` uses the first model server it finds running, or the one `--models` names. In a terminal, it asks before it downloads Oatmilk's source or any models; `--yes` downloads them without asking. Without `--name` and `--company`, it uses your computer's user name and "My company".

### What setup does

Setup shows each step as it goes:

| Step | What happens |
| --- | --- |
| Check Docker and Bun | Docker is running, and Bun is installed |
| Find Oatmilk | It uses a copy of Oatmilk's source on this computer, or downloads the newest one with git |
| Prepare the models | The model server answers, and Ollama downloads any models it's missing |
| Write the settings | It writes Oatmilk's settings, and keeps any secrets from before |
| Install packages | Only when Oatmilk runs from source |
| Start Oatmilk | Docker builds Oatmilk and starts it |
| Wait until it answers | It waits until `https://oatmilk.localhost` answers |
| Let Oatmilk reach the models | Oatmilk, inside Docker, can reach your model server |
| Make your account | It makes your account, with a password it makes up |
| Sign this terminal in | It signs the terminal in with that password |
| Open your company | It opens your company, or makes it |

The first start takes about 10 minutes, while Docker builds Oatmilk. Then setup says **Oatmilk is ready** and shows your password for the browser. ⏎ opens the app. `oatmilk local password` shows the password again.

If Oatmilk can't reach your model server, setup still finishes, and AI waits until you fix it (see [If something goes wrong](#if-something-goes-wrong)).

In the setup screens, ⌃C stops setup and everything it started, such as Docker's build. What finished stays done. Press `r` to run it again with your answers kept; the finished steps go fast.

**From source.** To work on Oatmilk itself, run it with `bun run dev` from a copy of its source instead of Docker: `oatmilk setup --local --runtime source`, or **How it runs** under **More options › Choose for each part**. It's then at `http://localhost:3000`, and it needs Node.js 24.

### Open it in your browser

`oatmilk local open` opens `https://oatmilk.localhost`. Sign in with your email and the password setup showed you.

The first time, your browser warns about the certificate. Oatmilk makes its own, and your computer doesn't know it yet. Trust it once:

```bash
cd "$(oatmilk local path)"
bun run self-host cert      # saves the certificate and prints the command that trusts it
```

Run the command it prints, then restart your browser. [Trust the local certificate](https://app.getoatmilk.com/docs/self-hosting/your-computer.md#4-trust-the-local-certificate) has the command for each system, and Firefox's own list. The terminal doesn't need this: it trusts Oatmilk's certificate by itself.

## Choose what goes online

Everything stays on this computer until you say otherwise. Oatmilk has eight parts, and each one can stay here, use a cloud service of your own, or be off:

| Part | This computer | Your cloud | Off |
| --- | --- | --- | --- |
| **Sign-in**: who can open your books | An account kept on this computer | Clerk, a hosted sign-in service | — |
| **Books**: transactions, invoices, people, history | PostgreSQL and Redis on this computer | A PostgreSQL you own: Supabase, Neon, RDS… | — |
| **Files**: statements, receipts and documents | Kept on this computer | An S3-compatible bucket: S3, R2, B2, MinIO… | — |
| **Ask AI & agents**: chat, the 16 agents and their tools | Ollama or LM Studio | Vercel AI Gateway | No Ask AI |
| **Classifiers**: categories, matching, mail routing | A small model on this computer | TypeSafe's Jev, through Vercel AI Gateway | You sort everything yourself |
| **Reading documents**: receipts, statements and PDFs | A vision model on this computer | Vercel AI Gateway | Documents are stored, not read |
| **Email**: invoices, invitations, reminders | — | Resend, with an API key and a domain | Share links yourself |
| **Web research**: looking things up for you | — | Tavily, with an API key | Off |

Email and web research start off, and the rest start on this computer. **Your cloud** means services you own, not Oatmilk Cloud.

In the setup screens, choose **More options**, then **Choose for each part**. `←` `→` changes a part, and the top of the screen says how local it is.

From a command line, setup starts from everything here. `--cloud-for` moves parts to your cloud, and `--off-for` turns them off. In commands, the parts are `account`, `database`, `files`, `agents`, `classifiers`, `documents`, `email` and `web`, in the same order; separate several with commas.

```bash
oatmilk setup --local --cloud-for agents --yes      # Ask AI and the agents in your cloud: 85% local
oatmilk setup --local --off-for classifiers --yes   # you sort everything yourself: still 100% local
```

A part in your cloud needs its keys. The setup screens ask for them. `oatmilk setup --local` reads them from the environment, and keeps the ones it saved before when you leave them out:

| Part | Settings |
| --- | --- |
| Sign-in | `NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY`, `CLERK_SECRET_KEY` |
| Books | `OATMILK_DATABASE_URL` |
| Files | `OATMILK_S3_BUCKET`, `OATMILK_S3_REGION`, `OATMILK_S3_ACCESS_KEY_ID`, `OATMILK_S3_SECRET_ACCESS_KEY`, and `OATMILK_S3_ENDPOINT` outside AWS |
| Ask AI, classifiers and reading documents | `AI_GATEWAY_API_KEY` |
| Email | `RESEND_API_KEY`, `OATMILK_EMAIL_DOMAIN` |
| Web research | `TAVILY_API_KEY` |

In Docker, `OATMILK_REDIS_URL` uses your own Redis instead of the one on this computer. Keys are kept only in the local Oatmilk's own settings file.

Changing where your books or files go doesn't move what's already there. Back them up first with `oatmilk local backup`.

### See how local it is

`oatmilk status` shows where you stand. It starts with how local it is, like `100% local` and "Everything stays on this computer". Then it lists where each part runs, whether Oatmilk answers, the models, where Ask AI thinks and who's signed in. `--json` prints the same for scripts, and the app shows it in **Settings › Privacy**.

Each part counts for a share: books 25%; sign-in, files and Ask AI 15% each; classifiers and reading documents 10% each; email and web research 5% each. A part that's off counts as local, because it sends nothing anywhere.

## Your AI models

Setup picks these models for your computer:

| Job | Ollama | LM Studio |
| --- | --- | --- |
| Ask AI, the agents and reading documents | `qwen3.5:9b`, or `qwen3.5:4b` with less than 14 GB of memory | `qwen/qwen3.5-9b`, or `qwen/qwen3.5-4b` with less than 14 GB |
| Classifiers | `tev1`, a decision model, on Ollama 0.35 or newer; before that, `qwen3.5:4b` | `qwen/qwen3.5-4b` |

If you don't have the chat model but have another one that calls tools, setup uses yours. On Ollama, setup downloads the models you don't have yet. On LM Studio, download them yourself first, with `lms get` or LM Studio's Discover tab.

```bash
oatmilk models                                   # Ollama and LM Studio here, their models, and what Oatmilk uses
oatmilk models test                              # asks each model to answer
oatmilk models pull qwen3.5:4b                   # downloads a model into Ollama
oatmilk models start                             # starts the model server Oatmilk uses; or name one: ollama, lmstudio
oatmilk models use --model qwen3.5:4b --restart  # uses another model, then restarts Oatmilk
```

- `models test` waits for each model's first answer. That loads the model into memory, which can take minutes on a laptop.
- When `models start` starts Ollama, it gives it room for long statements, and on Linux lets Docker reach it.
- `models use` checks that the models are on the server first. `--models lmstudio` moves to LM Studio. Without `--restart`, Oatmilk uses the new models after `oatmilk local restart`.

`models test` only checks that each model answers. To see how well your models do Oatmilk's own work, run `oatmilk models eval`; [Check your models](https://app.getoatmilk.com/docs/self-hosting/local-models.md#check-your-models) explains it.

## Every day

Run `oatmilk` to open the app. If your local Oatmilk is stopped, it starts it and signs you in first; `--no-start` leaves it stopped. `oatmilk local` looks after it:

| Command | Does |
| --- | --- |
| `oatmilk local start` | Starts it, waits until it answers, and signs the terminal in |
| `oatmilk local stop` | Stops it. Your data stays. |
| `oatmilk local restart` | Stops it and starts it again, for example after `oatmilk models use` |
| `oatmilk local status` | Says whether it's running, its address, and whether it runs in Docker or from source. `oatmilk local` alone does the same. |
| `oatmilk local logs` | Shows what it's doing. In Docker, the last 200 lines, then it keeps following until ⌃C; add a service, such as `app` or `db`, for just that one. From source, the last 200 lines of the dev server's log. |
| `oatmilk local open` | Opens it in your browser, and says which email to sign in with |
| `oatmilk local password` | Shows your email and password for the browser. `--reset` makes a new password and signs out every browser. |
| `oatmilk local backup` | Saves the database and files to `self-host/backups`. Docker only. |
| `oatmilk local wipe` | Deletes every company, account, transaction and file in it, after you type `wipe` |
| `oatmilk local path` | Prints the folder of Oatmilk's source that runs it, which holds its settings and backups |

In Docker, a running Oatmilk starts again by itself when Docker does, for example after you restart your computer. `--local` makes any command use this Oatmilk, starting it if it's stopped, and `--cloud` uses Oatmilk Cloud.

## Working offline

Once it's set up, Oatmilk runs with the Wi-Fi off. These keep working:

- Reading and sorting statements, receipts and documents, with your models.
- Ask AI.
- The five-minute background jobs.
- Team members, roles, invitations and outside accountants. With email off, copy an invitation link from **Settings › Team** and send it yourself.
- The REST API, API keys, MCP for AI apps on the same computer or network, and the terminal app.
- These docs, at `https://oatmilk.localhost/docs`.

These wait for the internet, and stay off until you turn them on:

- live bank feeds and payouts (Wise), Stripe, and data connections (Notion, Google Drive, Microsoft);
- email in and out, hosted AI models (Vercel AI Gateway), web research and Discord.

With everything on this computer, the CLI never looks for a new version by itself. `--offline`, or `OATMILK_OFFLINE=1`, makes it refuse any Oatmilk on the internet.

## Update, back up, move or start over

### Update

`oatmilk update` installs the newest CLI. It doesn't change the Oatmilk it runs. To update Oatmilk itself, back it up, get its newest source and restart it:

```bash
oatmilk local backup
cd "$(oatmilk local path)"
git pull
bun install
oatmilk local restart      # builds the new version; database changes apply on the way
```

From source, `oatmilk local backup` doesn't work, so keep a copy with `oatmilk export` first. Then start its database again yourself before Oatmilk (see [the known limits](#if-something-goes-wrong)):

```bash title="From source"
oatmilk local stop
cd "$(oatmilk local path)"
git pull
bun install
bun run self-host dev      # starts the database, Redis and file storage, and applies database changes
oatmilk local start
```

### Back up

```bash
oatmilk local backup       # the database and files, into self-host/backups
oatmilk export             # every record and file of your company, into a folder
```

`oatmilk local backup` is for Oatmilk in Docker, while it's running. It saves two files, readable only by you, in `self-host/backups` in the folder `oatmilk local path` prints. Restoring needs the same `self-host/.env` from that folder, so keep a copy of it with the backups, off this computer. If your books or files are in your cloud, use that service's backups instead.

`oatmilk export` is for administrators, and works from source too: a ZIP with every record, and every file. [Restore](https://app.getoatmilk.com/docs/self-hosting/operate.md#restore) puts a backup back.

### Move to another computer

Back up, then copy the backups and `self-host/.env` to the other computer. There, restore them into an Oatmilk that uses that same `self-host/.env`, as [Restore](https://app.getoatmilk.com/docs/self-hosting/operate.md#restore) shows. To move your books to Oatmilk Cloud or a server instead, use `oatmilk migrate`: see [Moving between your computer and the cloud](https://app.getoatmilk.com/docs/terminal.md#moving-between-your-computer-and-the-cloud).

### Start over

```bash
oatmilk local wipe         # deletes every company, account and file in the local Oatmilk
oatmilk setup --local      # sets it up again, empty
```

`oatmilk local wipe` keeps Oatmilk's settings, its certificate and your backups. `oatmilk wipe` instead removes every sign-in, the setup, Ask AI's message history and the cache from this computer; add `--local-data` to delete the local Oatmilk's data too. Both ask you to type `wipe` first. Neither deletes the copy of Oatmilk's source or the backups in it, so run `oatmilk local path` first to see where they are.

## If something goes wrong

| Problem | Fix |
| --- | --- |
| "Start Docker" before setup's questions, or "Oatmilk didn't start" | Docker isn't running. Open Docker Desktop, or on Linux run `sudo systemctl start docker`. Then press ⏎ to check again, or run `oatmilk local start`. |
| "Oatmilk didn't start", and Docker says port 443 or 80 is taken | Another program uses that port, and setup always uses 443 and 80. Stop that program, then run `oatmilk local start`. To use other ports, set Oatmilk up by hand with [Run it on your computer](https://app.getoatmilk.com/docs/self-hosting/your-computer.md). |
| The browser warns about the certificate | Trust Oatmilk's certificate once, as in [Open it in your browser](#open-it-in-your-browser). |
| The terminal shows a certificate error, or never sees Oatmilk answer | Your Node.js can't trust Oatmilk's certificate by itself. Update to Node.js 24.5 or newer (or 22.19 or newer on Node.js 22), or set `NODE_EXTRA_CA_CERTS` to `self-host/oatmilk-local-ca.crt` in the folder `oatmilk local path` prints. |
| "Ollama isn't answering", or "Neither Ollama nor LM Studio is running" | Start it with `oatmilk models start` (or `oatmilk models start lmstudio`), then check with `oatmilk models test`. |
| A model "isn't on your model server yet" | On Ollama: `oatmilk models pull <name>`. On LM Studio: `lms get <name>`, or its Discover tab. Then try again. |
| "Oatmilk can't reach your model server from Docker yet" | On Linux, Ollama must listen on every address: start it with `OLLAMA_HOST=0.0.0.0 ollama serve`, or let `oatmilk models start` start it. In LM Studio, turn on **Serve on Local Network**. AI waits until then. |
| Answers are slow | A first answer loads the model into memory and can take minutes on a laptop. For faster answers, add `OATMILK_LOCAL_REASONING=none` to `self-host/.env` in the folder `oatmilk local path` prints, then run `oatmilk local restart`. |
| Out of memory or disk | Give Docker at least 4 GB of memory in Docker Desktop's settings, and keep about 15 GB free for Oatmilk, plus a few GB for each model. On a small computer, use a 4B model: `oatmilk models pull qwen3.5:4b`, then `oatmilk models use --model qwen3.5:4b --restart`. |
| Setup stopped partway | It says which step stopped and what to do. Fix that, then press `r`, or run `oatmilk setup --local` again. What finished stays done. |
| "Oatmilk didn't answer at https://oatmilk.localhost in time" | `oatmilk local logs` shows what it's doing. Fix what it says, then run `oatmilk local start`. |
| "The download of Oatmilk's source failed", or "the download didn't work" | Setup downloads Oatmilk's source from GitHub, and the source isn't public yet, so only people with access to it can download it. If you have a copy, use it: `oatmilk setup --source <folder>`. |
| You use your own Redis, and `oatmilk models use` moved Oatmilk to the one on this computer | A known limit: in Docker, `models use` writes the settings again with the Redis on this computer. To change models, run the `oatmilk setup --local` you set it up with again, with `OATMILK_REDIS_URL` set and `--model` for the new model. |
| From source, Oatmilk can't open your books after `oatmilk local restart` | A known limit: from source, stopping Oatmilk also stops its database, Redis and file storage, and starting it doesn't start them again. `oatmilk local stop` and then `start`, and `oatmilk models use --restart`, do the same. In the folder `oatmilk local path` prints, run `bun run self-host dev`, then `oatmilk local start`. Your data is kept. |

[Update, back up and troubleshoot](https://app.getoatmilk.com/docs/self-hosting/operate.md#troubleshooting) has more fixes for Oatmilk in Docker.
