SSD Nodes Learn Hosting plans →
Guides Matt ConnorBy Matt Connor

Self-hosted budgeting apps compared

Which self-hosted budgeting apps pull bank transactions automatically, which need manual import, and how Actual, Firefly III, Fava and Wallos compare.

The short answer

Self-hosted budgeting apps differ on one thing that decides most installs: whether the software fetches your transactions, or you type them in. Actual Budget is the best default for a household budget. It uses the envelope method and runs as a single container. It can also pull transactions from a bank data provider when you ask it to.

Firefly III is the choice when you want double entry accounts, or when you want imports to run on a schedule with nobody watching. Beancount with Fava suits people who want the ledger to be a plain text file under version control. Wallos does one narrow job: it tracks the recurring costs that leave your account every month.

None of these apps talk to your bank by themselves. Every one that shows bank transactions gets them from a third party aggregator, and getting an account with an aggregator is the step that stops people. Read the sync section before you install anything.

What each one runs, side by side

ChartServices, storage and sync model, taken from each project's own install docs
The data behind this chart
[
  {
    "tool": "Actual Budget",
    "services": 1,
    "storage": "SQLite files",
    "bank_sync": "Aggregators, manual pull",
    "multi_user": "Needs OpenID",
    "encryption": "Optional end to end"
  },
  {
    "tool": "Firefly III",
    "services": 3,
    "storage": "MariaDB",
    "bank_sync": "Importer, cron capable",
    "multi_user": "Built in accounts",
    "encryption": "None at rest"
  },
  {
    "tool": "Beancount + Fava",
    "services": 1,
    "storage": "Text file",
    "bank_sync": "Import scripts only",
    "multi_user": "No login at all",
    "encryption": "File level, your choice"
  },
  {
    "tool": "Wallos",
    "services": 1,
    "storage": "SQLite file",
    "bank_sync": "Manual entry only",
    "multi_user": "Extra logins in settings",
    "encryption": "None at rest"
  }
]

Actual Budget runs 1 long running service. Firefly III runs 3, because its official compose file starts the app, a MariaDB database and a cron helper as separate containers. That is the whole resource story for these 4 apps: a database server is the only heavy part in the group, and the rest is a small process holding a small file.

Which self-hosted budgeting apps can pull transactions from your bank?

No self-hosted app connects to a bank directly. Banks expose transactions through open banking APIs, and those APIs are reached through an aggregator: a company that holds the bank relationships and resells access. So the real question has two halves. Does the app speak to an aggregator, and can you get an account with an aggregator that covers your bank?

Actual Budget speaks to several. As of August 2026 its documentation lists Akahu for New Zealand, Enable Banking for Europe, GoCardless Bank Account Data for Europe, SimpleFIN Bridge for North America, and Pluggy.ai for Brazil. You sign up with the provider yourself, generate keys and secrets, then paste them into your server. Two limits matter. The docs state plainly that Actual does not sync bank data automatically, so a person presses the button. The same page also notes that GoCardless is not accepting new accounts, which closes the free European route that older guides still recommend. SimpleFIN Bridge is a paid subscription service, billed to you by the provider, not by Actual.

Firefly III splits importing into a second container, the Firefly III Data Importer. Its example configuration carries credential slots for GoCardless (Nordigen), Enable Banking, Spectre and SimpleFIN, alongside CSV and CAMT.053 file imports. Set CAN_POST_AUTOIMPORT=true, put a long random value in AUTO_IMPORT_SECRET, and a cron job on the host can trigger an import with no browser open. That is the only genuinely unattended sync in this comparison.

Beancount has no sync. You download a CSV or OFX file from your bank and run an importer script you wrote or borrowed. Wallos has no sync either, by design: you enter a subscription once and it repeats on its own schedule.

Manual import is the path that always works, and it survives your bank changing providers. If bank connectivity is the deciding factor for you, check aggregator coverage for your specific bank before you install anything. That check takes ten minutes and it saves a weekend.

Actual Budget: envelope budgeting on one container

