Actual Budget behind Caddy
Config + gotchas checked against the official Actual Budget docs and the issues people actually hit · 2026-06-07
A Caddy reverse-proxy setup for Actual Budget, with automatic HTTPS on budget.example.com. Copy it, swap the domain — the gotchas that trip people up are solved below.
New to reverse proxies? Start with the Caddy + Docker guide.
# Caddyfile for Actual Budget
# Replace budget.example.com with your real domain. Caddy issues TLS automatically.
budget.example.com {
encode zstd gzip
reverse_proxy actual-budget:5006
}
Replace budget.example.com with your domain. Your DNS A record must already point at the server.
Gotchas
What trips people up
Blank page or a 'SharedArrayBuffer is not defined' console error.
Actual needs a Secure Context (HTTPS) for its in-browser SQLite engine — over plain HTTP it won't start. Reach it only through the HTTPS proxy; Caddy issues the cert automatically. Don't expose port 5006 to the host.
Sync stalls or the client can't reach the server behind the proxy.
Actual syncs over standard HTTP(S), which Caddy's default reverse_proxy handles. If it hangs, confirm the app and Caddy share the `proxy` network and you're opening the HTTPS domain, not the container IP.
Deploy
How to deploy
- Create the shared proxy network once: `docker network create proxy`.
- Start the stack: `docker compose up -d`.
- Point budget.example.com at the server, open the HTTPS URL (not the IP), and set your server password on first visit.
Runnable
Full runnable stack
Actual Budget + Caddy + the shared network, in one compose file — so it runs on the first try, not after you stitch the proxy in yourself. Save the Caddyfile above next to it.
# docker-compose.yml — Actual Budget + Caddy, ready to run.
# Pin caddy to a digest for production: caddy:2.8-alpine@sha256:<digest>
# 1. Save the Caddyfile above as ./Caddyfile
# 2. docker network create proxy
# 3. docker compose up -d
services:
actual-budget:
image: docker.io/actualbudget/actual-server:26.5.2@sha256:1aeeb3985db55556e716dec25e08f6ce09308c2571b65cddbc6746ee6d5e0d45
restart: unless-stopped
environment:
TZ: ${TZ}
ACTUAL_PORT: "5006"
volumes:
- actual-data:/data
networks:
- default
- proxy
caddy:
image: caddy:2.8-alpine
restart: unless-stopped
ports:
- 80:80
- 443:443
volumes:
- ./Caddyfile:/etc/caddy/Caddyfile:ro
- caddy-data:/data
- caddy-config:/config
networks:
- proxy
volumes:
actual-data: {}
caddy-data: {}
caddy-config: {}
networks:
proxy:
external: true