Fully verified25,032

Personal finance

Firefly III

Firefly III records income, expenses, transfers and attachments, groups spending into categories and budgets, and tracks subscriptions and savings goals. This recipe runs the web application with PostgreSQL and a daily cron service; bank imports and the separate Data Importer are not included.

Overview

Firefly III

Firefly III is a personal finance ledger for people who want to keep account balances, income, expenses, transfers, budgets, subscriptions and savings goals on their own server. Attach a receipt to a transaction, categorize spending, and inspect how money moved through your accounts. It can replace the record-keeping part of Quicken or Monarch Money; it does not reproduce their managed bank connections.

Compared with an envelope-first tool such as Actual Budget, Firefly III centers on accounts and transactions, with budgets and rules built around that ledger. It is useful for detailed historical spending and multiple accounts. Choose an envelope tool when assigning every available unit of money is the primary workflow.

The recipe runs Firefly III 6.7.9, PostgreSQL 18.6 and an Alpine 3.22.2 cron service. Scheduled jobs run daily at 03:00 UTC, including recurring transactions and automatic budgets. The separate Data Importer and external bank services are not installed. SMTP is also unconfigured: mail is written to the protected application log.

The browser is the primary client. Android and iOS clients are third-party projects, including Waterfly III and Abacus, listed in the official integration directory; no desktop client is included, and those mobile apps were not tested. English and Russian are available in the interface. The application and its logo use AGPL-3.0-or-later, while these deployment files use MIT; the logo notice is in assets/NOTICE.

Local email/password login and native TOTP are available. TOTP must be enabled in the profile and recovery codes stored safely. There is no native OIDC or SAML integration. An optional remote-user header guard exists upstream, but this recipe keeps password login: the header guard performs no identity checks of its own.

The default single-user mode closes public registration after the owner registers. Before that, whoever reaches the installation first can become its owner. Complete registration over loopback or an SSH tunnel before opening the proxy or LAN access.

Financial records live in PostgreSQL; attachments and the OAuth signing keys have their own persistent storage. The recipe deliberately preserves the whole Laravel storage directory as well as the separate upload volume, so recreating a container or restoring an archive does not invalidate API tokens by losing their signing keys. Treat all three volumes and .env as sensitive. Backup archives include unencrypted financial records and the keys needed to read encrypted attachments.

The amd64 smoke test exercises registration, login, a personal API token, an account, an expense, its category and tag, a real attachment, the cron endpoint and recovery after deleting every volume. arm64, overnight scheduling, TOTP, mail, bank integrations and mobile clients remain untested. Eight CRITICAL package findings across the pinned images are retained in the security snapshot. This functional check is not a security certification.

The home and VPS levels are supported after safe owner bootstrap. The reliable level is partial: PostgreSQL and healthchecks are present, but resource limits, external monitoring and backup scheduling still need an operator. Size the server with at least 1 CPU, 1 GiB RAM and 5 GiB disk; 2 GiB RAM gives more room for PHP and PostgreSQL. Large attachments and history need more storage.

Official references: Docker installation, backups, authentication, cron, and 6.7.9 release notes.

Update tracking

These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.

New releases

Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.

Current version 6.7.9Oct 29, 2026, 7:48 PM UTC

Breaking updates

A new major version is found by comparing version numbers. A compatibility verdict is entered by hand after reading the release notes and the migration steps.

Not checkedNot checked

compose.yaml

Upstream Compose changes relative to the verified recipe.

Upstream Compose is not trackedNot checked

Outdated Docker images

Pinned image tags checked for newer versions.

Not checkedNot checked

From observation to action

Update plan

The recipe already pins the latest known release. The steps below show the evidence retained for that version.

6.7.96.7.9
  1. Open the release evidence

    Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.

  2. Review the version boundary

    The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.

  3. Prepare image-tag changes

    No image-tag change is currently planned.

  4. Follow the documented migration shape

    Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.

  5. Protect the rollback point

    No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.

  6. Verify the updated recipe

    An exact Fully verified check for this target was recorded on 2026-10-29.

