Actual Budget mit Docker installieren: Lokale Budgetverwaltung nach dem Envelope-Prinzip
Actual Budget, eine Open-Source-Alternative zu YNAB, in unter 10 Minuten per Docker Compose einrichten: fester Image-Tag, SQLite ohne Datenbankcontainer, Port nur lokal bis zum Serverpasswort, Backup und Update mit Verifizieren.
Geprüft am 03.11.2026 · für actual-server 26.11.0
WerbelinksMit * markierte Links sind Werbelinks: Bei einem Kauf erhalten wir eine Provision, der Preis bleibt gleich. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Mehr dazu

Wer Haushalts- oder Kleinbetriebsfinanzen nach dem Umschlag-Prinzip (Envelope-Methode) verwalten will, ohne Kontodaten in die Cloud zu schicken, findet in Actual Budget eine selbst gehostete Lösung. Die Node.js-Anwendung läuft als einzelner Container, speichert alle Daten lokal in SQLite und ermöglicht die Synchronisation zwischen Desktop, Browser und mobilen Apps über den eigenen Server, ohne externe Datenbankdienste. Actual Budget ist eine verbreitete Open-Source-Alternative zu YNAB.
Voraussetzungen
- Docker Engine ≥ 20.10 und das Docker Compose Plugin v2 (
docker compose, nicht das veraltetedocker-compose) auf einem Linux-Host, einer VM oder einem NAS mit Docker-Unterstützung. Die Installation beschreibt die Anleitung Docker und Docker Compose auf Linux installieren. - Mindestens 512 MB RAM frei (empfohlen: 1 GB), mindestens 1 GB freier Speicherplatz für Image und Daten.
- Netzwerkzugang für den einmaligen
docker pull-Vorgang. - Für HTTPS und öffentlichen Zugriff: ein vorgelagerter Reverse Proxy (nginx, Traefik, Caddy) und ein gültiges TLS-Zertifikat. Caddy mit automatischem HTTPS wird in der Anleitung Caddy als Reverse Proxy einrichten erklärt.
- Ein Texteditor sowie Terminal-Zugang zum Host.
Eckdaten auf einen Blick
| Eigenschaft | Wert |
|---|---|
| Primäres Image (Docker Hub) | docker.io/actualbudget/actual-server:26.11.0 |
| Alternatives Image (GHCR) | ghcr.io/actualbudget/actual:26.11.0 |
| Alpine-Variante (~70 MB) | actualbudget/actual-server:26.11.0-alpine |
| Port | 5006 (HTTP), hier an 127.0.0.1 gebunden |
| Pflicht-Volume | ./actual-data:/data |
| Datenbank | SQLite (eingebettet, kein externer Container) |
| Container-User (UID) | root (UID 0), das Image legt zusätzlich actual (UID 1001) an |
| Architekturen | amd64, arm64 (arm/v7 und arm/v6 nur Alpine) |
| Passwort beim Setup | Wird beim ersten Browser-Aufruf interaktiv gesetzt |
Schritt 1: Projektordner anlegen und Verzeichnisrechte setzen
Legen Sie einen eigenen Ordner für Actual Budget an; dort liegen Konfiguration und Datenverzeichnis:
sudo mkdir -p /opt/actual-budget/actual-data
cd /opt/actual-budgetDas Image legt einen Benutzer actual mit UID 1001 an, startet den Server aber als root: Im Test mit 26.11.0 meldete docker exec actual_budget id die Ausgabe uid=0(root), und die Dateien in server-files und user-files gehörten root. Der Server schrieb deshalb auch in einen frisch angelegten, root-eigenen Ordner ohne Fehler. Den Besitzer auf UID 1001 zu setzen, schadet nicht und hält den Ordner für diesen Benutzer bereit:
sudo chown -R 1001:1001 /opt/actual-budget/actual-dataVerzichten Sie auf chmod 777: Es macht die Finanzdaten für alle Benutzer des Hosts les- und schreibbar.
Verifizieren: Prüfen Sie Existenz und Besitzer des Ordners:
ls -la /opt/actual-budget/
# Erwartete Ausgabe (Ausschnitt):
# drwxr-xr-x ... 1001 1001 ... actual-dataSchritt 2: compose.yaml erstellen
Erstellen Sie im Projektordner die Datei compose.yaml. Der Stack besteht aus einem Dienst; SQLite ist im Image eingebettet:
services:
actual_server:
image: docker.io/actualbudget/actual-server:26.11.0
container_name: actual_budget
restart: unless-stopped
ports:
# Nur lokal, bis das Serverpasswort gesetzt ist; danach Reverse Proxy
# oder für LAN-Zugriff "5006:5006"
- "127.0.0.1:5006:5006"
volumes:
- ./actual-data:/data
environment:
# Port des Servers (Standard: 5006)
- ACTUAL_PORT=5006
# Bind-Adresse: :: = IPv4 und IPv6 (Standard)
# - ACTUAL_HOSTNAME=::
# Direktes HTTPS (alternativ: Reverse Proxy vorschalten)
# - ACTUAL_HTTPS_KEY=/data/certs/privkey.pem
# - ACTUAL_HTTPS_CERT=/data/certs/fullchain.pem
# Upload-Limits (optional, in MB)
# - ACTUAL_UPLOAD_FILE_SYNC_SIZE_LIMIT_MB=20
# - ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB=20
healthcheck:
test: ["CMD", "node", "scripts/health-check.js"]
interval: 60s
timeout: 10s
retries: 3
start_period: 20s
# Kein externer DB-Container nötig, Actual nutzt eingebettetes SQLite.
# Der Server läuft im Container als root (siehe Schritt 1).
# Beim ersten Browser-Aufruf erscheint ein Dialog zur Passwortvergabe (Bootstrap).Zum Image-Tag: 26.11.0 ist die aktuelle Version (Stand November 2026); die offizielle Beispieldatei nutzt latest, ein fester Tag macht Updates planbar. Für Raspberry Pi und ältere NAS-Systeme nehmen Sie 26.11.0-alpine: rund 70 MB und als einzige Variante für arm/v7 und arm/v6. nightly und edge sind Entwicklungsstände und nicht für den Produktivbetrieb gedacht. Neben Docker Hub veröffentlicht das Projekt dieselben Images unter ghcr.io/actualbudget/actual.
Der Port ist an 127.0.0.1 gebunden, weil bis zum Setzen des Passworts jeder Erstbesucher das Serverpasswort festlegen könnte.
Verifizieren: Prüfen Sie die YAML-Syntax vor dem Start:
docker compose config
# Erwartet: fehlerfreie Ausgabe der Konfiguration ohne WarnungenSchritt 3: Container starten
Starten Sie den Stack im Hintergrund; Docker lädt das Image beim ersten Aufruf:
docker compose up -dDer Download des Images dauert je nach Verbindung ein bis zwei Minuten (Standard-Image rund 120 MB, Alpine rund 70 MB).
Verifizieren: Prüfen Sie Status und Gesundheitszustand des Containers:
docker compose ps
# Ausgabe im Test (gekürzt):
# NAME IMAGE STATUS
# actual_budget docker.io/actualbudget/actual-server:26.11.0 Up 8 seconds (healthy)
docker compose logs actual_server --tail 20
# Erwartete Ausgabe (Ausschnitt):
# Listening on :::5006...Zeigt der Status zunächst starting, warten Sie die start_period von 20 Sekunden ab und prüfen Sie erneut.
Schritt 4: Erst-Einrichtung im Browser
Öffnen Sie im Browser auf dem Server-Host oder über einen SSH-Tunnel (ssh -L 5006:127.0.0.1:5006 benutzer@SERVER-IP):
http://localhost:5006Beim ersten Aufruf zeigt Actual Budget einen Bootstrap-Dialog für das Serverpasswort. Es schützt alle Budgetdateien; ein Standardpasswort gibt es nicht. Danach legen Sie ein neues Budget an oder importieren eine vorhandene Datei. Die Sprache stellen Sie unter Settings ein.
Erst jetzt, mit gesetztem Passwort, geben Sie den Dienst fürs LAN frei (Portzeile "5006:5006") oder schalten einen Reverse Proxy mit HTTPS vor.
Verifizieren: Testen Sie die HTTP-Erreichbarkeit per curl:
curl -I http://localhost:5006
# Erwartete Ausgabe:
# HTTP/1.1 200 OKSchritt 5: Datenpfade und Backup verstehen
Actual Budget legt im Volume ./actual-data automatisch zwei Unterverzeichnisse an:
| Pfad im Volume | Inhalt |
|---|---|
server-files/ | SQLite-Datenbank (account.sqlite), Session-Daten, Server-Konfiguration |
user-files/ | Individuelle Budget-Dateien aller angelegten Budgets |
Der Ordner actual-data enthält alle Daten. Stoppen Sie den Container für ein konsistentes Backup der SQLite-Dateien kurz:
cd /opt/actual-budget
docker compose stop
sudo tar czf actual-backup-$(date +%Y%m%d).tar.gz actual-data
docker compose start
# Ergebnis: actual-backup-20261103.tar.gzAutomatisieren lässt sich das mit der Anleitung Restic Backup auf Linux einrichten und automatisieren. Zur Wiederherstellung nach einem Totalverlust stoppen Sie den Stack, ersetzen den Datenordner durch den Inhalt des Archivs und starten neu. Achtung: rm -rf löscht den aktuellen Datenstand endgültig, prüfen Sie vorher das Archiv mit tar tzf:
cd /opt/actual-budget
docker compose down
sudo rm -rf actual-data
sudo tar xzf actual-backup-20261103.tar.gz
docker compose up -dIm Test mit 26.11.0 waren danach Serverpasswort und beide zuvor angelegten Budget-Dateien unverändert vorhanden. Zusätzlich exportiert die Weboberfläche unter Settings → Export data einzelne Budgets als Datei.
Verifizieren: tar tzf actual-backup-*.tar.gz | head listet server-files und user-files. Nach dem ersten Browser-Aufruf existieren beide Unterverzeichnisse:
ls -la /opt/actual-budget/actual-data/
# Erwartete Ausgabe:
# drwxr-xr-x ... server-files
# drwxr-xr-x ... user-filesSchritt 6: Umgebungsvariablen und optionale Konfiguration
Für den Standardbetrieb sind keine Umgebungsvariablen nötig. Die wichtigsten optionalen Parameter:
| Variable | Standard | Beschreibung |
|---|---|---|
ACTUAL_PORT | 5006 | Listener-Port des Servers im Container |
ACTUAL_HOSTNAME | :: | Bind-Adresse (:: = IPv4 + IPv6) |
ACTUAL_DATA_DIR | /data | Basispfad für alle Daten im Container |
ACTUAL_SERVER_FILES | /data/server-files | Pfad für SQLite-DB und Sessions |
ACTUAL_USER_FILES | /data/user-files | Pfad für Budget-Dateien |
ACTUAL_LOGIN_METHOD | password | Auth-Methode: password, header oder openid |
ACTUAL_HTTPS_KEY | leer | Pfad zum TLS-Schlüssel (direktes HTTPS) |
ACTUAL_HTTPS_CERT | leer | Pfad zum TLS-Zertifikat (direktes HTTPS) |
ACTUAL_TRUSTED_PROXIES | RFC-1918-intern | Vertrauenswürdige Proxy-IP-Bereiche |
ACTUAL_SIMPLEFIN_ALLOWED_NETWORKS | leer | Private IP-Adressen oder CIDR-Bereiche, die als SimpleFIN-Server erlaubt sind (seit 26.11.0, kommagetrennt) |
ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB | leer | Maximale Upload-Dateigröße in MB |
Eine in manchen Anleitungen genannte SECRET_KEY-Variable kennt die offizielle Dokumentation nicht. Sensible Werte (z. B. OpenID-Zugangsdaten) lagern Sie in eine .env-Datei aus und reichen sie in der compose.yaml per ${VARIABLE} durch:
# /opt/actual-budget/.env
ACTUAL_PORT=5006
# ACTUAL_LOGIN_METHOD=openidDocker Compose liest .env im Projektverzeichnis automatisch für die Variablenersetzung. Details erklärt die Anleitung Docker Compose: Multi-Container-Stacks aufbauen.
Verifizieren: Nach Änderungen an der Konfiguration den Container neu starten und Logs prüfen:
docker compose up -d
docker compose logs actual_server --tail 10
# Erwartet: keine Fehler, „Listening on :::5006...“Schritt 7: Updates und Wartung
Die Daten liegen im Volume ./actual-data, ein Update ersetzt nur den Container. Erstellen Sie vorher ein Backup (Schritt 5), lesen Sie die Release Notes auf GitHub und tragen Sie den neuen Tag in die compose.yaml ein:
# Nach Backup und neuem Tag in compose.yaml
docker compose pull
docker compose up -d
# Status nach dem Update prüfen
docker compose psDie Ausfallzeit beträgt wenige Sekunden. Zum Stoppen des Dienstes:
docker compose down
# Container wird gestoppt und entfernt; Volume ./actual-data bleibt erhaltenIm Test lief das Update von 26.10.0 auf 26.11.0 mit genau diesen Befehlen ohne Änderung an der compose.yaml außer dem Tag. Das Log meldete Migrations: DONE, Serverpasswort, Budget-Dateien und bereits angemeldete Sitzungen blieben erhalten. Laut den Release Notes zu 26.11.0 müssen Betreiber einer eigenen SimpleFIN-Bridge unter einer privaten IP-Adresse diese jetzt über ACTUAL_SIMPLEFIN_ALLOWED_NETWORKS freigeben (siehe Schritt 6); wer SimpleFIN nicht nutzt, muss nichts tun.
Verifizieren: Nach dem Update prüfen, ob der Container mit dem neuen Image läuft und welche Version der Server meldet:
docker compose ps
# Erwartet: Spalte IMAGE zeigt den neuen Tag, Status Up (healthy)
curl -s http://localhost:5006/info
# Ausgabe im Test:
# {"build":{"name":"@actual-app/sync-server","description":"actual syncing server","version":"26.11.0"}}Troubleshooting / Typische Fehler
- Bind for 127.0.0.1:5006 failed: port is already allocated: Port 5006 ist durch einen anderen Prozess belegt. Lösung: In der
compose.yamlden äußeren Port ändern, z. B."127.0.0.1:5007:5006", unddocker compose up -derneut ausführen. - exec format error beim Container-Start auf Raspberry Pi (arm/v6): Das Standard-Image gibt es nur für amd64 und arm64. Lösung: Tag auf
26.11.0-alpineändern; nur die Alpine-Images unterstützen arm/v7 und arm/v6. - Serverpasswort vergessen: Offizieller Weg:
docker exec -it actual_budget node /app/src/scripts/reset-password.jsausführen und das neue Passwort zweimal eingeben. Budget-Dateien bleiben erhalten. Das Skript meldetPassword changed!; bestehende Anmeldungen sind danach ungültig. Im Test mit 26.11.0 lieferte ein vorher ausgestelltes Sitzungs-Token anschließend HTTP 401, Browser und Geräte melden sich also mit dem neuen Passwort neu an. - Alle Daten nach Container-Neustart verschwunden: Das Volume wurde nicht korrekt eingebunden. Prüfen Sie, ob der
volumes-Eintrag in dercompose.yamlvorhanden ist und ob das Verzeichnis./actual-datakorrekt angelegt wurde.docker compose configzeigt den aufgelösten absoluten Pfad. - Mobile App verbindet sich nicht: Die Actual-App (iOS/Android) benötigt die vollständige Server-URL inklusive Port, z. B.
http://192.168.1.100:5006im LAN.localhostist vom Mobilgerät aus nicht erreichbar. Die Portzeile muss"5006:5006"lauten (nicht127.0.0.1), und die Host-Firewall muss Port 5006 für das LAN freigeben. Ohne HTTPS verweigern manche Browser und Apps die Verbindung; nutzen Sie dann einen Reverse Proxy. - Container bleibt im Status „starting“ und wird nicht „healthy“: Der Healthcheck benötigt bis zu 20 Sekunden Anlaufzeit. Prüfen Sie mit
docker compose logs actual_server, ob Fehlermeldungen erscheinen. - ACTUAL_HTTPS_KEY/CERT schlagen fehl: Die Pfade müssen auf Dateien innerhalb des Containers zeigen (z. B.
/data/certs/privkey.pem). Die Zertifikatsdateien müssen via Volume in das/data-Verzeichnis eingebunden werden.
Häufige Fragen
Braucht Actual Budget PostgreSQL oder MySQL?
Nein. Actual Budget nutzt SQLite als eingebettete Datenbank direkt im Container. Ein zweiter Datenbankcontainer ist nicht nötig.
Welches Image-Tag ist für den Dauerbetrieb empfohlen?
Ein fester Versions-Tag wie 26.11.0, auf schwächerer Hardware und 32-Bit-ARM 26.11.0-alpine. latest funktioniert ebenfalls, aktualisiert aber unkontrolliert bei jedem pull. nightly eignet sich nur zum Testen.
Wie wird die mobile App mit dem Server verbunden?
Actual Budget hat keine eigene App im Store, sondern ist eine Web-App: Öffnen Sie im mobilen Browser die Server-Adresse, z. B. https://budget.ihredomain.de hinter einem Reverse Proxy, und fügen Sie die Seite zum Startbildschirm hinzu. Anmeldung mit dem Serverpasswort.
Wie lässt sich HTTPS ohne Reverse Proxy einrichten?
Actual Budget kann TLS direkt aktivieren: Legen Sie die Zertifikatsdateien in einem Unterordner von ./actual-data/certs/ ab und setzen Sie in der compose.yaml die Variablen ACTUAL_HTTPS_KEY=/data/certs/privkey.pem und ACTUAL_HTTPS_CERT=/data/certs/fullchain.pem. Für automatisches HTTPS per Let's Encrypt ist ein vorgelagerter Reverse Proxy wie Caddy bequemer.
Was ist der Unterschied zwischen Docker Hub und der GitHub Container Registry?
Beide Registries veröffentlichen inhaltlich identische Images. Das Docker Hub-Image heißt actualbudget/actual-server, das GHCR-Image ghcr.io/actualbudget/actual (ohne -server). Die offizielle Beispieldatei nutzt Docker Hub; GHCR ist die Alternative bei Rate-Limits oder Registry-Beschränkungen.
Wie unterstützt Actual Budget mehrere Nutzer?
Mehrere Geräte oder Nutzer können sich mit demselben Server verbinden und separate Budget-Dateien anlegen. Für erweiterte Zugriffsszenarien kann die Login-Methode via ACTUAL_LOGIN_METHOD auf header (HTTP-Header-Auth durch Reverse Proxy) oder openid (OpenID Connect) umgestellt werden.
Fazit
Actual Budget kommt mit einem Container, einem Volume und ohne externen Datenbankdienst aus. Wer nach dem Umschlag-Prinzip budgetiert und keine Finanzdaten an Dritte geben will, erhält eine aktiv weiterentwickelte Lösung, die in unter zehn Minuten läuft. Für den Dauerbetrieb richten Sie HTTPS über einen Reverse Proxy und ein automatisiertes Backup ein.
Weiterführende Anleitungen und Quellen
- Docker und Docker Compose auf Linux installieren: Voraussetzung für diesen Stack
- Caddy als Reverse Proxy mit automatischem HTTPS einrichten: Ideal für den öffentlichen Zugriff auf Actual Budget
- Restic Backup auf Linux einrichten und automatisieren: Automatisches Backup des
actual-data-Ordners - Docker Compose absichern: Secrets, Healthchecks, Non-Root: Best Practices für den Produktivbetrieb
Offizielle Quellen: Actual Budget: Docker-Installationsdokumentation | Konfigurationsvariablen | Offizielles docker-compose.yml auf GitHub | Docker Hub: actualbudget/actual-server | Release Notes (26.11.0)


