Skip to content

Installation

Use this page if you run Actual Bench yourself. Docker is the supported installation method. If another person runs it for you, go to Connect to a budget.

  • Docker (and optionally Docker Compose) installed on the host.
  • A spare port on the host. The container listens on 3000.
  • Somewhere to put a persistent volume for the metadata database, or Bench forgets itself every restart.
  • Browser access to wherever you publish it.
  • For Direct Actual Server mode: your browser has to reach the Actual Server, which brings CORS and cross-origin isolation requirements with it. See Connect to a budget.
  • For HTTP API Server mode: the container has to reach actual-http-api.

Actual Bench publishes multi-architecture images (linux/amd64 and linux/arm64):

TagStabilityUse it when
latestStableNormal production / self-hosting (default)
:<version> (e.g. :1.2.4)Stable, pinnedYou want a specific, unchanging version
edgeUnstable - rebuilt on every mergeTesting the newest changes before a release

Create a docker-compose.yml:

services:
actual-bench:
image: xrous/actual-bench:latest
container_name: actual-bench
ports:
- "3000:3000"
environment:
ACTUAL_BENCH_DB_PATH: /data/actual-bench.sqlite
# --- Optional settings (uncomment to use; see the Configuration guide) ---
# LOG_LEVEL: info # debug | info | warn | error
# SYNC_VAULT_KEY: "<strong-secret>" # enable unattended server-side sync
# SYNC_SCHEDULER_SECRET: "<strong-secret>" # enable the external scheduler trigger
volumes:
- actual-bench-data:/data
restart: unless-stopped
volumes:
actual-bench-data:

Start it:

Terminal window
docker compose up -d

A basic setup needs no environment variables at all. ACTUAL_BENCH_DB_PATH appears above only to make the metadata location obvious - it already defaults to that path. Everything commented out is optional; Configuration says what each one does.

The equivalent without Compose:

Terminal window
docker run -d \
--name actual-bench \
--restart unless-stopped \
-p 3000:3000 \
-v actual-bench-data:/data \
xrous/actual-bench:latest

Prefer not to run your own Docker host? Actual Bench also deploys on:

  • Fly.io - fly launch --from https://github.com/x-rous/actual-bench --generate-name --ha=false
  • PikaPods - available once the official listing is approved

Both run the same image as Docker Compose above. See Deployment → Hosted platforms for the full walkthrough of each.

Open the app in your browser:

  • Local host: http://localhost:3000
  • Remote host: http://<your-server>:3000, or your reverse-proxy URL

The app opens on the connection screen. Continue with Connect to a budget.

  1. The app loads and shows the connection screen.
  2. The version appears in the sidebar footer (for example v1.2.4).
  3. Open App Health (under Tools) and confirm:
    • Writable is Yes.
    • Schema shows a version number (not Not initialized).
    • Persistence reads Persistent when /data is mounted.

If Writable is No, or persistence is incorrect, fix the database mount or its permissions before you connect a budget.

Bench keeps its own workflow metadata in a SQLite database at /data/actual-bench.sqlite; set ACTUAL_BENCH_DB_PATH to move it. Persist and back up the volume that database sits on - /data unless you moved it, in which case persist the directory ACTUAL_BENCH_DB_PATH names instead. Recreate the container without it and that metadata is gone.

To move to a newer image:

Terminal window
docker compose pull
docker compose up -d

Before a real upgrade, read Upgrading and Backups. It covers backups, pinning a version, and what happens to the database.

Continue to Connect to Actual Budget.