Read the update procedure in the installation guide ↓

Specifications

Security and exposure

Installation levels

The status shows how fully the published recipe meets each level's requirements.

Ready

For home

A minimal setup for a local network or access through a VPN.

  • Single server
  • Local access
  • Minimum components

The recipe covers every requirement at this level.

Ready

For a VPS

A public deployment with protected access and backups.

  • Domain
  • HTTPS via reverse proxy
  • Off-server backup

The recipe covers every requirement at this level.

Partial

Reliable

A setup designed for predictable operations and observability.

  • Separate database
  • Healthcheck
  • External monitoring
  • CPU and memory limits

You still need to add: external monitoring, cpu and memory limits.

Tailored to your server

Configuration generator

Answer five questions and download a ready-to-run Firefly III bundle.

Runs locally
01 Do you have a domain?
02 Which reverse proxy?
03 Server architecture
04 Where should data be stored?

Use an absolute path for a separate disk, such as /srv/firefly-iii.

05 Is VPN access required?

Your bundle is ready

The archive is created in your browser. Your answers are never sent anywhere.

The same files as one shell script, written in plain heredocs so you can read the whole thing before it runs. It checks Docker, the architecture, memory, disk and the ports it needs, and writes nothing until you add --apply.

Screenshots

Quick start

Read the full guide first: it explains external access, backups, and data locations.

Prepare settingscp .env.example .env
Launchdocker compose up -d
Check statusdocker compose ps

Step-by-step guide

Install and maintain Firefly III

Ubuntu and Debian

Use Ubuntu 24.04/26.04 LTS or Debian 12/13 with Docker Engine and Docker Compose v2.24 or newer, following Docker’s installation guide. Commands run from this recipe directory. Install openssl; Python 3 and curl are needed only for the smoke test. Docker access grants administrative control of the host.

cp .env.example .env
chmod 600 .env
openssl rand -hex 16  # FIREFLY_APP_KEY: exactly 32 characters
openssl rand -hex 16  # FIREFLY_CRON_TOKEN: exactly 32 characters
openssl rand -hex 24  # FIREFLY_DB_PASSWORD

Paste the three different results into .env, set FIREFLY_SITE_OWNER, and leave the service bound to loopback for the first registration. The entrypoint rejects the template key, template database password and incorrect key/token lengths before HTTP starts. The administrator password is created in the browser, not in .env.

docker compose --env-file .env config --quiet
docker compose --env-file .env pull
docker compose --env-file .env up -d --wait --wait-timeout 600
docker compose ps

VPS and first owner

Create the owner account before publishing the proxy. For a remote server, open a tunnel from your computer:

ssh -L 8088:127.0.0.1:8088 user@server

Open http://localhost:8088, register with a unique password of at least 16 characters, and finish the account/currency setup. The default single-user mode closes registration after the first account. Enable TOTP in the profile and keep recovery codes offline. Only then enable public HTTPS. Keep PostgreSQL and cron ports unpublished; configure a firewall, updates and encrypted off-server backups.

Trusted LAN or VPN

Use the same tunnel for bootstrap. After registration, replace 127.0.0.1 in the web port mapping with the server’s private address, set FIREFLY_URL=http://<private-address>:8088, and recreate the services. Restrict access to trusted clients or a VPN. A private address without HTTPS still carries login sessions and financial information in cleartext.

Domain and HTTPS

Point money.example.com at the server and set FIREFLY_URL=https://money.example.com. Set FIREFLY_TRUSTED_PROXIES to the exact address or CIDR of your proxy, as seen by the application, then run docker compose up -d --wait --wait-timeout 600. Do not use ** unless all direct access is controlled. Keep AUTHENTICATION_GUARD=web; forwarded protocol headers do not enable single sign-on.

