Self-hosting

Run it on a server

Host Oatmilk for your team on a VPS or your own server at your domain, with HTTPS from Let's Encrypt.

30 minutes · Intermediate

This guide puts Oatmilk on one Linux server at your own domain, such as books.example.com, for you and your team. It works the same on Hetzner, DigitalOcean, Linode, OVH, a homelab machine or any other host with Docker. Everything runs on the server; for managed databases, see the AWS, Google Cloud and Azure guides.

1. Get a server

  • Ubuntu 24.04 or another current Linux.
  • 2 CPUs, 8 GB of memory and 40 GB of disk. The build needs the memory; Oatmilk itself runs in about 4 GB.
  • A public IP address, with ports 80 and 443 open to the internet.

Log in with SSH as a user that can run sudo.

2. Point your domain at it

At your DNS provider, add an A record (and an AAAA record for IPv6) for the name people will open, pointing at the server's IP address:

Text
books.example.com.   A   203.0.113.10

Check it from your own computer before you continue. Caddy can only get a certificate once the name leads to the server.

Shell
dig +short books.example.com

3. Install Docker, Bun and Git

Shell
curl -fsSL https://get.docker.com | sudo sh
sudo usermod -aG docker "$USER"         # then log out and back in
curl -fsSL https://bun.sh/install | bash
sudo apt-get install -y git
docker compose version                  # 2.20 or newer

If the server has a firewall, open the web ports:

Shell
sudo ufw allow 22/tcp && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp && sudo ufw enable

4. Get Oatmilk and run setup

Shell
git clone https://github.com/AGI-Ventures-Canada/oatmilk.git
cd oatmilk
bun install
bun run self-host setup
QuestionAnswer
Where will Oatmilk run?On a server
Domain people will openbooks.example.com
Use the standard web ports, 443 and 80?Yes
Which PostgreSQL database?Run one here, or your own (see Bring your own services)
Which Redis?Run one here
Where should files be kept?On this machine, or an S3-compatible bucket
How should people sign in?Accounts kept here
Who can make an account?Only people I add or invite
Which AI models…?Vercel AI Gateway with an API key, or a model server your team runs (see Use local AI models)
Should Oatmilk send email?Yes, with Resend, with your Resend API key and the domain you send from
Create your own account now?Yes
Build and start Oatmilk now?Yes

The first build takes about 10 minutes. Caddy gets a Let's Encrypt certificate the first time someone opens the site.

5. Sign in and invite your team

  1. Open https://books.example.com and sign in with the account setup made.
  2. Create your company and fill in its profile.
  3. Invite people from Settings › Team. They get an email with a link, and make their account from it.

Sign-up is closed, so only people you invite, or add yourself, can make an account:

Shell
bun run self-host user add --email ada@example.com --first-name Ada --last-name Lovelace

Turn on two-step sign-in for administrators in their profile. Sign-in and accounts covers the rest.

Without email, people only get invitations and reminders when you copy the link and send it yourself. With Resend:

  1. Add your sending domain in Resend, such as books.example.com, and add the DNS records it shows.
  2. Wait until Resend says the domain is verified.
  3. Run setup again and answer Yes, with Resend, or add these to self-host/.env and run bun run self-host up:
self-host/.env
ACCOUNTING_EMAIL_ENABLED=true
RESEND_API_KEY=re_…
OATMILK_EMAIL_DOMAIN=books.example.com

7. Connect your tools

  • API keys from Developers › API keys start with oat_live_, and the API reference at https://books.example.com/docs/api uses your address.
  • AI apps: add https://books.example.com/api/mcp as a remote MCP server.
  • The CLI: npx @getoatmilk/cli login --host https://books.example.com.

Keep it running

TaskCommand
Check its healthbun run self-host doctor
Update to a new versiongit pull, then bun run self-host up
Back up the database and filesbun run self-host backup

Schedule backups with cron, and copy them off the server:

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

See Update, back up and troubleshoot.

If something goes wrong

ProblemFix
The site has no certificateThe domain doesn't point at the server yet, or ports 80 and 443 are closed. Check with dig and bun run self-host logs caddy.
A teammate can't make an accountSign-up is closed: invite them in Settings › Team, or bun run self-host user add.
Invitation emails don't arriveCheck that Resend shows the domain as verified, and read bun run self-host logs app. You can always copy the invitation link from Settings › Team.
The build stops with "killed"The server ran out of memory. Use 8 GB, or add swap while building.