Self-host Firefly III on a managed VPS: your finances on your own server

When you self-host Firefly III on a managed VPS, your accounts, budgets and spending live on a machine of your own, which our teams keep up to date and monitor day and night. Firefly III is a free and open source personal finance manager under the AGPL license. This guide follows its official documentation: installing with Docker Compose, the APP_KEY, HTTPS, the first account, the cron job, the database, the data importer, email, backups and updates.

Simon Vasconcelos Lee By Published on 10 November 202613-minute read

All guides

In short

  1. The AGPL-3.0-or-later license lets you run Firefly III freely; if you modify it, you offer the source code of your version to the people who use it.
  2. Install it from the three official files: docker-compose.yml, .env and .db.env. Pick the database password before the first start.
  3. APP_KEY is exactly 32 characters long, stays secret and is backed up along with the data.
  4. Behind an HTTPS reverse proxy, set TRUSTED_PROXIES=* and your address in APP_URL, then create your account right away: the first user to register becomes the owner, and registration closes after that.
  5. The cron service in the official file runs auto-budgets and recurring transactions every night. Back up the database, the upload volume and .env before every update.

What Firefly III does

Firefly III is a personal finance manager built for self-hosting. It tracks your expenses and income, with budgets, categories and tags, so you can see where your money goes. Its README describes it as completely self-hosted and isolated: it contacts no outside server until you explicitly tell it to.

One instance hosts as many users as you like, each with a financial administration fully separated from the others. The documentation describes a model for sharing a single administration, currently in development.

What the AGPL license allows

Firefly III is published under the GNU Affero General Public License, version 3 or, at your option, any later version: that is the SPDX identifier AGPL-3.0-or-later in its composer.json file. The text affirms an unlimited permission to run the unmodified program. You can therefore use the image published by the project for yourself, your family or your company, with no license to buy.

Section 13 adds a condition specific to the AGPL: if you modify the program, your version must offer everyone who uses it remotely over a network a way to get its source code at no charge. The Data Importer is under the same license.

Installing with Docker Compose

The documentation calls Docker Compose the easiest way to install Firefly III. On a server with Docker installed, create a folder and download the three official files into it, raw: their spacing matters, and the documentation asks you not to copy them from the browser.

Terminal
mkdir firefly-iii && cd firefly-iii
curl -o docker-compose.yml https://raw.githubusercontent.com/firefly-iii/docker/main/docker-compose.yml
curl -o .env https://raw.githubusercontent.com/firefly-iii/firefly-iii/main/.env.example
curl -o .db.env https://raw.githubusercontent.com/firefly-iii/docker/main/database.env

Settings to change before the first start

Generate two 32-character strings with the command given in the .env file: one for the key, one for the database password.

Terminal
head /dev/urandom | LC_ALL=C tr -dc 'A-Za-z0-9' | head -c 32 && echo
.env, the lines to change
SITE_OWNER=you@example.com
APP_KEY=
DB_PASSWORD=
APP_URL=https://finances.example.com
.db.env
MYSQL_PASSWORD=

Pick the database password before the first start. The database stores it in its volume as soon as it is created: change it afterward and it no longer matches, and the Docker FAQ offers only one way out, removing the containers and volumes and starting over.

A port published locally

The official file publishes the application on port 80 of every address on the server. To serve it over HTTPS behind a reverse proxy on the same machine, change this single line in the app service:

docker-compose.yml, in the app service
ports:
  - 127.0.0.1:8080:8080
Terminal
docker compose -f docker-compose.yml up -d --pull=always
docker compose -f docker-compose.yml logs -f

On first start, the container creates and migrates the database by itself. When installation is done, Firefly III thanks you for installing it in the log: move on to the reverse proxy.

The documentation gives no memory figure; its Docker FAQ suggests PHP_MEMORY_LIMIT=512M or more if PHP runs out of memory. The size depends on the number of users, the volume of transactions and the data importer. On a managed VPS, describe your use to us and we will recommend the right size.

HTTPS and the reverse proxy

Firefly III serves the application over HTTP on port 8080 inside the container, and HTTPS sits on the reverse proxy in front of it. The application reads the X-Forwarded-Proto header to know that the visitor arrived over HTTPS, as long as it trusts the proxy:

.env
TRUSTED_PROXIES=*

The Docker FAQ is clear on this point: without TRUSTED_PROXIES=*, links stay on http, forms and charts stop working, and CSP security headers fail. With the port published on 127.0.0.1, only the server’s own proxy reaches the application. Recreate the container after each change to the .env file:

Terminal
docker compose up -d --force-recreate app

With Caddy

Caddy gets the Let’s Encrypt certificate on its own, and its reverse_proxy directive sets the X-Forwarded-For, X-Forwarded-Proto and X-Forwarded-Host headers by default:

Caddyfile
finances.example.com {
  reverse_proxy 127.0.0.1:8080
}

With Nginx and Certbot

This block follows the headers from the installation FAQ, and the buffers it suggests against 502 errors when the application returns a token in a long header. It listens on port 80 so that Certbot can set up HTTPS:

Nginx block
server {
    listen 80;
    listen [::]:80;
    server_name finances.example.com;

    client_max_body_size 64M;
    proxy_buffer_size 128k;
    proxy_buffers 4 256k;
    proxy_busy_buffers_size 256k;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Host $host;
        proxy_set_header X-Forwarded-Server $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_read_timeout 300s;
    }
}

Certbot then installs the certificate and takes care of renewing it:

Terminal
sudo certbot --nginx

The first account and registration

The first user to register becomes the owner of the instance. As a security measure, registration then closes on its own: most people install Firefly III just for themselves.

  1. Open your HTTPS address as soon as the proxy answers, and create your account: it takes the owner role.
  2. Turn on two-factor authentication in your profile, and keep the eight backup codes it shows.
  3. To open the instance to other people, go to /settings, open the Firefly III configuration options, untick the option that disables registration and save.

The cron job

Several features only work with a scheduled job: auto-budgets, recurring transactions, subscription warnings and up-to-date exchange rates. The Docker image does not run cron by itself; the official file adds a small cron service for this, which calls the application every day at 3 a.m.

It authenticates with a static token. Generate a third string with the same command, and put it in .env:

.env
STATIC_CRON_TOKEN=

This token is exactly 32 characters long: the version 6.7.9 code accepts no other length. Here is the service as the official file declares it; the double $$ is intentional and refers to the TZ and STATIC_CRON_TOKEN lines of the .env file:

docker-compose.yml, the cron service
cron:
  image: alpine
  restart: always
  container_name: firefly_iii_cron
  env_file: .env
  init: true
  command: ["sh", "-c", "apk add tzdata && \
    (ln -s /usr/share/zoneinfo/$$TZ /etc/localtime || true) && \
    echo \"0 3 * * * wget -qO- http://app:8080/api/v1/cron/$$STATIC_CRON_TOKEN;echo\" \
    | crontab - && \
    exec crond -f -L /dev/stdout"]
  networks:
    - firefly_iii
  depends_on:
    - app

One job is enough for every user on the instance. To check that it ran, read the container log: each job reports back in JSON, with job_fired and job_succeeded.

Terminal
docker logs firefly_iii_cron

To run it by hand, the Docker FAQ asks you to go through the www-data user rather than root: the FAQ links this pitfall to permission errors on log files:

Terminal
docker exec --user www-data firefly_iii_core /usr/local/bin/php /var/www/html/artisan firefly-iii:cron --force

MariaDB, PostgreSQL or SQLite

The official file ships MariaDB, with the mariadb:lts image. Firefly III also supports PostgreSQL and SQLite, chosen with DB_CONNECTION in .env:

DatabaseSetupBackup
MariaDB or MySQLThe db service from the official file; DB_CONNECTION=mysql, port 3306A dump with mariadb-dump, or the database volume with the service stopped
PostgreSQLDB_CONNECTION=pgsql, port 5432, the postgres image and POSTGRES_* variables in .db.envA dump with pg_dump, or the database volume with the service stopped
SQLiteDB_CONNECTION=sqlite, no db service, and a volume mounted on /var/www/html/storage/databaseThe database volume, with the service stopped
.env (PostgreSQL)
DB_CONNECTION=pgsql
DB_HOST=db
DB_PORT=5432