Envelope budgeting means you assign money you already have to a category before you spend it. Groceries gets 300, transport gets 80, and the total assigned can never exceed the money in your accounts. Actual implements that method, and the app is local first: your browser or desktop client holds a full copy of the budget and syncs to your server, so the app keeps working while the server is down.

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

Start it with docker compose up -d and open http://your-server:5006. The data volume grows two directories, server-files and user-files. Those directories are your budget. Put a reverse proxy with TLS (transport layer security) in front before you use it from anywhere but your own network. The full walkthrough is in our Actual Budget install guide, and the compose patterns behind that file are in Docker Compose on a VPS.

Multi-user. Out of the box the server has one password that everyone shares. Separate accounts require OpenID Connect, which the server supports against providers such as Authentik, Keycloak, Google and GitHub. The first person to log in through OpenID becomes the server owner, and ACTUAL_USER_CREATION_MODE decides whether later logins create accounts on their own. Two people can open the same budget file at the same time, though the documentation warns that conflicting edits are not safe, so a couple sharing one budget should avoid editing the same screen at once.

Encryption. Actual offers end to end encryption per budget file. With it on, the server stores data it cannot read, which is what you want on a rented box. Two consequences follow. Lose the encryption password and the file is gone, because there is no reset. And bank sync tokens are stored separately and are not covered by that encryption, so anyone with database access on the server can read them.

Firefly III: double entry, and the only unattended import here

Double entry means every transaction has a source account and a destination account. Buying groceries moves money from your checking account to an expense account, so nothing appears from nowhere. Money you cannot explain surfaces as an unbalanced account instead of hiding inside a category total. That is the reason to pick Firefly III: it sits closer to accounting than to budgeting.

Install from the project's own files:

mkdir -p /srv/firefly
curl -fsSL -o /srv/firefly/docker-compose.yml https://raw.githubusercontent.com/firefly-iii/docker/main/docker-compose.yml
curl -fsSL -o /srv/firefly/.env https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/.env.example
curl -fsSL -o /srv/firefly/.db.env https://raw.githubusercontent.com/firefly-iii/docker/main/database.env

Edit .env before the first start. APP_KEY must be a string of exactly 32 characters, and the project ships the command that makes one:

head /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 32 && echo

STATIC_CRON_TOKEN wants a second 32 character string. The cron container in that compose file calls the app once a day, and the call is rejected without a valid token, which means recurring transactions and bill reminders never fire. Set SITE_OWNER to your email address, make the database password in .env match the one in .db.env, then start it:

docker compose up -d
docker compose logs -f app

A healthy first start ends with the app serving on port 80 inside the container and the log settling down. A loop of database connection errors means the password in .env and .db.env disagree, so the app cannot log in to MariaDB.

Automatic import. The Data Importer runs as its own container and authenticates to Firefly III with a personal access token in FIREFLY_III_ACCESS_TOKEN. Configure one import interactively, save the configuration file it gives you, then let cron post to the auto import endpoint on a schedule. This is what people mean when they say Firefly III syncs: the aggregator holds the bank connection, the importer collects from it, and your server does the work while you sleep.

Multi-user. Firefly III keeps real accounts in its own database, which is what AUTHENTICATION_GUARD=web selects. Point it at remote_user_guard instead and an authenticating proxy such as Authelia handles login, passing the username in a header. LDAP is no longer supported. On an instance reachable from the internet, open the administration page after your first login and check the single user mode setting, which controls whether anyone else can register.

Encryption. There is none at rest. Transactions sit in MariaDB in readable form, so anyone with the database password or a copy of the volume has your financial history. Terminate TLS in front of it, keep it off the open internet where you can, and encrypt the backups.

Beancount and Fava: your ledger is a text file

Beancount is a plain text double entry syntax. A transaction is a few lines in a file you own:

2026-08-19 * "Supermarket" "Weekly shop"
  Expenses:Food:Groceries   42.10 EUR
  Assets:Bank:Checking

Fava is the web interface over that file. It draws the charts, the balance sheet and the income statement, and it can edit the source. Install it in a virtual environment:

sudo apt update && sudo apt install -y python3-venv
python3 -m venv ~/.venvs/fava
~/.venvs/fava/bin/pip install fava
~/.venvs/fava/bin/fava --read-only ~/ledger/main.beancount

