Firefly III: Self-Hosted Personal Finance Without the Bank Watching

Budgets, rules and reports that live on your own server

Every budgeting app I have ever tried eventually wanted three things: my bank login, a monthly subscription, and the right to “anonymise” my spending and sell it to whoever buys that sort of thing. I gave up on all of them and put Firefly III on a box in the corner of my flat instead. It has been quietly tracking every penny I move for years now, and the only entity studying my coffee habit is me.

Advertisement

Firefly III is a self-hosted personal finance manager. You run it yourself, on your own server, and your financial history never leaves the machine you put it on. That is the whole pitch, and for me it is the entire point.

Under the bonnet it works on a sensible, almost double-entry model. Money does not appear or vanish; it moves. Every transaction has a source and a destination. A withdrawal goes from one of your asset accounts (current account, savings, cash in a tin) to an expense account (Tesco, the landlord, your favourite pub). A deposit comes from a revenue account. Transfers shuffle money between your own accounts. Once that clicks, the reports start telling the truth, because nothing is double-counted or quietly lost.

On top of that you get the usual suspects, done properly:

  • Budgets — envelopes with a monthly amount, so you can see “groceries: £240 of £300 spent” at a glance.
  • Categories and tags — for slicing spending however you like.
  • Bills — expected recurring payments, so Firefly nags you when the council tax has not landed.
  • A rules engine — the part I love most. Match on description, amount, source, anything, and then act: set a category, add a tag, rename the payee. Auto-categorisation that you fully control.
  • Reports — proper breakdowns by period, category, budget and account, with charts that are actually readable.

Firefly III is a PHP application that wants a database behind it. Docker Compose is the least painful way to stand the whole thing up. Here is a stripped-down stack with the app, a MariaDB database, and the companion Data Importer:

version: "3.8"

services:
  app:
    image: fireflyiii/core:latest
    restart: unless-stopped
    depends_on:
      - db
    environment:
      - APP_KEY=GENERATE_A_32_CHARACTER_RANDOM_STRING
      - APP_URL=https://firefly.example.com
      - TZ=Europe/London
      - DB_CONNECTION=mysql
      - DB_HOST=db
      - DB_PORT=3306
      - DB_DATABASE=firefly
      - DB_USERNAME=firefly
      - DB_PASSWORD=use-a-long-password-here
    volumes:
      - ./firefly_upload:/var/www/html/storage/upload
    ports:
      - "8080:8080"

  db:
    image: mariadb:11
    restart: unless-stopped
    environment:
      - MYSQL_RANDOM_ROOT_PASSWORD=yes
      - MYSQL_USER=firefly
      - MYSQL_PASSWORD=use-a-long-password-here
      - MYSQL_DATABASE=firefly
    volumes:
      - ./firefly_db:/var/lib/mysql

  importer:
    image: fireflyiii/data-importer:latest
    restart: unless-stopped
    depends_on:
      - app
    environment:
      - FIREFLY_III_URL=http://app:8080
      - FIREFLY_III_ACCESS_TOKEN=paste-a-personal-access-token-here
      - TZ=Europe/London
    ports:
      - "8081:8080"

The APP_KEY must be exactly 32 random characters or the app refuses to start. Generate one with head -c 32 /dev/urandom | base64 | cut -c1-32 and never reuse it. Put the whole thing behind a reverse proxy with TLS — your transaction history is not something to serve over plain HTTP.

This is where honesty matters, because it is the part the glossy apps make look effortless and Firefly does not.

There is no magic, hands-off bank feed for most people. Firefly III itself does not log into your bank. What you get instead is the Data Importer, the companion service in the stack above. It can pull from a few real bank connectivity providers in regions where those exist, and crucially it imports CSV files.

In practice, CSV is how most of us live. You download a statement from your bank’s website, point the importer at it, and map the columns once: which is the date, which is the amount, which is the description. Firefly remembers that mapping as a configuration file, so next month it is a thirty-second job. The rules engine then catches the import and tidies everything — that Tesco line gets categorised, tagged and renamed automatically before you have even looked at it.

It is fiddlier than tapping “connect my bank” in a SaaS app. The trade-off is that nobody is brokering a permanent connection to your accounts on a third party’s servers.

Firefly III asks for discipline. If you go three months without importing a statement, you have three months of mapping and reconciling to slog through, and the temptation is to give up. The data is only as good as the habit feeding it. Entry is semi-manual: you are running imports, not living inside a fire-and-forget app.

The reward, and it is a real one, is clarity you cannot get any other way. Because you built the categories and wrote the rules, the reports mean exactly what you think they mean. I have caught two creeping subscriptions and one genuinely wrong direct debit purely because the numbers were finally trustworthy.

If you want zero effort and do not mind an app studying your spending, a hosted budgeting service will be less work — go and enjoy it. Firefly III is for the person who already self-hosts, owns the privacy argument, and is willing to spend ten minutes a week importing a statement in exchange for owning their entire financial picture outright.

That person is me, and after years of it I would not go back. Stand up the stack, import one month, write three rules, and see whether the clarity hooks you too.

Advertisement

Related Content

Advertisement
Smarc
Written by Smarc

Founder and editor of vo.rs. A lifelong tinkerer who self-hosts far more than is sensible, hardens Linux boxes for fun, and prods the latest AI tools to see what they can really do. The how-to guides here are the notes Smarc wishes had existed the first time round.