SSD Nodes Learn Hosting plans →
How to do am Matt ConnorBy Matt Connor · Updated 2026-08-27

How to Host Actual Budget on Your VPS

Run Actual Budget with Docker Compose on your VPS, then set HTTPS, create your first budget, import bank data, and back up the data volume safely.

Wetin you dey build

Actual Budget na self-hosted envelope budgeting app, and na the usual answer when people dey find YNAB alternative wey dem fit host by demself. The server na one container, one data volume, and one HTTPS name. Everything wey normal budget need go run comfortably for the smallest VPS wey you fit rent, because the server mostly dey store files and sync dem.

E good make you understand the architecture before you type anything. The budget itself na SQLite database wey dey inside your browser and inside each mobile app. The server wey you wan install na sync endpoint: e dey hold account list, budget files, and change log wey allow phone and laptop agree with each other. Na why the app still dey work when server dey down, and na why losing the server no go lose your budget as long as one client still get copy.

Wetin make the server need HTTPS

Actual dey ask for HTTPS, and e no be formality. Browsers only expose Web Crypto API, the interface wey Actual dey use for end-to-end encryption, inside wetin the specification dey call secure context. Secure context na https:// or http://localhost. If you load the app from http://203.0.113.10:5006 for browser on another machine, those features no go dey there, because browser no ever hand dem over to the page. The official mobile builds too dey reject plain http:// server URL.

So, two setups fit work. Put real certificate for real name in front of the container, na this guide dey do. Or give the server self-signed certificate with ACTUAL_HTTPS_KEY and ACTUAL_HTTPS_CERT, as the project document, then accept browser warning for every device. Free certificate from Let's Encrypt dey take five minutes, so use the first option.

Install Actual Budget with Docker Compose

Install Docker first if the box fresh. If Compose file syntax new to you, the Docker Compose basics for a VPS guide explain the fields wey dey below.

sudo install -d -m 755 /opt/actual
sudo install -d -m 700 /opt/actual/data

Write /opt/actual/docker-compose.yml:

services:
  actual:
    image: actualbudget/actual-server:latest
    container_name: actual
    restart: unless-stopped
    ports:
      - '127.0.0.1:5006:5006'
    volumes:
      - ./data:/data

Three details for that file matter.

The image na actualbudget/actual-server:latest, wey the project publish to Docker Hub and mirror for ghcr.io/actualbudget/actual. E get latest-alpine tag for low-power machines.

The container write everything under /data. Inside am, you get server-files, wey hold account.sqlite with your login and session tokens, and user-files, wey hold the budget files themselves. Mount that path, otherwise the next docker compose pull go throw your budget away. ACTUAL_DATA_DIR fit move am, but the default dey okay.

The port publish only for 127.0.0.1. A bare 5006:5006 publish for every interface, and Docker write im own rules ahead of ufw. So the app go open to internet even when firewall get deny-all rule. Why Docker published ports bypass ufw explain this surprise. Binding to loopback mean say only the reverse proxy for the same box fit reach am.

Start am:

cd /opt/actual
docker compose up --detach
docker compose logs -f actual

The log go settle once the server report say e dey listen on port 5006. Check am locally before you touch DNS:

curl -fsS -o /dev/null -w '%{http_code}\n' http://127.0.0.1:5006/

A 200 mean say the app dey serve. curl: (7) Failed to connect mean say the container no dey run, and docker compose ps go show say e don exit. The usual cause na permission problem for the mounted volume, wey go show as an EACCES line for the log.

Put certificate and real name for front

Point A record go the VPS, budget.example.com, then wait make e resolve. After that, install nginx and issue the certificate. The Certbot for Ubuntu 24.04 with nginx guide explain issuance and renewal timer well-well.

The proxy block:

server {
    listen 443 ssl;
    http2 on;
    server_name budget.example.com;

    ssl_certificate     /etc/letsencrypt/live/budget.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/budget.example.com/privkey.pem;

    client_max_body_size 100m;

    location / {
        proxy_pass http://127.0.0.1:5006;
        proxy_set_header Host              $host;
        proxy_set_header X-Real-IP         $remote_addr;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

client_max_body_size na the line wey people dey forget. Full sync dey upload the budget file complete. Nginx default request body na 1 MB, so when the file pass that size, sync go fail with 413 Request Entity Too Large for nginx access log, while the app go show only general sync error. The server get separate limits: ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB default na 20 and ACTUAL_UPLOAD_SYNC_ENCRYPTED_FILE_SYNC_SIZE_LIMIT_MB default na 50. So set the nginx limit above whichever one apply to you.

Reload and test:

sudo nginx -t && sudo systemctl reload nginx
curl -fsS -o /dev/null -w '%{http_code}\n' https://budget.example.com/

First run: password and your first budget file

Open https://budget.example.com for browser. The first screen go ask you to set server password. That one password dey protect the whole server, so generate long random one and keep am for place wey you go fit find am again, like self-hosted Vaultwarden password manager. You no need create user accounts. Actual server na single-password by design, so to share budget mean say you share that password.

Then create budget file. Actual go ask whether to enable end-to-end encryption. Answer yes, and server go store only ciphertext. This na the correct choice for financial data wey dey rented machine. But e get real cost: encryption password no dey reach server, so if you lose am, the file don go and no reset dey available. Write am down before you click pass that screen.

Set your starting balances from your bank current figures instead of importing years of history. Envelope budgeting dey work forward from the money wey you get now, so empty history no go cost you anything.

Getting transactions enter

For here, honesty dey matter pass enthusiasm, because how import dey work na the main reason wey make people abandon self-hosted budgeting.

Manual entry na the basic option, and e always work. For envelope method, na arguably the main point, because when you type purchase, you go notice am.

File import dey handle plenty transactions. Actual dey read CSV, QIF, OFX and QFX, and every bank dey export at least one of dem. Import each account from the account screen, map the columns once, and Actual go remember that layout for the account.

Automatic bank sync dey available, but e need third-party service because server no fit talk to banks by itself. Actual supports SimpleFIN Bridge for North American banks, Enable Banking for Europe, Akahu for New Zealand, and Pluggy.ai for Brazil. GoCardless still dey supported, but e no dey accept new accounts. You go sign up with the provider yourself, generate credentials, and add dem to server. SimpleFIN Bridge dey charge 15 US dollars per year for up to 25 institutions as of July 2026, while the other providers get different prices.

Accept these two limits before you rely on this feature. API credentials dey on server, and end-to-end encryption no cover dem, because server need use dem. Actual no dey poll: syncing na button wey you press, no be background job.

Backups, na just files

Everything wey you care about dey under /opt/actual/data. No export step dey, and no database dump wey you need script.

The only trap na SQLite. If you copy account.sqlite while the server dey write to am, you fit capture transaction wey never finish. You no go know until you try restore. Stop the container for the few seconds wey the copy go take:

cd /opt/actual
docker compose stop
restic -r sftp:backup@backup.example.com:/srv/restic backup /opt/actual/data
docker compose start

Put am for schedule with the approach for restic backups for VPS, wey cover repository setup, retention, and restore drill. Run the restore drill. Backup wey you never restore na just guess.

Actual own client-side backups na separate thing, and e good make you know about am. The browser dey keep recent copies of the budget file. You fit reach dem from the file menu. This handles “I delete category by mistake” without touching the server at all.

Update di server

cd /opt/actual
docker compose pull
docker compose up --detach

Compose go recreate the container from the new image and attach the same volume again, so the data go survive. Update the clients too. Server and app versions suppose stay close, and client wey old pass the server fit refuse to sync with a version mismatch message. Make backup before major version jump, because migrations go run for first start and downgrade path no dey. Actual dey forgiving when e use floating latest tag because e state na directory of files. But app wey carry real database no be like that. self-hosting Chatwoot explain the pinned tags and pre-upgrade dump wey this habit require.

Wetin fit break, and wetin you go see

The app loads but sync never finishes. Check the nginx access log for 413. That one mean say client_max_body_size too low. A 502 instead mean say nginx dey up but the container no dey.

Encryption options are missing, or the mobile app refuses the URL. The page no dey for secure context. Address bar go show http:// with an IP address or hostname wey no be localhost. Fix the certificate instead of finding workaround.

A message that the budget file is not compatible with this version. Client and server versions don separate. Update both to the same release, then reload.

The container restarts in a loop. Read docker compose logs actual. Permission error for /data mean say the mounted directory no writable by the container user. Address-in-use error mean say another thing don already hold 5006 for loopback.

First load feels slow. The whole budget file dey download go browser when you open am. Na one large transfer, then local reads. E no be server sizing problem, and adding RAM no go change am.

FAQ

Actual Budget need HTTPS to work?

Yes, for real use. Actual end-to-end encryption dey use browser Web Crypto API, and browser only expose am inside secure context, meaning https:// or http://localhost. If you use plain HTTP from another machine, those features no go dey available, and official mobile apps no go accept plain HTTP server URL. Use Let's Encrypt certificate for real hostname, or self-signed certificate with ACTUAL_HTTPS_KEY and ACTUAL_HTTPS_CERT if na desktop browser you go use every time.

Actual fit import my bank transactions automatically?

Only through third-party service wey you sign up for by yourself: SimpleFIN Bridge for North America, Enable Banking for Europe, Akahu for New Zealand, or Pluggy.ai for Brazil. GoCardless dey supported, but dem no dey accept new accounts. Those API credentials dey your server, and end-to-end encryption no cover dem. Sync sef manual, so you go press button; nothing dey poll in the background. CSV, QIF, OFX and QFX import no need any third party.

Wetin exactly I need back up?

The mounted data directory, wey be /opt/actual/data for this guide. E contain server-files/account.sqlite with logins and sessions, and user-files with the budget files. Stop the container before you copy am, because copying live SQLite database fit capture partial write. Nothing else for the server dey hold state.

Wetin go happen if I lose the encryption password?

You no fit recover the file. Password never reach the server, and na the main reason for end-to-end encryption, so no reset dey and no support path dey. Store am for password manager immediately after you create the file, and keep one copy for somewhere wey no depend on this same server.

How much server Actual Budget need?

Very little. The container dey serve static assets and files, while budget calculations dey happen for browser. One shared vCPU with 1 GB of RAM fit run am without problem, and data directory for household budget with several years of history dey remain within tens of megabytes. Na your backups and other containers dey use disk space, not Actual. If you dey size a box wey must run something more demanding alongside am, photo server usually dey set the minimum, so check how much RAM PhotoPrism and Immich actually need before you choose plan.