Skip to content
Go back

Firefly III vs Actual vs Fava (Beancount)

By SumGuy 10 min read
Firefly III vs Actual vs Fava (Beancount)
Contents

Your 2 AM self wants a money app that won’t sell your mortgage history to Stripe. Your 10-year self wants data that isn’t locked behind a defunct startup. And your current self? You just want to know if you’re blowing through rent on pizza delivery again.

Here’s the thing: self-hosting personal finance sounds like “I want to reconcile bank statements in a cave,” but it’s actually “I want a tool that works the way I think about money.” Three legit options exist, and they represent three totally different philosophies. Let’s dig in.

Why Self-Host Your Money Anyway?

Privacy. You don’t owe Plaid your checking account history. You don’t owe some VC-backed budget app your spending patterns (“oh, you like coffee; let me show you a coffee subscription”). And if the company shuts down, you’re not locked out.

Control. Want to track cryptocurrency alongside checking accounts? Firefly does it. Want to mark transactions as “maybe a business expense”? Fava handles that. Want your mobile app to work offline and sync later? Actual Budget’s got your back.

Longevity. Plain-text ledgers in git survive forever. Databases can migrate. Web apps can vanish. But a CSV or a .beancount file? That’s readable in 2036.

The Three Apps in 30 Seconds

Firefly III (PHP + Laravel): Full-featured web app. Think GnuCash but modern, self-hosted, with a nice UI. Relational database, tons of bells and whistles, decent mobile companion.

Actual Budget (TypeScript): YNAB-style envelope budgeting, open-source, slick mobile app, end-to-end encrypted sync. The best UX of the three right now.

Fava (Python): Web UI wrapped around Beancount, a plain-text double-entry accounting system. You edit a .beancount file by hand, git-commit it, Fava renders the reports. Delightfully minimalist.

Data Model: How They Store Your Money

Firefly III: Relational (The Flexible Database)

Firefly uses a MariaDB/PostgreSQL database. Accounts, transactions, categories, budgets, bills, rules—all normalized tables. You’re thinking relationally: every transaction is a row, accounts are entities, rules are procedures.

This is great if you like spreadsheets and want to generate reports by querying the database. It’s the “most flexible” if you want custom fields, multiple currencies, complex rule chains, or a dozen data types.

Trade-off: Your data lives in a database. Backing it up means exporting JSON or doing database dumps. It’s not human-readable in a text editor.

Actual Budget: SQLite + Encrypted Sync

Actual stores everything in a local SQLite database on your phone/desktop, and syncs encrypted to the server. No unencrypted data on the server, ever. The server is dumb—just a blob store.

This is clever. You get offline-first UX (edit transactions on the plane) and privacy (the server can’t read your data).

Trade-off: You’re locked into Actual’s UI to view data. The database isn’t meant for custom queries. But for most people, that’s fine—the app is the interface.

Fava: Plain Text (The Git-Friendly Answer)

Fava reads a .beancount file. That’s it. You edit the file in your editor, git-commit it (free version control and history), and Fava renders reports in a web UI.

Example entry:

personal.beancount
2026-07-10 * "Pizza Hut" "Friday night panic"
Assets:Checking -45.00 USD
Expenses:Food:Dining
2026-07-15 * "Direct deposit"
Assets:Checking 3500.00 USD
Income:Salary

That’s it. Double-entry accounting (every transaction balances). No GUI-generated schema lock-in. In 30 years, you can still read it in less.

Trade-off: You edit plaintext. If you hate that, Fava’s not your jam. But if you live in a terminal anyway, this feels right.

Budgeting Philosophy: How They Want You to Spend

Firefly III: Categories + Monthly Rules

Firefly thinks in categories and budgets. You set a monthly budget for “Dining Out” ($150), assign transactions to that category, and Firefly tells you how much you’ve left.

Rules are powerful: “Any transaction from Uber → Commute category, then subtract from my Commute budget.” You can automate a lot.

It’s traditional budgeting. Personal finance software companies have done this since 2005.

Actual Budget: Zero-Based Envelope

