Tech Digest

Head to head

Actual Budget vs Firefly III

These are two different mental models, not two implementations of one idea. This page decides which model you want, then tells you what running it actually costs in containers, backups and weekly attention.

Last reviewed 2 tools compared

Actual Budget or Firefly III?

Pick Actual Budget if you want to spend less, and Firefly III if you want an accurate record of what you already spent. Actual is envelope budgeting: you assign the money you have to categories before you spend it, it runs in one container on port 5006, and it keeps working when the server is down because every device holds a full copy. Firefly III is double-entry accounting: every transaction moves value between two accounts, balances reconcile to the cent against statements, and it costs you three or four containers including MariaDB, a cron container and a separate data importer. Choosing on features rather than on model is why people install one, bounce off it and blame the software.

Both are free, both run on a Raspberry Pi, both replace Mint or YNAB. That is where the similarity stops. Actual Budget is envelope budgeting: you assign the money that is actually in your accounts today to categories before you spend it, and next month's money is not this month's money. Firefly III is double-entry accounting: every transaction moves value between two accounts, a salary is a deposit from a revenue account into checking, groceries are a withdrawal to an expense account, the books balance because they must, and budgets are a reporting layer on top of a ledger that would be complete without them.

Everything else follows from that difference, including how many containers you run and what a restore looks like.

The one question that decides it#

When you open the app on a Tuesday, what do you want it to tell you?

  • "You have 38 left for groceries this month." That is Actual: a decision aid before you spend.
  • "Your net worth on 31 December was 41,204.16, and here is every movement that got you there." That is Firefly III: a record after you spent.

Answer honestly rather than aspirationally. The failure mode is not picking the worse tool, it is picking the one that answers the other question.

Why people abandon both#

Someone who wants to know where the money went installs Actual, and finds an app that keeps asking them to assign money to categories and treats an unbudgeted month as an error state. They call it nagging. They are right, because nagging is the product.

Someone who wants to stop overspending installs Firefly III, spends a weekend building asset, revenue and expense accounts, imports a year of CSVs, and gets an accurate report of a past that does not change. Nothing in it stops them buying the thing on Thursday.

Both then say the software is fine but did not work for them. It worked exactly as designed.

The specification table, and the two rows that matter#

Look past RAM. The rows that decide this are container count and datastore: one container with no database service against three or four built around a relational database, and SQLite on your device against MariaDB or PostgreSQL on the server. That is the split between backing up with a file copy and backing up with a dump plus a volume plus a key.

SpecificationActual BudgetFirefly III
LicenceMIT (Permissive)AGPL-3.0-or-later (Copyleft)
Written inTypeScript / JavaScript (React and Node.js)PHP (Laravel)
First release20222015
MaturityStableMature
DatastoreSQLite on the client; the server keeps account.sqlite plus per-budget binary filesMariaDB or MySQL by default, PostgreSQL supported, SQLite possible but discouraged
Services to run14
Idle memory120 MB250 MB
Memory in use250 MB600 MB
Operational load2 / 5, Light3 / 5, Moderate
IdentityNative OIDCTrusted proxy header
arm64 buildsYesYes
Default ports50068080
Backup shapeSQLite backupMySQL dump

Actual runs one image, publishes 5006, and mounts one volume at /data, under which it creates server-files/ and user-files/ on first start. Firefly III's standard shape is fireflyiii/core on 8080, a mariadb:lts, a small Alpine container running crond, and fireflyiii/data-importer on its own 8080. The cron container is not decoration: without something fetching /api/v1/cron/<STATIC_CRON_TOKEN> daily, recurring transactions never materialize and bills are never marked paid or due, with no warning. STATIC_CRON_TOKEN and APP_KEY must each be exactly 32 characters, and DB_CONNECTION defaults to mysql, so a Postgres stack that forgets pgsql fails at startup with a connection error that never names the real problem. Keep those in an .env beside the compose file, per Docker Compose conventions.

Behind a proxy, Firefly also needs TRUSTED_PROXIES=** and an APP_URL matching exactly what the browser uses, or you get http:// links inside an HTTPS site and login loops that look like session bugs. See Reverse proxy and TLS and Caddy.

Actual's end-to-end encryption is a second password with no reset#

