Blog / Tutorials

Self-Host Actual Budget on a VPS — Private, Encrypted Money Tracking

By the NoctHost TeamSeptember 7, 20267 min read

Your budget is a map of your whole life. Where you shop, what you owe, how much cushion you have, the month things got tight. It is exactly the data a venture-funded budgeting startup would love to hold — and exactly the data you should think hardest about before handing over, because startups get acquired, pivot, shut down, or get breached, and your five years of transaction history goes wherever the assets go.

Actual Budget is the answer if you like the YNAB envelope method but not the part where a company keeps your finances. It is open-source, it uses zero-based budgeting (every dollar gets a job before the month starts), and critically, it is local-first: the app on your device holds the real data. The server you run does one narrow thing — sync an encrypted copy between your devices. Here is how to stand that server up and, more importantly, how to not lose your data once you do.

The part that makes this safe: local-first

Most cloud apps are the opposite of local-first. The server holds the truth; your screen shows a rendered view of it. Lose your connection and you are locked out; the company can read everything because the data lives on their side in a form they can query.

Actual inverts that. The full budget file lives in the client — your browser or the desktop app — and that is where every calculation happens. It works completely offline. The sync server exists only to pass changes between your laptop and your phone so both stay current. When you turn on end-to-end encryption, the server stores nothing but ciphertext: it cannot read a single transaction, because the key never leaves your devices.

So the threat model is refreshingly small. A compromise of your server leaks an encrypted blob, not your spending. That is the whole reason self-hosting Actual is worth doing rather than just trusting someone's cloud.

Standing up the sync server

You need very little. Actual's server is tiny — it will run on the smallest VPS you can rent, with room to spare. Ubuntu 24.04 and a subdomain pointed at the server's IP is the whole shopping list.

Run the official server image behind Caddy so HTTPS is automatic. Save as docker-compose.yml:

services:
  actual:
    image: actualbudget/actual-server:latest
    restart: always
    volumes:
      - actual_data:/data
    expose:
      - "5006"

  caddy:
    image: caddy:2-alpine
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile
      - caddy_data:/data
    depends_on:
      - actual

volumes:
  actual_data:
  caddy_data:

Note the sync server is not exposed directly to the internet — only Caddy publishes ports, and it proxies inward. The Caddyfile next to the compose file:

budget.example.com {
  reverse_proxy actual:5006
}

Bring it up:

docker compose up -d

Open https://budget.example.com. On first load Actual asks you to set a server password. This password protects access to the server itself — it is the gate on who can reach the sync API. It is not your encryption key, which comes next and matters more.

Turn on end-to-end encryption

This is the step that makes the server blind, and it is off by default, so do it deliberately. When you create or upload your budget file, Actual offers to enable encryption and asks for a separate encryption password. From that it derives a key that stays on your client. The server only ever receives already-encrypted data.

Two things to burn into memory:

  • The encryption password is not recoverable. There is no reset link, no support email that can help. If you forget it, the encrypted file on the server is permanently unreadable — that is what "the server can't read it" cuts both ways to mean. Put it in your password manager the moment you set it.
  • The server password and the encryption password are different jobs. One controls access; one controls readability. Use two distinct strong passwords so a leak of one does not hand over the other.
Tip — Store the encryption password somewhere independent of the server — ideally in a self-hosted password manager on a different box. If both your budget and the only copy of its key live on the same VPS, a single failure loses both at once.

Connecting your devices

Point clients at your server and they sync through it:

  • Desktop and mobile: install the Actual app, and on setup enter https://budget.example.com plus your server password. It downloads the (encrypted) file and prompts for the encryption password to unlock it locally.
  • Browser: just visit the URL. Actual runs entirely as a web app; there is nothing else to install.

Each device keeps its own local copy and reconciles through the server. Edit your budget on a plane with no signal, and it merges cleanly the next time you are online.

Backups — the step people skip and regret

Here is the catch of local-first design: if your devices each hold a copy, you might feel backed up. You are not, reliably. Reinstall the app, wipe a laptop, or lose your phone, and the local copies go with them. The server's copy is your durable one — and if the server dies without a backup, an encrypted blob you cannot restore is the same as no blob at all.

Everything the server needs lives in one Docker volume, actual_data. Back it up off-site nightly. Configure an rclone remote first with rclone config, then:

docker run --rm -v actual_data:/data -v /root:/backup alpine \
  tar czf /backup/actual-backup.tar.gz -C /data .
rclone copy /root/actual-backup.tar.gz remote:actual-backups

Drop that in a cron job. Because the data at rest is already end-to-end encrypted, the backup you ship off-site is encrypted too — you can store it on ordinary cloud storage without exposing your finances, which is a quiet nice consequence of turning encryption on earlier.

Actual also has its own trick worth knowing: from the app, File → Export downloads the whole budget as a single .zip you can open on any Actual install. Run that manually every so often and keep it with your important documents. It is the belt to the server backup's suspenders, and it is the copy that survives even if you walk away from self-hosting entirely.

What running this costs

Almost nothing in resources — Actual's sync server is one of the lightest things you can self-host, comfortable on a 1 vCPU / 1 GB box with the CPU mostly asleep. On NoctHost you can pay for that box hourly from a prepaid balance topped up with crypto, which is a fitting way to keep the machine that holds your budget out of a card-linked account. The heavier cost is discipline: set the encryption password, save it somewhere independent, and let the backup cron run. Do those three things and you have private money tracking that no company can read, pivot away from, or lose on your behalf.

Spin one up in about a minute

Email signup, pay with crypto, hourly billing. Trying a box costs cents — destroy it when you are done.

Deploy a server

Frequently asked

Can Actual's server read my financial data?
Not when end-to-end encryption is on. The key is derived on your device from a password that never reaches the server, so the server only ever stores ciphertext. Encryption is off by default, though, so you must enable it explicitly when you set up your budget file — until you do, the server holds a readable copy.
What happens if I forget the encryption password?
Your data is gone. There is no recovery mechanism by design — the whole point is that no one, including the server operator, can decrypt without the key. Save the encryption password in a password manager the instant you create it, and keep it separate from the server itself.
Do I need the server running to use Actual?
No. Actual is local-first and works fully offline — the app on your device holds the real budget and does all the math. The server only syncs changes between multiple devices. If you only ever use one device, you can run Actual with no server at all, though then your backups are entirely on you.
How much VPS do I need for Actual Budget?
The smallest tier available. The sync server passes small encrypted diffs and does no heavy computation, so a 1 vCPU / 1 GB instance runs it with the resources barely touched. Storage needs are trivial too — a budget file is small, even after years of transactions.

Keep reading