What you will set up
Actual Budget is a budgeting app that runs in your browser. Its server keeps your budget in sync between your computers and phones, so you can plan on one and check on another.
Here the server runs as one container, under a user of its own called actual, with Caddy from the Podman guide in front for HTTPS.
Every step below was run on a Melonslab VC-P Alloy (2 vCPU, 8 GB) with Debian 13: the server password was set, logins and the syncing of a budget file were checked through the server's API, failed logins were limited per visitor rather than for everyone, and the backup and a reboot were checked. At idle, Actual used about 250 MB of memory, so the smallest of our Standard plans has room to spare.
Before you start
You need:
- a server set up as in the Podman guide, with Caddy running;
- a name for it, such as
budget.example.com, with an A record and an AAAA record pointing at your server.
The examples use budget.example.com for the name and 203.0.113.10 for your server's IPv4 address. Replace them with your own throughout.
1. Create the user
As root:
useradd -m -s /bin/bash actual
loginctl enable-linger actual
machinectl shell actual@
2. Describe the container
mkdir -p ~/.config/containers/systemd
Create ~/.config/containers/systemd/actual.container:
[Unit]
Description=Actual Budget, a budgeting app server
[Container]
ContainerName=actual
Image=docker.io/actualbudget/actual-server:latest
Volume=actual-data:/data
# Only Caddy, on this server, can reach Actual: the port is not open to the internet.
PublishPort=127.0.0.1:8084:5006
Environment=ACTUAL_TRUSTED_PROXIES=203.0.113.10
AutoUpdate=registry
[Service]
Restart=always
[Install]
WantedBy=default.target
Actual allows five failed logins per 15 minutes from each IP address. It takes the visitor's address from what Caddy passes on, but by default only from proxies on private addresses, and Caddy's connections reach the container from the server's own IPv4 address. ACTUAL_TRUSTED_PROXIES names that address. Without it, every visitor would share one address, and a few wrong passwords from anyone would lock everyone out.
Start it:
systemctl --user daemon-reload
systemctl --user start actual
podman logs -f actual
When the log says Listening on :::5006..., press Ctrl+C.
3. Put Caddy in front
Go back to root with exit, and switch to Caddy's user with machinectl shell caddy@. Add this block to the end of ~/Caddyfile:
budget.example.com {
reverse_proxy 127.0.0.1:8084
}
And restart Caddy, which briefly interrupts every site it serves:
systemctl --user restart caddy
Caddy gets a certificate for budget.example.com and renews it by itself. Actual only runs in the browser over HTTPS.
4. Set the server password
Open https://budget.example.com. The first time, Actual asks you to set a server password, which protects access to your budgets. Then create a new budget, or import one from a file.
On your other devices, open the same address and log in with that password; your budget syncs between them. Actual can also encrypt a budget on your devices before it is synced, with a password of its own, if you want the server to hold only encrypted data.
5. Keep it up to date
Switch on Podman's daily updates for this user:
systemctl --user enable --now podman-auto-update.timer
6. Back up
As actual:
mkdir -p ~/backup
systemctl --user stop actual
podman volume export actual-data --output ~/backup/actual-data.tar
systemctl --user start actual
Stopping Actual for the few seconds it takes gives a consistent copy. The archive holds the server's account database and every budget file. Copy ~/backup to another machine afterwards: a backup on the same server does not survive losing the server. Actual can also export a budget from its settings, as a file you can import again.
Troubleshooting
Logging in says there were too many attempts. Five wrong passwords from your address lock logins from it for 15 minutes, even with the right password. Wait, and try again.
One person's wrong passwords lock everyone out. ACTUAL_TRUSTED_PROXIES in step 2 must be the server's IPv4 address, the one Caddy's connections arrive from.
The app does not start in the browser. Open it at https://budget.example.com: Actual needs a secure connection to run.
Your budget, in Sweden
With Actual on a Melonslab server, your budget is stored in Sweden, under Swedish and EU law, on a server you control.