Actual Budget mit Docker installieren: Lokale Budgetverwaltung nach dem Envelope-Prinzip
Actual Budget ist die beliebteste Open-Source-Alternative zu YNAB mit über 27.000 GitHub-Stars. Diese Anleitung zeigt, wie du den Budgetserver in unter 10 Minuten per Docker Compose aufstellst – datenschutzkonform, ohne Cloud-Abhängigkeit, mit SQLite und optionaler Geräte-Synchronisation.

Wer seine Haushaltsfinanzen nach dem Umschlag-Prinzip (Envelope-Methode) verwalten möchte, ohne seine Kontodaten in die Cloud zu schicken, hat mit Actual Budget eine ernstzunehmende Option. 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 – ganz ohne externe Datenbankdienste. Mit über 27.000 GitHub-Stars ist Actual Budget die meistgenutzte Open-Source-Alternative zu YNAB im Self-Hosting-Bereich.
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. Falls du Docker noch nicht installiert hast, hilft dir die Anleitung Docker und Docker Compose auf Linux installieren weiter. - 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:latest |
| Alternatives Image (GHCR) | ghcr.io/actualbudget/actual:latest |
| Alpine-Variante (~60 MB) | actualbudget/actual-server:latest-alpine |
| Port | 5006 (HTTP, konfigurierbar) |
| Pflicht-Volume | ./actual-data:/data |
| Datenbank | SQLite (eingebettet, kein externer Container) |
| Container-User (UID) | actual (UID 1001) |
| Architekturen | amd64, arm64, arm/v7 (arm/v6 nur Alpine) |
| Passwort beim Setup | Wird beim ersten Browser-Aufruf interaktiv gesetzt |
Schritt 1: Projektordner anlegen und Verzeichnisrechte setzen
Lege einen dedizierten Ordner für Actual Budget an. Alle Konfigurationsdateien und das persistente Datenverzeichnis leben dort:
mkdir -p /opt/actual-budget/actual-data
cd /opt/actual-budgetActual Budget läuft im Container als Benutzer actual mit UID 1001. Das Datenverzeichnis auf dem Host muss für diesen Benutzer schreibbar sein – sonst schlägt der Container beim ersten Schreiben in die SQLite-Datenbank fehl:
chown -R 1001:1001 /opt/actual-budget/actual-dataAuf manchen Systemen (z. B. einem NAS ohne einfachen chown-Zugriff) kannst du alternativ chmod -R 777 /opt/actual-budget/actual-data nutzen, was jedoch weniger restriktiv ist.
Verifizieren: Prüfe Existenz und Besitzer des Ordners:
ls -la /opt/actual-budget/
# Erwartete Ausgabe (Ausschnitt):
# drwxr-xr-x 2 1001 1001 ... actual-dataSchritt 2: compose.yaml erstellen
Erstelle im Projektordner die Datei compose.yaml. Der Stack besteht aus einem einzigen Dienst – kein separater Datenbankcontainer wird benötigt, da SQLite direkt im Image eingebettet ist:
services:
actual_server:
image: docker.io/actualbudget/actual-server:latest
container_name: actual_budget
restart: unless-stopped
ports:
- "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.
# Das Verzeichnis ./actual-data muss für UID 1001 schreibbar sein (siehe Schritt 1).
# Beim ersten Browser-Aufruf erscheint ein Dialog zur Passwortvergabe (Bootstrap).Hinweis zum Image-Tag: latest verweist immer auf den aktuellen stabilen Stand. Für ressourcenschwache Geräte (Raspberry Pi, ältere NAS-Systeme) empfiehlt sich latest-alpine – funktional identisch, aber nur rund 60 MB groß und als einzige Variante auch für arm/v6 verfügbar. Das nightly-Tag enthält den neuesten Entwicklungsstand und sollte nicht in produktiven Umgebungen eingesetzt werden.
Zwei offizielle Registries stehen zur Verfügung: Docker Hub (actualbudget/actual-server) und die GitHub Container Registry (ghcr.io/actualbudget/actual). Beide Images sind inhaltlich identisch; die Anleitung nutzt Docker Hub als primäre Quelle.
Verifizieren: Prüfe die YAML-Syntax, bevor du den Container startest:
docker compose config
# Erwartet: fehlerfreie Ausgabe der Konfiguration ohne WarnungenSchritt 3: Container starten
Starte den Stack im Hintergrund. Docker lädt das Image beim ersten Aufruf automatisch herunter:
docker compose up -dDer Download des Images dauert je nach Verbindung ein bis zwei Minuten (Standard-Image ~150 MB, Alpine ~60 MB).
Verifizieren: Prüfe Status und Gesundheitszustand des Containers:
docker compose ps
# Erwartete Ausgabe:
# NAME IMAGE STATUS
# actual_budget actualbudget/actual-server:latest Up (healthy)
docker compose logs actual_server --tail 20
# Erwartete Ausgabe (Ausschnitt):
# Listening on http://[::]:5006
# Server started successfullyErscheint statt healthy zunächst starting, warte die im Healthcheck definierte start_period von 20 Sekunden ab und führe docker compose ps erneut aus.
Schritt 4: Erst-Einrichtung im Browser
Öffne in einem Browser auf dem Server-Host oder im lokalen Netzwerk:
http://localhost:5006
# oder vom LAN aus:
http://<SERVER-IP>:5006Beim allerersten Aufruf zeigt Actual Budget einen Bootstrap-Dialog, in dem du ein Passwort für den Server vergibst. Dieses Passwort schützt alle Budgetdateien auf dem Server. Es gibt kein vorkonfiguriertes Standardpasswort – das Setzen dieses Passworts ist ein Pflichtschritt.
Nach dem Setzen des Passworts kannst du entweder ein neues Budget anlegen oder eine vorhandene Actual-Budget-Datei importieren. Die Benutzeroberfläche ist vollständig in Englisch; eine offizielle deutsche Lokalisierung existiert zum Zeitpunkt dieser Anleitung nicht, die Bedienung ist jedoch auch ohne Vorkenntnisse intuitiv.
Wichtig: Wenn du das Passwort vergessen hast oder einen Neustart des Bootstrap-Prozesses benötigst, lösche ausschließlich die Datei /opt/actual-budget/actual-data/server-files/account.sqlite – die Budget-Dateien in user-files/ bleiben dabei erhalten.
Verifizieren: Teste die HTTP-Erreichbarkeit per curl:
curl -I http://localhost:5006
# Erwartete Ausgabe:
# HTTP/1.1 200 OK (oder 302 Found mit Location-Header zum Login)Schritt 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 |
Ein vollständiges Backup ist denkbar einfach – der gesamte actual-data-Ordner enthält alle relevanten Daten:
tar czf actual-backup-$(date +%Y%m%d).tar.gz /opt/actual-budget/actual-data
# Ergebnis: actual-backup-20260612.tar.gzFür eine automatisierte Backup-Strategie empfiehlt sich die Anleitung Restic Backup auf Linux einrichten und automatisieren. Zusätzlich bietet das Web-UI unter „Einstellungen" eine integrierte Export-Funktion, die einzelne Budgets als Datei exportiert.
Verifizieren: Prüfe, ob die Unterverzeichnisse nach dem ersten Browser-Aufruf vorhanden sind:
ls -la /opt/actual-budget/actual-data/
# Erwartete Ausgabe:
# drwxr-xr-x ... server-files
# drwxr-xr-x ... user-filesSchritt 6: Umgebungsvariablen und optionale Konfiguration
Actual Budget benötigt für einen Standardbetrieb keine zwingenden Umgebungsvariablen – alle Einstellungen haben sinnvolle Standardwerte. Die folgende Tabelle listet die wichtigsten konfigurierbaren 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 | – | Pfad zum TLS-Schlüssel (direktes HTTPS) |
ACTUAL_HTTPS_CERT | – | Pfad zum TLS-Zertifikat (direktes HTTPS) |
ACTUAL_TRUSTED_PROXIES | RFC-1918-intern | Vertrauenswürdige Proxy-IP-Bereiche |
ACTUAL_UPLOAD_FILE_SIZE_LIMIT_MB | – | Maximale Upload-Dateigröße in MB |
Hinweis zu SECRET_KEY: Verschiedene inoffizielle Anleitungen erwähnen eine SECRET_KEY-Variable. Die offizielle Dokumentation kennt diese Variable nicht – Actual Budget benötigt für den Grundbetrieb kein separates Secret. Das Serverpasswort wird ausschließlich beim ersten Browser-Aufruf gesetzt.
Wenn du sensible Variablen (z. B. zukünftige OpenID-Credentials) aus der compose.yaml heraushalten möchtest, kannst du sie in eine .env-Datei im selben Ordner auslagern:
# /opt/actual-budget/.env
ACTUAL_PORT=5006
# ACTUAL_LOGIN_METHOD=openidDocker Compose liest .env im Projektverzeichnis automatisch ein. Details zur Compose-Syntax findest du in der Grundlagenanleitung 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 http://[::]:5006"Schritt 7: Updates und Wartung
Da alle Daten im Volume ./actual-data liegen und nicht im Container, ist ein Update ohne Datenverlust möglich. Erstelle vor jedem Update sicherheitshalber ein Backup (siehe Schritt 5):
# Optional: Backup vor dem Update
tar czf actual-backup-pre-update.tar.gz /opt/actual-budget/actual-data
# Update durchführen
docker compose pull
docker compose up -d
# Status nach dem Update prüfen
docker compose psDocker Compose lädt das neue Image herunter, stoppt den laufenden Container, ersetzt ihn und startet ihn neu. Die Ausfallzeit beträgt typischerweise wenige Sekunden.
Zum Stoppen des Dienstes (z. B. für Wartungsarbeiten):
docker compose down
# Container wird gestoppt und entfernt; Volume ./actual-data bleibt erhaltenVerifizieren: Nach dem Update prüfen, ob der Container mit dem neuen Image läuft:
docker compose ps
docker inspect actual_budget | grep -i image
# Erwartet: neues Image-Digest in der AusgabeTroubleshooting / Typische Fehler
- EACCES: permission denied, open '/data/server-files/account.sqlite' – Das Volume-Verzeichnis ist nicht für UID 1001 schreibbar. Lösung:
chown -R 1001:1001 /opt/actual-budget/actual-dataausführen und den Container mitdocker compose restartneu starten. - Bind for 0.0.0.0: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."5007:5006", unddocker compose up -derneut ausführen. - exec format error beim Container-Start auf Raspberry Pi (arm/v6) – Das
latest-Tag unterstützt arm/v6 nicht. Lösung: Image-Tag in dercompose.yamlauflatest-alpineändern; nur Alpine-Images unterstützen arm/v6. - Bootstrap-Dialog erscheint nicht mehr, Passwort vergessen – Die Datei
/opt/actual-budget/actual-data/server-files/account.sqlitelöschen unddocker compose restartausführen. Budget-Dateien inuser-files/bleiben dabei erhalten. - Alle Daten nach Container-Neustart verschwunden – Das Volume wurde nicht korrekt eingebunden. Prüfe, 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. Zusätzlich muss Port 5006 in der Host-Firewall für das lokale Netzwerk freigegeben sein. - Container bleibt im Status „starting" und wird nicht „healthy" – Der Healthcheck benötigt bis zu 20 Sekunden Anlaufzeit. Prüfe mit
docker compose logs actual_server, ob Fehlermeldungen erscheinen. Häufige Ursache: fehlerhafte Volume-Rechte. - 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
Brauche ich PostgreSQL oder MySQL für Actual Budget?
Nein. Actual Budget nutzt SQLite als eingebettete Datenbank direkt im Container. Es ist kein zweiter Datenbankcontainer notwendig – das unterscheidet den Stack angenehm von vielen anderen Self-Hosting-Projekten und hält die Komplexität niedrig.
Welches Image-Tag ist für den Dauerbetrieb empfohlen?
Für stabile Produktivsysteme empfehle ich latest oder – besonders auf schwächerer Hardware und ARM-Geräten – latest-alpine. Das Alpine-Image ist funktional identisch, aber deutlich kleiner (~60 MB statt ~150 MB) und unterstützt zusätzlich arm/v6. Das nightly-Tag enthält den neuesten Entwicklungsstand und eignet sich ausschließlich zum Testen neuer Funktionen.
Wie verbinde ich die mobile App mit meinem Server?
Öffne die offizielle Actual-App (iOS oder Android) und wähle beim Budget-Hinzufügen „Existing server". Gib als Server-URL die vollständige Adresse deines Hosts ein, z. B. http://192.168.1.100:5006 im LAN oder https://budget.meinedomain.de hinter einem Reverse Proxy. Anschließend das beim Bootstrap gesetzte Passwort eingeben.
Wie richte ich HTTPS ohne Reverse Proxy ein?
Actual Budget kann TLS direkt aktivieren: Lege die Zertifikatsdateien in einem Unterordner von ./actual-data/certs/ ab und setze 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 Dokumentation empfiehlt das Docker Hub-Image; für Umgebungen mit strikten Zugriffsregeln auf externe Registries kann das GHCR-Image als Alternative dienen.
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 ist einer der seltenen Self-Hosting-Stacks, bei denen die Einfachheit wirklich durchgängig ist: ein Container, ein Volume, kein externer Datenbankdienst. Wer nach dem Umschlag-Prinzip budgetiert und dabei keine Finanzdaten in fremde Hände geben möchte, bekommt hier eine ausgereifte, aktiv weiterentwickelte Lösung mit breiter Community. Der Einstieg gelingt in unter zehn Minuten, die laufenden Kosten beschränken sich auf Strom und Speicherplatz. Wer den Stack langfristig betreiben möchte, sollte den Schritt zu HTTPS und einem automatisierten Backup-Prozess nicht aufschieben.
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