Encryption is optional, per budget file, and it happens on your device, so the file is already encrypted before it reaches the sync server. Turn it on and the server keeps relaying changes while being unable to read a single transaction.

The consequence is blunt. The server login password resets with no data loss. The encryption password cannot be reset, recovered or brute-forced, because the server never had the key. Lose it and every local copy, and the objects in user-files/ are permanently unreadable. Two more edges: encryption is one-way (getting back to an unencrypted file means exporting and importing into a fresh one), and GoCardless or SimpleFIN credentials sit on the server outside it.

A correct backup here is three things, not one:

  1. The /data volume: server-files/account.sqlite and the encrypted blobs in user-files/.
  2. Regular exports from inside the app (Settings, Export Data), the only artifact that survives losing both the server and the password.
  3. The encryption password in a password manager, not in your head and not in that volume. Vaultwarden is the usual home for it.

Then do the part everyone skips: install the app on a second device, restore the export there, and confirm it opens. An untested backup is a hypothesis. Backups that actually restore argues it at length.

Firefly III's importer is a fourth container you also have to operate#

Bank import is not built in. fireflyiii/data-importer is a separate image with its own configuration and web interface, split off deliberately for security and maintenance separation. It talks to Firefly over the API using either FIREFLY_III_ACCESS_TOKEN, a personal access token you generate in the Firefly UI, or FIREFLY_III_CLIENT_ID for OAuth.

yaml
importer:
  image: fireflyiii/data-importer:latest
  environment:
    FIREFLY_III_URL: http://app:8080
    FIREFLY_III_ACCESS_TOKEN: ${FIREFLY_III_ACCESS_TOKEN}
  ports:
    - "8081:8080"

FIREFLY_III_URL must be the address reachable on the container network, not your public hostname, or you get a 500 with no explanation. For ops load: a second image to update, a token to rotate, one more thing that can break while the main app looks healthy.

It also couples versions. Firefly III 6.6.0 and 6.6.1 invalidated every existing OAuth token and client, so the importer and any script or integration needed new tokens. Releases from 6.6.0 issue UUID-style tokens an older importer will not accept, so both containers move in the same window. Read release notes before pulling: An update strategy that does not lose data.

Bank connectivity, honestly, for both#

Neither gives you the consumer experience of typing a bank login once and seeing transactions arrive forever.

  • Actual supports GoCardless (parts of Europe) and SimpleFIN (parts of North America). You register with the provider yourself and paste your own credentials into the server. Coverage is regional, connections expire and need reauthorizing, and those credentials sit on the server outside end-to-end encryption.
  • Firefly III imports through the separate data importer, which handles file formats including CSV and camt.053 as well as API-based connections. Its import rules are the real advantage: they classify and split transactions automatically rather than by hand.

The fallback that works in most of the world is unglamorous: download a CSV or OFX export from online banking weekly and import it. Budget for that friction rather than hoping a feed removes it. If an automatic feed is the only reason you are doing this, read When not to self-host first.

Restoring each one is a different job#

Firefly III's restore is a database restore: a mysqldump or pg_dump (never a copy of the running database container's data directory), plus the upload volume at /var/www/html/storage/upload for attachments, plus the original APP_KEY. Laravel uses that key for encrypted values, so a dump restored under a different one leaves you data you cannot fully read. storage/framework and storage/logs are regenerable. See Backing up a running database and restic vs BorgBackup.

Actual's restore is stranger, because the server holds change history. Restore an older local file, let it sync, and the server pulls you straight back to where you were. The documented order is: restore, then sync reset from the restored device so it becomes authoritative, then revert every other device (which deletes its local copy and re-downloads). Do it from the device with the most complete data, because a reset discards unsynced changes held anywhere else. That reset also fixes the slow problem: Actual stores every mutation in the budget file, so files grow even when transaction count does not, until they hit the 20 MB ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB default and present as sync failures rather than a size warning.

Three uncomfortable things#

Actual's sync server is a relay, not the source of truth. People back up the container, feel covered, and have missed the point of a local-first app: the authoritative copy is on the device you use. Do not substitute Syncthing on the data directory for Actual's own sync either, because two devices writing one SQLite file is a corruption story, not a backup story.

Budgeting only works if you reconcile weekly, and no software makes you. Both go quietly wrong the same way: the numbers drift from your bank, you stop trusting them, you stop opening the app. Firefly at least fails loudly, since a ledger that does not match the statement is visibly broken. Actual will show a confident envelope balance built on three weeks of missing transactions.

