Deployn

Self-Host Actual Budget: Budgeting App with Docker and Caddy

Install and use Actual Budget on your own server: Docker, HTTPS with Caddy, accounts, envelope budgeting, reports, rules and import, step by step.

Published on
Self-Host Actual Budget: Budgeting App with Docker and Caddy hero image
Contents
Tested with
Actual Budget 26.11.0Caddy 2.11Docker 29.9Ubuntu 26.04
Last checked on

Actual Budget is a free, open-source app for your personal budget. You enter income and expenses, assign your money to categories, and always see what you can still spend this month. The app runs on your own server, so your financial data stays with you.

In this post I install Actual Budget with Docker, secure it with HTTPS through Caddy, and then walk through the most important features. The screenshots show the German interface; the menu names in the text are the English ones.

What is Actual Budget?

Actual Budget is a local-first app: your budget lives as a full copy in your browser and also on your server. Changes sync to the server in the background, and the app stays fast even on a weak connection.

The concept behind it is envelope budgeting: you assign every euro you earn to a category, for example groceries, rent or savings. Expenses are deducted from that category. When an “envelope” is empty, the money for that category is gone, and you decide deliberately whether to move money over from another category.

Here is what Actual Budget can do:

  • Manage multiple accounts, including accounts outside your budget such as an investment account or a house.
  • Enter transactions by hand or import them from bank files.
  • Create rules that categorize transactions automatically.
  • Manage recurring payments as schedules.
  • Build reports covering net worth, cash flow and spending by category.
  • Stay in sync across several devices, optionally with end-to-end encryption.

Prerequisites

  1. A server: I use a small VPS from Hetzner running Ubuntu 26.04. Actual Budget needs few resources, so a small server is enough.
  2. A domain: it must point to your server’s IP address with an A record, for example {{domain}}. I explain below why you need HTTPS.
  3. Open ports: ports 80 and 443 must be reachable so Caddy can get its certificate.

Install Docker

Connect to the server over SSH and install Docker with the official script:

Terminalbash
curl -fsSL https://get.docker.com | sudo sh

Then check the installation:

Terminalbash
docker --version

Start Actual Budget with Docker Compose

Create a directory for Actual Budget:

Terminalbash
mkdir ~/actual
cd ~/actual

Create the compose.yaml:

Terminalbash
nano compose.yaml
yaml
services:
    actual_server:
        image: actualbudget/actual-server:latest
        container_name: actual_server
        restart: unless-stopped
        volumes:
            - ./data:/data

    caddy:
        image: caddy:2
        container_name: caddy
        restart: unless-stopped
        ports:
            - "80:80"
            - "443:443"
        volumes:
            - ./Caddyfile:/etc/caddy/Caddyfile
            - caddy_data:/data
            - caddy_config:/config

volumes:
    caddy_data:
    caddy_config:

The ./data folder holds your entire budget. The Actual container does not publish a port of its own. You reach it only through Caddy.

Create the Caddyfile:

Terminalbash
nano Caddyfile
{{domain}} {
    reverse_proxy actual_server:5006
}

On first start, Caddy automatically requests a certificate from Let’s Encrypt. Start both containers:

Terminalbash
sudo docker compose up -d

Run sudo docker compose ps to check that both containers are running. You can see whether the certificate worked in the Caddy logs:

Terminalbash
sudo docker compose logs caddy | grep "certificate obtained"

Why Actual Budget needs HTTPS

If you open Actual Budget over a plain IP address with http://, the app does not start. This message appears instead:

Fatal error message about SharedArrayBuffer when accessing over HTTP

Actual needs SharedArrayBuffer, and browsers only provide that feature in a secure context, meaning HTTPS or localhost. That is why Caddy is already part of the compose.yaml. Open your domain with https:// and the app works.

Set the server password

Open https://{{domain}} in your browser. On the first visit, Actual asks for a password for your server. Choose a strong password and confirm it. After that, you log in with this password.

On the start page you choose “Start budgeting”. “Try the demo” loads a sample budget with data. The demo budget lives only in your browser and is not synced to the server. It is handy for trying out the reports, but for your real finances you create a new file.

In the file list, click “Create new file”:

File list with the local demo budget and the button to create a new file

Actual creates an empty budget with default categories. You can rename, delete or add categories later.

Create your first account

Click “Add account” at the bottom left of the sidebar.

Add account dialog with the options for a local account and bank sync

You have two options here:

  • Create a local account: You enter transactions yourself or import files from your bank.
  • Set up bank sync: Actual can connect accounts through external providers. This requires an account with the provider and is not equally well supported in every country.

I start with the local account. Enter a name, type in the current balance and click “Create”.

Create local account form with the name Girokonto and a balance of 2500

You need the “Off budget” option for accounts that are not part of your spending budget, such as an investment account or a house. They count toward your net worth, but not toward the money you can assign. You cannot change this later.

The balance shows up as the first transaction, “Starting Balance”, in the “Starting Balances” category:

Checking account with the opening transaction of 2,500 euros

Enter transactions

In the account, click “Add New” at the top. A new row appears. The Tab key moves you from field to field: date, payee, notes, category, payment or deposit. Actual creates new payees as you type. Save the transaction with “Add” or the Enter key.

New transaction with the payee Rewe, category Food and a payment of 42.50