Actual uses zero-based budgeting (YNAB-style): Every dollar has a job. You start the month with $3000 income, and you allocate it: $500 to rent, $200 to food, $100 to discretionary. The budget is the plan.

If you overspend groceries, you either find extra money or raid another envelope. There’s tension in the system, and that’s the point. It forces you to be intentional.

Trade-off: Zero-based is strict. Some people love that; others find it exhausting.

Fava: Report-Driven, No Enforced Budgets

Fava doesn’t have budgets built in. It has queries. You write:

SELECT account, SUM(position)
WHERE account ~ "Expenses:Food"
AND date >= 2026-07-01

(Beancount Query Language isn’t exactly SQL, but you get the idea.)

You decide what to measure. You might track spending by category, or by time period, or by tag. Fava doesn’t enforce a budget; it just shows you what happened.

This appeals to people who hate categories. You tag transactions and slice them however you want.

Getting Data In: Import, CSV, Bank Sync

Firefly III: The Data Importer Companion

Firefly has a separate tool called Data Importer (also self-hosted) that handles CSV, OFX, and CAMT.053 imports. You upload a file, map columns, and transactions appear.

Some banks offer OFX export natively. If yours doesn’t, you’re copying CSV and praying the column names match.

Not real-time sync. You’re batch-importing.

Actual Budget: Bank Sync (If You Can Get It)

Actual integrates with SimpleFIN (an open standard for read-only bank access) and GoCardless. If your bank supports either, you get near-real-time transactions.

If your bank doesn’t, you’re back to CSV.

The slick part: mobile app stays synced, and you see transactions across devices instantly.

Fava: Scripts and Plugins

Fava doesn’t have built-in importers. But Beancount has a whole ecosystem of scripts. People write custom importers for their banks in Python.

Example: convert-to-beancount.py reads your Chase CSV and outputs Beancount entries. You commit those to your repo.

Not for the impatient. But if you’re comfortable scripting, you can build exactly what you need.

Mobile: The Real-World Difference

Firefly III: Decent mobile app (third-party built), lets you log transactions on the go. Not pretty, but functional.

Actual Budget: Beautiful mobile app. Offline-capable. Syncs back to the server. If mobile is important, Actual wins here by miles.

Fava: Desktop/tablet web UI only. You’re opening a browser, not launching a native app. Fine if you’re near a computer; annoying if you’re at a restaurant trying to log dinner.

Multi-Currency: Who Handles It?

Firefly III: Excellent. Tracks multiple currencies natively, converts on the fly, reports in your base currency. Mint-like.

Actual Budget: Good. Handles multi-currency, shows exchange rates. Works smoothly.

Fava: Perfect. Double-entry accounting was designed for multi-currency. You post in different currencies, balances automatically. If you travel or have overseas accounts, Fava handles it like a professional accountant.

End-to-End Encryption

Firefly III: Stores everything on your server, unencrypted. The server is your security perimeter.

Actual Budget: Client-side encryption. The server can’t read your data, ever. This is a Big Deal if you’re paranoid.

Fava: No server sync at all. Just your .beancount file on your machine. Encrypt the whole directory with LUKS or Full Disk Encryption.

The Plain-Text Advantage: Fava’s Secret Weapon

Here’s the angle nobody talks about: Your .beancount file is git-friendly.

Terminal window
$ git log personal.beancount
commit a1b2c3d "Fixed mislabeled dining from May"
commit 9f8e7d6 "Added car payment"
$ git blame personal.beancount | head
^abc1234 2021-01-15 Assets:Checking
2021-02-01 * "Opening balance"

You have full history. You can audit when you added an account, revert a wrong entry, or check what you spent in 2022. For free. Beancount is version-control-native in a way databases aren’t.

Firefly and Actual have backups, sure. But git is better for plain-text.

Docker Compose: Spinning Up Firefly and Actual

Firefly III