Caddy, Nginx and Traefik

proxy/Caddyfile routes an HTTPS domain to the loopback port. proxy/nginx.conf requires your certificate paths; check with nginx -t before reload. proxy/traefik.yaml is a dynamic file-provider configuration for an existing Traefik installation with the declared certificate resolver. These examples assume the proxy runs on the Docker host. A containerized proxy must share a controlled network or use a reachable host address; its 127.0.0.1 points to itself.

Data and every setting

Compose creates project-scoped database, storage and uploads volumes. PostgreSQL 18 stores data under /var/lib/postgresql; Laravel storage is /var/www/html/storage; the separate upload volume overlays /var/www/html/storage/upload. The storage volume includes OAuth signing keys. .env contains the attachment/session encryption key; losing FIREFLY_APP_KEY can make encrypted data unreadable.

Variable Meaning
FIREFLY_VERSION Application version, 6.7.9; the image tag is version-6.7.9.
FIREFLY_PORT Loopback HTTP port, 8088.
FIREFLY_URL External URL including scheme and port, if nonstandard.
FIREFLY_APP_KEY Secret of exactly 32 characters; retain for restoration.
FIREFLY_DB_PASSWORD Unique PostgreSQL password. Set before first start; changing only .env does not change the stored database role password.
FIREFLY_CRON_TOKEN Independent 32-character secret for scheduled HTTP jobs.
FIREFLY_SITE_OWNER Operator email shown in some errors; use an appropriate contact.
FIREFLY_LANGUAGE Initial interface language, en_US or ru_RU; users may override it.
FIREFLY_TRUSTED_PROXIES Trusted proxy IP/CIDR; empty by default.
TZ Application timezone, UTC by default. The cron service always runs at 03:00 UTC.

The scripts also accept optional FOSSARY_ENV_FILE, COMPOSE_PROJECT_NAME and FIREFLY_BACKUP_DIR. Defaults are .env, project firefly-iii, and backups/. If you started with a custom docker compose -p name, export that exact COMPOSE_PROJECT_NAME for every backup and restore command. The daily cron covers recurring transactions and automatic budgets; bank import and backup scheduling are separate operations. SMTP is not configured; mail links go to application logs and must be protected.

Backup

bash backup.sh

The script stops cron and the application, creates a logical PostgreSQL dump and two storage archives, includes .env and Compose, then restarts only the services that were running. Archives use mode 600 and unique names. Encrypt a copy on another machine and test restoration regularly. Exported transactions are not a complete backup.

Restore — replaces financial data

Use only trusted archives. Keep a copy of the old .env, especially FIREFLY_APP_KEY, and the matching application/database versions. On a new server, start only the database and create the application containers without starting them:

docker compose up -d --wait database
docker compose create --no-deps firefly cron

Then restore:

bash restore.sh /absolute/path/firefly-iii-<timestamp>.tar

The script validates archive members, creates a safety backup, stops writers, recreates the application database, replaces both storage volumes and waits for health. It does not activate the archived .env. Check account balances, transactions, attachments and API tokens before reopening access. A failure leaves writers stopped; inspect logs and fix the error before restarting. Recovery onto another server also needs the archived secrets and a corrected external URL.

Update

Read the release notes and upgrade guide. Save the current .env, Compose and a verified backup. Stop cron and the application, change only FIREFLY_VERSION to the reviewed pinned version, pull and restart:

bash backup.sh
docker compose stop cron firefly
# Edit FIREFLY_VERSION in .env; keep the database major version unchanged.
docker compose pull firefly
docker compose up -d --wait --wait-timeout 600
docker compose logs --tail=100 firefly

Startup runs database migrations automatically. Check balances, receipt downloads, scheduled jobs and API access. PostgreSQL major upgrades require their own export/import plan; never point an existing data directory at a different major version.