A plain pip3 install fava fails on Ubuntu 24.04 with error: externally-managed-environment because apt owns the system Python, so the virtual environment is the fix rather than a workaround.

Fava listens on localhost port 5000 by default. -H changes the host and -p changes the port, but think before you widen it: Fava has no login. The command line has no user or password option, so anything that can reach the port reads every transaction you own. Keep the default host and reach it over an SSH tunnel, a VPN, or a proxy that authenticates. The --read-only flag blocks writes from the browser, which is worth setting when you edit the file on your laptop and commit it.

Multi-user and encryption. There is no user model at all. One instance serves one ledger with no identity attached, so sharing means sharing the proxy login. Encryption is whatever you apply to the file, such as an age or gpg encrypted copy, or an encrypted backup repository. The upside of a text ledger is that git gives you history, blame and a remote copy for free.

Wallos: the small one, for recurring costs

services:
  wallos:
    image: bellamy/wallos:latest
    ports:
      - "8282:80/tcp"
    volumes:
      - './db:/var/www/html/db'
      - './logos:/var/www/html/images/uploads/logos'
    restart: unless-stopped

Wallos keeps everything in one SQLite file at db/wallos.db. It handles several currencies with conversion, and it can warn you about an upcoming renewal by email, Discord, Telegram, Gotify, Pushover or a webhook. Extra logins are created from the settings page, each with their own list.

Be clear about what it is not. Wallos does not track daily spending and has no concept of a budget. It answers one question: what am I paying every month, and what renews next week? Plenty of people run it beside Actual for exactly that. If the recurring amounts you care about are the ones you send out rather than the ones you pay, look at self-hosted invoicing software instead.

What about Maybe?

Maybe Finance gets recommended in a lot of threads, so it needs an answer. Its README states that the repository is no longer actively maintained, with a final release tagged v0.6.0, and the code is AGPLv3. It still runs. Budgeting data is data you keep for a decade, and unmaintained software holding a database schema and bank credentials is a poor place to keep it. That was the position in August 2026, so check the repository yourself before you commit.

How much server do these need?

All of these are small, and you should measure rather than trust a number from a blog. Run this on your own box after a normal day of use:

docker stats --no-stream

Read the MEM USAGE column. Actual, Wallos and Fava each hold one small file behind one small process, so they sit comfortably on a modest VPS next to other services. Firefly III differs because MariaDB is in the stack, and a database server holds memory whether or not you are querying it. Plan headroom for the database rather than for your transactions.

The data itself barely grows. Ten years of household transactions is a file measured in megabytes, so disk is not the constraint. The real consequence of that longevity is the next section.

The mobile story, and where it disappoints

This is where self-hosted finance apps lose to commercial ones, so be realistic before you move a household onto one.

Actual documents that the official mobile applications are deprecated. What you get instead is the web version, described in its own docs as a responsive progressive web app that you install to your home screen and use like a native app. It works well. It is not an app store app, though the community maintains unofficial native clients.

Firefly III ships no official app either, and its API has produced two capable unofficial ones. Waterfly III is an Android app, published on Google Play and F-Droid. Abacus runs on iPhone, iPad and Android, logs in with OAuth2, and stores its tokens in the iOS keychain. Both need your instance reachable from the phone.

Fava and Wallos give you a responsive web page and nothing more. Reading a ledger on a phone is fine. Entering a Beancount transaction on a phone keyboard is not.

One point applies to all of them: a phone on mobile data is not on your home network. Either you publish the app on a domain with TLS and strong authentication, or you connect the phone to a VPN. Publishing a finance app to the open internet behind one shared password is the mistake to avoid.

Backups: the section that matters most in this category

Losing a media library costs you a download. Losing five years of categorised transactions costs you something you cannot re-download. Set this up on day one.

What to copy, per app:

  • Actual Budget: the whole data volume, both server-files and user-files.
  • Firefly III: a database dump, plus .env, .db.env and the upload volume.
  • Beancount: the ledger file, ideally as a git repository with a remote.
  • Wallos: db/wallos.db and the uploaded logos directory.