Household support is weak in both. Actual's multi-user path runs through an OpenID login still marked preview; the common setup is one budget file and shared credentials. Firefly III has no built-in OIDC and dropped LDAP, so shared access means trusting a proxy-injected REMOTE_USER header from something like Authelia. Neither has per-person permissions on shared accounts; see Single sign-on for self-hosters.

Which one for your situation#

Your situationUseWhy
You overspend and want it to stopActual BudgetEnvelopes force the decision before the purchase, not after
You want a tax-year-accurate ledgerFirefly IIIDouble-entry reconciles to the cent against statements
Household, two people, shared accountsActual Budget, reluctantlyOne budget file across devices works; neither has real per-person permissions
You are leaving YNABActual BudgetSame mental model, so your habits transfer rather than being relearned
You want charts of where money went, no disciplineFirefly IIIReporting is the point; nothing asks you to budget first
Multiple currencies and many accountsFirefly IIIMulti-currency accounts and per-account balances are first-class
A Pi with a phone as the main clientActual BudgetOne container, ~120 MB idle, and the client works with the server offline
You will not run a cron containerActual BudgetFirefly's recurring transactions and bills silently never fire without it

What to do next#

Pick the model, not the feature list. If your answer to the Tuesday question was "how much is left", deploy Actual, enable end-to-end encryption only after the password is in a password manager, and schedule an export from Settings that lands outside the server. If it was "where did it go", deploy Firefly III with all four containers from the start, and prove a mysqldump restores into an empty database before you enter a year of history.

Then do the migration properly. Replace Mint or YNAB covers exporting from a hosted budgeting service into either of these, including what does not survive the export. The rest of the finance and organization tools sit in Productivity.

Questions#

Can I run Actual Budget and Firefly III together?

You can, and a few people do, but the duplication is real. You would import the same bank data twice, categorize it twice and reconcile it twice, because there is no supported sync between them. If you genuinely want both jobs done, run Firefly III as the ledger of record and use its budget pages loosely, or run Actual and accept that its reports are simpler than Firefly's. Running both properly is a weekly chore, not a one-time setup.

Does Actual Budget connect to my bank automatically?

Sometimes, and it depends where you live. Actual supports GoCardless, which covers parts of Europe, and SimpleFIN, which covers parts of North America. Both need their own account and their own credentials, which you create yourself and paste into the server. Neither is bundled or automatic. If your bank is not covered, the working fallback is downloading a CSV or OFX file from online banking and importing it on a schedule you keep, typically weekly.

What happens if I lose my Actual Budget encryption password?

The budget files on the server become unrecoverable. End-to-end encryption is a second password on top of the server login, the key is derived from it on your device, and the server never receives it, so there is nothing to reset. If you still hold a local copy on any device, you can reset the key from that device, which forces a sync reset and re-uploads. If you hold neither the password nor a local copy, the objects in user-files/ are noise. The server login password is different: that one resets with no data loss.

Do I have to run the Firefly III data importer container?

Only if you want to import anything, which is most people. fireflyiii/data-importer is deliberately a separate image with its own configuration, and it authenticates to Firefly over the API using a personal access token or an OAuth client id. FIREFLY_III_URL must be the address reachable on the container network, http://app:8080 in the official compose, and wrong values return a 500 rather than a useful error. Adding it takes the stack from three containers to four.

Which one is the easier move from YNAB?

Actual Budget, by a wide margin, because it is the same mental model. Envelopes, assigning every unit of money, and rolling a negative category into next month all behave the way you expect. Firefly III has budgets, but they sit on top of a ledger rather than being the point of it, so the habit you built in YNAB does not transfer cleanly. Category structure and starting balances still need rebuilding by hand in either tool.

Can two people share one household budget?

Weakly, in both. Actual's usual answer is one budget file synced across a laptop and two phones, which in practice means shared credentials unless you enable the OpenID login that is still marked preview and is the prerequisite for multi-user. Firefly III has no built-in OIDC and dropped LDAP, so shared access means a proxy header guard with AUTHENTICATION_GUARD=remote_user_guard. Neither has per-person permissions on a shared set of accounts.

Sources#

Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.