Rollback

  1. Close external access and stop cron and firefly.
  2. Restore the previous Compose and .env, preserving the correct encryption key and the same database major version. Set the previous FIREFLY_VERSION.
  3. Pull that image, run docker compose up -d --wait database, then docker compose create --no-deps firefly cron. Keep the application stopped so it cannot migrate the current database with older code.
  4. Run bash restore.sh /path/to/pre-upgrade.tar. The safety copy preserves the current state, then the previous database and storage replace it.
  5. Verify login, balances, transactions, attachments and API access before reopening the proxy. Never downgrade the image over a migrated database and assume compatibility.

Stop or remove

docker compose down retains the volumes. To erase the installation after exporting and verifying an external backup, run docker compose down --volumes --remove-orphans. This destroys all financial records, attachments and OAuth keys. Remove .env and local archives separately when they are no longer needed.

See the official backup and authentication guides before changing the security model.

Report a recipe problem

Tell us how it went on your server

Opens a prefilled GitHub issue. A GitHub account is required; submitting the issue makes it public.

Troubleshooting

Troubleshooting Firefly III

Start with docker compose ps --all, docker compose logs --tail=200 firefly database cron, docker compose --env-file .env config --quiet, and curl -i http://127.0.0.1:8088/health. Do not paste resolved configuration, tokens, financial records or password-reset links into a public issue.

  • The application restarts immediately: check the credential guard message. FIREFLY_APP_KEY and FIREFLY_CRON_TOKEN must be independent 32-character values; replace the template database password. Never rotate an established encryption key to fix a startup error.
  • Database authentication fails: changing .env does not change the password in an initialized PostgreSQL volume. Restore the original secret or deliberately alter the database role password; do not delete volumes holding financial data.
  • The first visitor created the wrong owner: close access immediately. For a genuinely empty, disposable installation, remove its volumes and bootstrap again over loopback. If records already exist, preserve a backup and follow upstream account recovery guidance instead of resetting data.
  • Registration is unavailable: this is expected after the first account in default single-user mode. Use owner invitations or review the upstream multi-user settings deliberately; do not disable the protection merely to complete a smoke test.
  • HTTPS redirects, session or CSRF errors: compare FIREFLY_URL with the real browser origin, forward the original host/protocol and trust only the proxy address. Recreate the service after env changes. Check that the proxy does not send requests to its own loopback address.
  • No recurring transactions or automatic budgets: check the cron container and its crontab with docker compose exec cron crontab -l. Its schedule is 03:00 UTC regardless of TZ. To check manually, use docker compose exec firefly php artisan firefly-iii:cron; a daily job may report that it already ran. Treat the static HTTP token and access logs as secrets.
  • Attachments or API tokens fail after recovery: restore the matching FIREFLY_APP_KEY, the PostgreSQL dump, storage keys and uploads together. Replacing only the database is incomplete. Protect a copy of the current volumes before attempting another restore.
  • Upgrade failure: leave access closed, inspect the application migration log and restore a pre-upgrade archive with the matching image. Do not remove database tables or force an older image over the upgraded schema.
  • No bank feed or email: neither the Data Importer nor SMTP is configured. Browser file exports are not backups. Review the official installation and mail documentation before enabling integrations.

References: Docker FAQ, backup guide, cron, and upstream issues.

Official sources

Latest release: 2026-10-11 · GitHub Stars: 25,032 · metadata checked Oct 29, 2026, 7:48 PM UTC

Disclaimer

Fossary is an independent informational catalog and is not affiliated with the developers of the listed applications. We collect information from public sources and verify published recipes, but we do not develop, distribute, or control these applications.

A listing or validation status is not a requirement, endorsement, or personal recommendation to use an application. You decide whether it is suitable and use it at your own risk. We do not warrant an application's security, availability, or fitness for purpose and, to the extent permitted by law, are not liable for resulting loss or damage. Review its license, security settings, data processing, and backup procedures before installation.