For PostgreSQL, the documentation also points the db service to the postgres image, which installs PostgreSQL 18, and mounts its volume on /var/lib/postgresql. In .db.env, the MYSQL_* variables become POSTGRES_USER, POSTGRES_DB and POSTGRES_PASSWORD.

Make this choice before entering your first transactions. The FAQ says moving from one database to another remains possible, without being supported by Firefly III itself, and that the application’s export does not carry every piece of data.

The Data Importer

The Firefly III Data Importer is a separate tool, with its own installation and its own updates; its version 2.3.5 came out on September 23, 2026. It imports CSV and CAMT.053 files, and can also fetch transactions from your banks through third-party data providers, each with its own terms.

The official Docker repository ships it in a second file, docker-compose-importer.yml, which keeps the same services and adds importer. Its settings go in an .importer.env file, taken from the importer’s .env.example. Serve it under its own domain name, with a port published locally and a second proxy block: the FAQ asks you not to place it in a subdirectory.

.importer.env, the lines to change
FIREFLY_III_URL=http://app:8080
VANITY_URL=https://finances.example.com
TRUSTED_PROXIES=*

Email: the SMTP configuration

By default, MAIL_MAILER=log writes messages to the logs instead of sending them. To receive notifications, such as subscription warnings, set up sending in .env. The documentation asks you to always change MAIL_FROM and SITE_OWNER, or no email will ever go out.

On a Simafri VPS, sending goes through an authenticated SMTP relay, on port 587: the address of an email account you already have, or Simafri Suite.

.env
MAIL_MAILER=smtp
MAIL_HOST=smtp.example.com
MAIL_PORT=587
MAIL_FROM=finances@example.com
MAIL_USERNAME=finances@example.com
MAIL_PASSWORD=account_password
MAIL_ENCRYPTION=tls

Backups

Firefly III has no built-in backup routine: the documentation recommends the system’s own tools, and points out that the application’s export function is not a backup. With Docker, here is what to keep:

ItemWhat it holdsBackup
The database (firefly_iii_db)Accounts, transactions, budgets, rules and usersRequired
Attachments (firefly_iii_upload)Files attached to transactionsRequired
.envThe settings, and above all APP_KEYRequired, encrypted
.db.envDatabase credentialsRequired, encrypted
docker-compose.yml and .importer.envYour version of the official filesRecommended

The database, with the service running

With MariaDB, mariadb-dump exports the database without stopping the service; its --single-transaction option gives a consistent copy of InnoDB tables without locking them. Run from the Compose file folder, the command reads the credentials from the container’s environment:

Terminal
docker exec firefly_iii_db sh -c 'mariadb-dump --single-transaction -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' > firefly-$(date +%F).sql

With PostgreSQL, the same idea goes through pg_dump:

Terminal
docker exec firefly_iii_db sh -c 'pg_dump -U "$POSTGRES_USER" "$POSTGRES_DB"' > firefly-$(date +%F).sql

Schedule this dump on the server at least once a day, and sort through the files, which pile up.

The volumes, with the service stopped

The documentation archives each volume with a throwaway container. Docker Compose prefixes volume names with the project name, which is the folder name by default: check the exact names before you run the archive.

Terminal, in the firefly-iii folder
docker volume ls
docker compose stop
docker run --rm -v "firefly-iii_firefly_iii_db:/tmp" -v "$HOME/backups/firefly:/backup" \
    ubuntu tar -czvf /backup/firefly_db.tar /tmp
docker run --rm -v "firefly-iii_firefly_iii_upload:/tmp" -v "$HOME/backups/firefly:/backup" \
    ubuntu tar -czvf /backup/firefly_upload.tar /tmp
docker compose start

A misspelled volume name raises no error: Docker creates an empty volume under that name and archives it, which overwrites the previous backup. The documentation asks you to check that the volume exists before every archive.

Keep a copy of .env and of these archives off the server, encrypted, since it carries the key and the database credentials. Stored on the server’s disk, these files are also part of its nightly backups.

Restoring

The documentation restores a volume with the reverse command, with the service stopped; the attachments volume is restored the same way:

Terminal
docker run --rm -v "firefly-iii_firefly_iii_db:/recover" -v "$HOME/backups/firefly:/backup" \
    ubuntu tar -xvf /backup/firefly_db.tar -C /recover --strip 1