The balance drops immediately, and the expense lands in the “Food” category. If one purchase covers several categories, split the transaction and give each part its own category.

Assign money to categories

Now comes the core of Actual Budget. On the “Budget” page you see the “To Budget” amount at the top. That is money you have received but not yet assigned to a category.

In the “Budgeted” column, click the cell of a category and enter an amount. I assigned 400 to groceries, 900 to bills and 500 to savings:

Budget page with assigned amounts and 700 euros still left to budget

The columns mean:

  • Budgeted: What you assigned to this category this month.
  • Spent: What you actually spent.
  • Balance: What is left in the category. Red values show overspending.

The goal is for “To Budget” to reach 0. Then every euro has a purpose. Amounts left over in a category carry over to the next month. If you overspend, click the red balance and move money over from another category.

Reports

Under “Reports” you find a dashboard with widgets: income and expenses over the year, net worth, cash flow, a comparison with the previous month and a transaction calendar. Use “Add new widget” and “Edit dashboard” to rearrange it.

Reports dashboard with net worth, cash flow and transaction calendar

The screenshot comes from the demo budget, because my fresh budget has no data yet.

Schedules for recurring payments

You do not want to book rent, internet or insurance by hand every month. Under “Schedules” you set them up once. Actual then shows whether a payment is upcoming, due or missed, and posts it automatically if you want.

Schedules with the statuses missed, due, upcoming and scheduled

With “Add new schedule” you set the name, payee, account, amount and recurrence.

Rules for automatic categorization

Under “Settings” -> “Rules” you define what Actual should do automatically with new transactions, for example always assign a payee to a category. That saves a lot of manual work when you import bank data.

Rules in the settings with conditions and actions

A rule consists of conditions (“if the payee is Rewe”) and actions (“set the category to Food”). Schedules also create rules of their own, which you see in the list.

Import transactions

In the account, click “Import”. Actual reads files in these formats:

  • OFX/QFX: The best choice if your bank offers it. The files contain unique IDs, so Actual detects duplicates reliably.
  • QIF
  • CSV: You map the columns of the file to date, payee, amount and so on, and check the date format in the preview.
  • CAMT

If you import transactions twice, Actual looks for entries with a similar date, the same amount and a similar payee, and merges them.

Optional: end-to-end encryption

Under “Settings” you can enable encryption for sync. Your budget is then encrypted on the device with a key derived from your password before it reaches the server. The server cannot read the contents.

Keep two things in mind: once enabled, encryption cannot be switched off again, unless you restore your budget from a backup into an unencrypted file. And without your password and without a local copy, the data cannot be recovered.

Backup

Actual stores everything in the data folder. Back it up regularly, ideally to another machine:

Terminalbash
cd ~
sudo tar czf actual-backup.tar.gz actual/data

You can also back up directly in the app: “Settings” -> “Export data”. You get a file that you can import again into a new installation.

Update Actual Budget

Terminalbash
cd ~/actual
sudo docker compose pull
sudo docker compose up -d

The data in the data folder stays intact. You can see the current version under “Settings” -> “General”.

Who is Actual Budget for, and what speaks against it?

Actual Budget fits if you want to plan deliberately what your money is for and do not want to store your data with a cloud provider. Envelope budgeting forces you to assign money actively.

A few points to know:

  • Envelope budgeting takes getting used to. If you only want to analyze spending, a simple spreadsheet is faster.
  • With self-hosting, you are responsible for updates and backups.
  • Bank sync runs through external providers and is not available for every bank. File import, on the other hand, always works.

Firefly III is an alternative. It leans more toward bookkeeping and also runs on your own server.

Conclusion

With Docker and Caddy, Actual Budget runs on your server within minutes. Envelope budgeting takes some getting used to, but afterwards you see at a glance how much money is left for which purpose. Enter income and expenses, assign money, look at the reports: that is all you need to start. Rules, schedules and import come in when you need them.

Do you already use a budgeting tool, and which feature is missing there? Let me know in the comments.

FAQ

Frequently asked questions

Is Actual Budget free?

Yes. Actual Budget is open source and free. For self-hosting you only pay for the server.

Is Actual Budget secure?

Your data sits on your own server. The connection is encrypted over HTTPS, and you can optionally add end-to-end encryption for your budget. You have to keep the server updated and backed up yourself.

Why does Actual Budget not start and show a SharedArrayBuffer error?

You are probably accessing an IP address over HTTP. Actual needs a secure context. Set up a reverse proxy such as Caddy with a domain and open the app over HTTPS. Only localhost works without HTTPS.

Where does Actual Budget store my data?

On the server, in the folder you mounted to /data in the compose.yaml, here ./data. In addition, every device on which you open the app keeps a local copy.

Can I connect Actual Budget to my bank?

Yes, through providers such as SimpleFIN, GoCardless or Pluggy.ai. Which provider works for your bank depends on the country. Alternatively, you import files in OFX, QFX, QIF, CSV or CAMT format.

What is envelope budgeting?

You assign every euro you earn to a category, the so-called envelope. Expenses are deducted from the envelope. When it is empty, the money for that category is gone.

How do I update Actual Budget?

In the folder with the compose.yaml, run sudo docker compose pull and then sudo docker compose up -d. Your data stays intact.

How do I back up my budget?

Regularly copy the data folder to another machine, and also export your data in the app under Settings with Export data.