Firefly III
Double-entry personal finance for people who want a ledger, not a spending chart
Firefly III is a self-hosted personal finance manager built on double-entry bookkeeping: every transaction moves value between two accounts, so balances reconcile against your bank instead of approximating. It replaces Mint, Quicken or a decade of spreadsheets, with a separate importer container for bank and CSV data.
Our verdict on Firefly III#
Firefly III is double-entry, and that is not a marketing distinction. 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, and nothing is a categorized line floating on its own. Your first month is slower than Mint ever was, and afterwards your balances actually reconcile against statements. Three operational facts decide whether you stay. The importer is a separate container, fireflyiii/data-importer, talking to Firefly over the API, and v6.6.0 invalidated every OAuth token and client, so you regenerate them and reconfigure every integration. Recurring transactions and bills do not fire on their own: a cron container has to hit /api/v1/cron/<STATIC_CRON_TOKEN> daily, and that token must be exactly 32 characters. And DB_CONNECTION defaults to mysql, so a Postgres deployment that skips it fails at startup. 6.6.6 is current, from 1 July 2026.
Who Firefly III is for#
Choose it if
- Someone who reconciles against real bank statements and wants the arithmetic to hold to the cent
- Multi-currency or many-account households where a single spending chart stops being useful
- People whose bank offers CSV or camt.053 exports and no usable API
- Anyone who wants import rules that classify and split transactions automatically rather than by hand
Look elsewhere if
- You want a five-minute setup and a pie chart, because the double-entry model has a genuine learning curve
- You will not run the cron container, in which case recurring transactions and bill checks silently never happen
- Envelope budgeting is your primary mental model; Actual Budget fits that far better
What running it actually looks like#
Four containers in the shape most people land on: fireflyiii/core listening on 8080, a mariadb:lts, a small Alpine container running crond, and fireflyiii/data-importer on its own 8080 published separately. Three if you skip the importer. Before first start, APP_KEY must be exactly 32 characters, DB_CONNECTION must match your database (mysql, pgsql or sqlite), APP_URL must be the external URL, and TRUSTED_PROXIES=** if anything sits in front, or generated links come out wrong. STATIC_CRON_TOKEN is also exactly 32 characters and the cron container builds its crontab from it at start. Mount /var/www/html/storage/upload for attachments. Schema migrations run automatically on container start, which makes upgrades a pull and restart but is also why release notes matter.
Resource profile#
- Memory
- 250 MB idle, around 600 MB in ordinary use. PHP-FPM in the app container plus whatever MariaDB or Postgres wants alongside it. Large reports are the thing that pushes it, and the docs tell you to raise PHP `memory_limit` to at least 256M before running those.
- CPU and acceleration
- Quiet during normal browsing. Spikes come from generating multi-year reports, running rules across a bulk import, and the nightly cron pass over recurring transactions.
- Storage growth
- The database grows roughly linearly with transaction count and stays modest, in the low hundreds of MB even after years. Attachments in the upload volume are the only unbounded part.
- Operational load
- 3 of 5, Moderate. Three or four containers with a real relational database, schema migrations that run on container start, and a restore that needs a dump plus the upload volume plus the original `APP_KEY`. Major releases have invalidated OAuth tokens, so integrations need rebuilding rather than just restarting.
Figures describe a small single-household install and are the working assumptions behind our stack planner. Your numbers will differ with library size, user count and hardware. See methodology.
Data and backup surface#
The database is effectively everything: accounts, transactions, rules, budgets, recurrences, categories. Take a proper dump with mysqldump or pg_dump rather than copying the database container's data directory while it runs. The second item is the upload volume at /var/www/html/storage/upload, holding transaction attachments, which are not in the database. Keep APP_KEY with the backup: Laravel uses it for encrypted values and sessions, and restoring a dump under a different key leaves you with data you cannot fully read. Everything under storage/framework and storage/logs is regenerable and can be excluded. The importer container holds no state beyond .importer.env and any import configuration JSON you mounted.
Derived backup shape
MySQL dump. mariadb-dump / mysqldump with --single-transaction, then snapshot the dump plus the app's data volume.
Traps and surprises#
The importer is a separate container with its own token
fireflyiii/data-importer is not bundled with Firefly III, deliberately, for security and maintenance separation. It runs beside the app, listens on 8080 inside its own container, and authenticates over the API with either FIREFLY_III_ACCESS_TOKEN (a personal access token) or FIREFLY_III_CLIENT_ID for OAuth. FIREFLY_III_URL has to be the address reachable on the container network, http://app:8080 in the official compose. Other values return a 500 rather than a useful message.
v6.6.0 invalidated every OAuth token and client
6.6.0 and 6.6.1 shipped backwards-incompatible dependency updates that killed all existing OAuth tokens and clients. Anything talking to the API, the data importer first among them, plus Home Assistant integrations and personal scripts, has to be reconfigured with newly generated tokens. 6.6.0 and later also issue UUID-style access tokens, so an older data importer will not accept them and needs updating in the same window.
No cron container means no recurring transactions
Firefly III ships no scheduler of its own. The official compose adds an Alpine container whose crontab fetches http://app:8080/api/v1/cron/$STATIC_CRON_TOKEN at 03:00 daily. The token must be exactly 32 characters or the endpoint refuses it. Without this running, recurring transactions never materialize and bills are never marked as paid or due, and nothing warns you: you find out weeks later when the numbers stopped matching reality.
DB_CONNECTION defaults to mysql
The shipped .env.example sets DB_CONNECTION=mysql with DB_HOST=db and port 3306. Point the stack at Postgres without changing it and startup fails on a connection error rather than anything that names the problem. Use pgsql with port 5432, and on PostgreSQL 15 or newer set PGSQL_SCHEMA to a schema you actually hold grants on, because the public schema no longer works by default there.
TRUSTED_PROXIES has to be set behind a reverse proxy
Behind Traefik, Caddy or nginx, leaving TRUSTED_PROXIES empty produces http:// links inside an HTTPS site, broken OAuth redirects and login loops that look like session bugs. The documented answer in .env.example is to set it to **. Pair that with an APP_URL that matches exactly what the browser uses, including scheme and any port, since the same value is used to validate requests and build email links.
Specifications#
- Category
- Tasks, recipes and personal finance
- Licence
- AGPL-3.0-or-later (Copyleft)
- Written in
- PHP (Laravel)
- First release
- 2015
- Maturity
- Mature
- Datastore
- MariaDB or MySQL by default, PostgreSQL supported, SQLite possible but discouraged
- Default ports
8080- Ships as
- docker, docker compose, self-managed PHP on a web server
- arm64 builds
- Yes
- Identity
- Trusted proxy header. `AUTHENTICATION_GUARD=remote_user_guard` trusts a proxy-injected header (`REMOTE_USER` by default) for Authelia and similar; LDAP support was dropped and there is no built-in OIDC.
- Replaces
- Mint, YNAB, Quicken, Monarch Money
- Project site
- www.firefly-iii.org
- Source
- github.com/firefly-iii/firefly-iii
- Documentation
- docs.firefly-iii.org
Alternatives to Firefly III#
Everything else in tasks, recipes and personal finance, closest in operational weight first.
| Tool | Ops load | Idle RAM | Licence |
|---|---|---|---|
| PLANKA | 3, Moderate | 220 MB | PLANKA Community License (Fair Use License 1.1) |
| Actual Budget | 2, Light | 120 MB | MIT |
| Mealie | 2, Light | 300 MB | AGPL-3.0-or-later |
| Vikunja | 2, Light | 70 MB | AGPL-3.0-or-later |
| grocy | 1, Set and forget | 40 MB | MIT |
Where Firefly III comes up elsewhere#
Head to head
Migration guides
Sources#
Facts on this page were checked against the project's own material. Where a number is our own estimate rather than a published figure, the text says so.
- Official docker-compose.yml, container layout and cron container
- Official compose with the data importer container
- Official .env.example, APP_KEY, DB_CONNECTION, STATIC_CRON_TOKEN, auth guard
- Data importer repository and supported import sources
- Release notes, current version and the v6.6.0 OAuth token change
Published . Last reviewed . Found something out of date? Tell us and we will fix it and log the change.