An SQL dump is loaded back into the running database:

Terminal
docker exec -i firefly_iii_db sh -c 'mariadb -u"$MYSQL_USER" -p"$MYSQL_PASSWORD" "$MYSQL_DATABASE"' < firefly-YYYY-MM-DD.sql

Restore .env with its APP_KEY too, then start again and open your accounts. The documentation repeats it after every method: the first thing to do with a backup is to restore it, to check that it works.

Updates and migrations

Firefly III upgrades itself, even from versions as old as 4.7, and migrates its database when the container starts. The documentation warns that some upgrades remove or clean up data, and asks for a backup of the database and volumes before each one, especially across major versions. From the Compose file folder:

Terminal
docker compose stop
docker compose pull
docker compose -f docker-compose.yml up -d --remove-orphans

The importer updates the same way, with docker compose rm -f between stopping and pulling the images, according to its documentation.

Who does what on a managed VPS

Self-hosting means running the server on top of the application. On a managed VPS, our teams take charge of the server and proactively monitor your instance; Firefly III remains your software, with its settings and your financial data.

TopicOur teamsYou
The serverSystem and security updates, server interventions includedThe timing that suits your business
MonitoringServer monitored 24/7, with intervention without waiting for your callReading your instance’s logs
Firefly IIIProactive monitoring: errors, slowdowns, the cron job and disk space spotted, fix recommendedIts installation, updates and settings, which you apply, yourself or through your AI assistant
Accounts and dataThe server that carries them, monitored and backed upUsers, two-factor authentication, the data importer and its data providers
BackupsEvery night, encrypted and kept off the machine; restores includedFirefly III’s own dumps, and the restore request, when you need one
MigrationYour current server migrated, free of chargeThe timing of the switch

If you would rather have our teams work inside the application itself, that service is billed by the hour or at a fixed rate, with an estimate shared before the work starts.

Firefly III, on a server run for you

With a Simafri Managed VPS, our teams keep your Firefly III instance’s server up to date, monitor it day and night and step in without waiting for your call; they also proactively monitor your instance and recommend useful fixes, which you apply, yourself or through your AI assistant. Backups are encrypted every night and kept off the machine, restores are included, and migrating your current server is complimentary.

Explore Managed VPS

Frequently asked questions

Is Firefly III free?

Yes. Firefly III is under the AGPL-3.0-or-later license: you can run it for yourself, your family or your company with no license to buy. If you modify it, your version must offer its source code to the people who use it over the network. What remains is the cost of the server that runs it.

How do I install Firefly III with Docker Compose?

Download the three official files, docker-compose.yml, .env and .db.env. Set a 32-character APP_KEY and the same password in DB_PASSWORD and MYSQL_PASSWORD, before the first start, then run docker compose up -d. Next, put an HTTPS reverse proxy in front.

What is APP_KEY for in Firefly III?

APP_KEY is the encryption key for sessions. It is exactly 32 characters long, with no special characters such as = or #, and stays secret. The documentation asks you to back it up along with the volumes: it is part of what you need to restore an instance.

Why do Firefly III links stay on http behind a proxy?

The Docker FAQ says TRUSTED_PROXIES=* is missing from the .env file: without it, Firefly III ignores the proxy’s X-Forwarded-Proto header, links stay on http and forms and charts stop working. Also set your public address in APP_URL.

How do I set up the Firefly III cron job with Docker?

The official Compose file includes a cron service that calls the application every day at 3 a.m. Set a STATIC_CRON_TOKEN of exactly 32 characters in .env, then restart the stack. A single job serves every user on the instance.

How do I open Firefly III to several users?

The first user to register becomes the owner, and registration closes after that. The owner reopens it in /settings, under the Firefly III configuration options. Each user then keeps a financial administration separated from the others.

How do I back up Firefly III?

Back up the database, with a mariadb-dump or pg_dump export, the attachments volume, and the .env and .db.env files, encrypted. The application’s export function is not a backup. Test the restore right after the first backup.

Does a managed VPS cover Firefly III?

A managed VPS covers the server: system, security, monitoring and backups. Our teams also proactively monitor your instance and recommend useful fixes, which you apply. Work inside the application is billed by the hour or at a fixed rate.

Sources and official documentation

Contact us