Copying a live SQLite file is the classic way to produce a backup that does not restore. SQLite keeps recent writes in a separate write ahead log file, so a copy of the .sqlite file alone can be missing the newest transactions, or fail to open at all. Stop the container for the few seconds the copy takes:

cd /srv/actual
docker compose stop actual_server
tar czf /srv/backups/actual-$(date +%F).tgz -C /srv/actual actual-data
docker compose start actual_server

Firefly III wants a dump instead. Take it from inside the database container so the password never reaches your shell history:

cd /srv/firefly
docker compose exec -T db sh -c 'mariadb-dump -u firefly -p"$MYSQL_PASSWORD" firefly' > /srv/backups/firefly-$(date +%F).sql

Older MariaDB images ship the same tool under its previous name, mysqldump. Keep .env and .db.env beside the dump, because a database restored into an install whose configuration you no longer have is a puzzle you solve on the worst possible day.

A copy on the same VPS protects you from a bad edit. It does not protect you from a lost server or a compromised one. Push the backup directory to object storage with restic, which encrypts the repository before anything leaves the box:

export RESTIC_REPOSITORY=s3:s3.example.com/money-backups
export RESTIC_PASSWORD_FILE=/root/.restic-pass
restic init
restic backup /srv/backups
restic forget --keep-daily 7 --keep-weekly 8 --keep-monthly 12 --prune

restic snapshots should now list a snapshot stamped with today's date. Scheduling, credentials and the systemd timer are covered in our guide to encrypted restic backups on a VPS.

A backup you have never restored is a guess. Test it once now, while nothing is wrong:

restic restore latest --target /tmp/restore-test

Unpack the Actual archive into a scratch directory, start a second container against it on another port, and open it. You should see your accounts with the balances they held when the backup ran. For Firefly III, load the dump into a scratch database and count the rows in the transactions table. Numbers that match the live instance mean the backup is real. Then delete the scratch copies, because a forgotten test instance with real financial data is its own problem.

Pick one app, run it for a month with real numbers before you migrate any history, and get the backup working the same week. If you are still deciding which parts of your life belong on your own box, our roundup of what is worth self-hosting in 2026 covers the rest of the stack.

FAQ

Which self-hosted budgeting app can connect to my bank?

Actual Budget and Firefly III both can, through a third party aggregator rather than a direct bank connection. Actual supports providers including SimpleFIN Bridge for North America, Enable Banking for Europe, Akahu for New Zealand and Pluggy.ai for Brazil, and its documentation says Actual does not sync bank data automatically, so you press the button. Firefly III uses a separate Data Importer container that can be triggered by cron, which makes it the one that truly runs unattended. Beancount and Wallos have no bank connection at all. Check that an aggregator covers your specific bank before you choose, because coverage decides this more often than features do.

Can I use these budgeting apps on my phone?

Yes, though not with an official app. Actual's documentation states that the official mobile applications are deprecated and points to the web version, a responsive progressive web app you install to your home screen. Firefly III has two solid unofficial clients: Waterfly III on Android, and Abacus on iOS and Android. Fava and Wallos are responsive web pages and nothing more. In every case the phone must reach your server, so plan for a domain with TLS and strong authentication, or put the phone on a VPN.

How do I back up a self-hosted budgeting app without corrupting the database?

Never copy a live SQLite file on its own. SQLite holds recent writes in a separate write ahead log, so a copy taken mid write can be missing transactions or fail to open. Stop the container, copy the data directory, then start it again, which takes seconds for apps this small. For Firefly III, run mariadb-dump inside the database container instead, and keep the environment files with the dump. Send the result off the box in an encrypted restic repository, and restore it once to prove that it works.

Is Firefly III too heavy for a small VPS?

It is the heaviest option here, and only because its official compose file runs a MariaDB server next to the app and cron containers. Personal finance data is tiny, so the memory goes to the database process sitting idle rather than to your transactions. Run docker stats --no-stream on your own box after a normal day and read the MEM USAGE column instead of trusting a published figure. If you want the smallest possible footprint, Actual Budget and Wallos each run one process against one file.

#budgeting#personal-finance#self-hosting#docker#privacy