docker-compose.yml
services:
firefly_db:
image: mariadb:latest
environment:
MYSQL_ROOT_PASSWORD: root_password
MYSQL_DATABASE: firefly
MYSQL_USER: firefly_user
MYSQL_PASSWORD: firefly_pass
volumes:
- firefly_db:/var/lib/mysql
restart: unless-stopped
firefly:
image: fireflyiii/core:latest
depends_on:
- firefly_db
environment:
DB_CONNECTION: mysql
DB_HOST: firefly_db
DB_PORT: 3306
DB_DATABASE: firefly
DB_USERNAME: firefly_user
DB_PASSWORD: firefly_pass
APP_KEY: your-base64-encoded-key-here
ports:
- "127.0.0.1:8000:8080"
volumes:
- firefly_uploads:/var/www/html/storage/uploads
restart: unless-stopped
volumes:
firefly_db:
firefly_uploads:

Firefly auto-migrates on startup. Navigate to http://localhost:8000, create an admin account, and you’re in.

Actual Budget

docker-compose.yml
services:
actual_server:
image: actualbudget/actual-server:latest
ports:
- "127.0.0.1:5006:5006"
volumes:
- actual_data:/data
environment:
ACTUAL_HTTPS_KEY: ""
ACTUAL_HTTPS_CERT: ""
ACTUAL_PORT: 5006
restart: unless-stopped
volumes:
actual_data:

Actual is lightweight. Hit http://localhost:5006, download the mobile app, scan the QR code, and sync begins.

Fava

Fava doesn’t need Docker if you’re comfy with pip:

Terminal window
python3 -m venv beancount_env
source beancount_env/bin/activate
pip install fava beancount
# Create a beancount file
cat > personal.beancount << 'EOF'
plugin "beancount.plugins.auto_accounts"
2026-01-01 open Assets:Checking USD
2026-07-01 * "Test transaction"
Assets:Checking -50.00 USD
Expenses:Food
EOF
# Start Fava
fava personal.beancount

Then visit http://localhost:5000. Fava watches the file; if you edit it externally, the UI updates.

For Docker fans:

docker-compose.yml
services:
fava:
image: yegle/fava:latest
ports:
- "127.0.0.1:5000:5000"
volumes:
- ./ledger:/data
restart: unless-stopped

Put your .beancount file in ./ledger, and Fava serves it.

The Verdict Matrix

FeatureFirefly IIIActual BudgetFava
SetupDocker easyDocker easypip or Docker
UI/UXGoodExcellentMinimal
MobileOkayExcellentNone
EncryptionNoYes (E2E)No (use disk encryption)
Multi-currencyExcellentGoodExcellent
Offline-firstNoYesNo
Custom queriesDB-levelLimitedQuery language
Data portabilityJSON exportEncrypted DB.beancount file
Learning curveLowLowMedium (double-entry)
10-year readabilityDatabase dumpEncrypted DBPlain text + git

So Which One?

Actual Budget if you want the best 2026 UX right now. Slick mobile, offline, encrypted. It’s a joy to use. Your only lock-in risk is that it’s one person + community. But the code’s open, so worst case, you fork it.

Firefly III if you want it all in one place. Budgets, bills, rules, currencies, reports. You don’t mind the database, and you like having options. Also: it’s been around for years, active community, less chance of abandonment.

Fava + Beancount if you’re a command-line person, or if you care deeply about your data living in plaintext forever. If you ever delete a cloud account and realize your data wasn’t really yours, you’ll understand Fava’s appeal. Your ledger is a .beancount file in git. That’s it. In 2036, you’ll open it in any text editor and it’ll still make sense.

The Honest Truth

None of these is “better.” Firefly is a database Swiss Army knife. Actual is the experience of modern budgeting. Fava is the principle that your data should be human-readable.

Pick based on what you’ll actually use. If you need mobile, Actual. If you need features, Firefly. If you want to sleep better knowing your ledger is readable in plaintext forever, Fava.

Your 2 AM self will appreciate not having to wonder where your mortgage history went.


Share this post on:

Send a Webmention

Written about this post on your own site? Send a webmention and it'll show up above once verified.


Next Post
Postgres HA: Patroni + etcd + HAProxy

Discussion

Powered by Garrul . Sign in with GitHub or Google, or post anonymously.

Related Posts