New releases
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
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.
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.
These are stored results of separate checks, not real-time data. An unknown status means the check has not been performed yet.
Recipe version compared with the latest known upstream release. A version number, not a reading of what changed.
Result of a dedicated Docker image scan, not a Compose configuration check.
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.
Upstream Compose changes relative to the verified recipe.
Pinned image tags checked for newer versions.
From observation to action
The recipe already pins the latest known release. The steps below show the evidence retained for that version.
6.7.96.7.9Open this release on GitHub ↗ The link is evidence to review, not proof of compatibility.
The version numbers do not cross a major compatibility boundary. No compatibility verdict has been recorded.
No image-tag change is currently planned.
Change the tag and restart. Release-specific migration steps still come from the release notes and installation guide.
No version change is currently planned. The recipe includes a backup procedure. Restore has been tested.
An exact Fully verified check for this target was recorded on 2026-10-29.
The status shows how fully the published recipe meets each level's requirements.
A minimal setup for a local network or access through a VPN.
The recipe covers every requirement at this level.
A public deployment with protected access and backups.
The recipe covers every requirement at this level.
A setup designed for predictable operations and observability.
You still need to add: external monitoring, cpu and memory limits.
Tailored to your server
Answer five questions and download a ready-to-run Firefly III bundle.
Read the full guide first: it explains external access, backups, and data locations.
cp .env.example .envdocker compose up -ddocker compose psUse 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
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.
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.
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.
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.
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.
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.
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.
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.
cron and firefly..env, preserving the correct encryption key and the same database major version. Set the previous FIREFLY_VERSION.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.bash restore.sh /path/to/pre-upgrade.tar. The safety copy preserves the current state, then the previous database and storage replace it.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.
Tell us how it went on your server
Opens a prefilled GitHub issue. A GitHub account is required; submitting the issue makes it public.
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.
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..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.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.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.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.References: Docker FAQ, backup guide, cron, and upstream issues.
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.