Ghostfolio mit Docker Compose selbst hosten: Vermögen und Depots im Blick
Ghostfolio ist ein quelloffener Portfolio-Tracker für Aktien, ETFs, Kryptowerte und Konten. Die Anleitung zeigt den getesteten Betrieb von Version 3.86.0 mit Docker Compose, PostgreSQL 15 und Redis, die Anmeldung per Security Token, das Sperren der Registrierung, Backup mit pg_dump samt erprobtem Restore nach Totalverlust, Updates und typische Fehlerbilder.
Mit KI erstellt – redaktionelle Prüfung ausstehend
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

Ghostfolio ist ein quelloffenes Werkzeug, mit dem Sie Aktien, ETFs, Kryptowerte und Bankkonten an einer Stelle zusammenführen und die Entwicklung Ihres Vermögens auswerten. Statt Depotdaten einem Cloud-Anbieter anzuvertrauen, betreiben Sie die Anwendung selbst: ein Node.js-Dienst mit Angular-Oberfläche, PostgreSQL als Datenbank und Redis als Cache. Diese Anleitung führt Sie durch Installation mit Docker Compose, Absicherung der Registrierung, Backup samt echtem Restore, Updates und typische Fehlerbilder. Alle Befehle und Ausgaben stammen aus einem Testlauf mit Ghostfolio 3.86.0.
Voraussetzungen
Ghostfolio ist für einen einzelnen Haushalt oder ein kleines Team genügsam. Im Test belegte der App-Container nach dem Start rund 430 MB RAM, PostgreSQL etwa 36 MB und Redis etwa 27 MB.
- Linux-Server oder VM mit 2 CPU-Kernen und 2 GB RAM (4 GB, wenn weitere Dienste mitlaufen), x86_64 oder ARM64; Images gibt es für
linux/amd64,linux/arm64undlinux/arm/v7 - Rund 3 GB freier Speicher für Images und Datenbank, mit Kursdaten wächst die Datenbank langsam
- Docker Engine mit Compose-Plugin v2
- Für den Zugriff von außen eine Subdomain und ein Reverse Proxy mit TLS, etwa Caddy, Traefik oder Nginx Proxy Manager
- Ein Speicherziel außerhalb des Servers für die Datenbank-Dumps
Eckdaten im Überblick
| Punkt | Wert |
|---|---|
| Image | docker.io/ghostfolio/ghostfolio:3.86.0 (auch :3 und :latest) |
| Port | 3333 (HTTP, Weboberfläche und API) |
| Volumes | postgres für /var/lib/postgresql/data |
| Pflicht-Variablen | DATABASE_URL, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, ACCESS_TOKEN_SALT, JWT_SECRET_KEY, POSTGRES_* |
| Datenbank | PostgreSQL 15 (Alpine), Cache Redis |
| Lizenz | AGPL-3.0 |
Schritt 1: Projektordner und Compose-Datei anlegen
Die Vorlage stammt aus docker/docker-compose.yml im offiziellen Repository. Zwei Anpassungen sind sinnvoll: Das Image ist auf 3.86.0 festgeschrieben, damit ein späteres pull keine ungeplante Version holt, und die festen container_name-Einträge entfallen, damit sich Stack und Container klar über den Ordnernamen zuordnen lassen. Die .env liegt im selben Ordner statt eine Ebene höher. Die Härtung aus der Vorlage bleibt: Alle Linux-Capabilities sind entzogen, PostgreSQL erhält nur die fünf, die es zum Start braucht.
mkdir -p /opt/ghostfolio && cd /opt/ghostfolio
Legen Sie dort die Datei compose.yaml an:
services:
ghostfolio:
image: docker.io/ghostfolio/ghostfolio:3.86.0
restart: unless-stopped
init: true
stop_grace_period: 1m
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
env_file:
- .env
ports:
- 3333:3333
depends_on:
postgres:
condition: service_healthy
redis:
condition: service_healthy
healthcheck:
test: ['CMD-SHELL', 'curl -f http://localhost:3333/api/v1/health']
interval: 10s
timeout: 5s
retries: 5
postgres:
image: docker.io/library/postgres:15-alpine
restart: unless-stopped
cap_drop:
- ALL
cap_add:
- CHOWN
- DAC_READ_SEARCH
- FOWNER
- SETGID
- SETUID
security_opt:
- no-new-privileges:true
env_file:
- .env
healthcheck:
test: ['CMD-SHELL', 'pg_isready -d "$${POSTGRES_DB}" -U $${POSTGRES_USER}']
interval: 10s
timeout: 5s
retries: 5
volumes:
- postgres:/var/lib/postgresql/data
redis:
image: docker.io/library/redis:alpine
restart: unless-stopped
user: '999:1000'
cap_drop:
- ALL
security_opt:
- no-new-privileges:true
env_file:
- .env
command:
- /bin/sh
- -c
- redis-server --requirepass "$${REDIS_PASSWORD:?REDIS_PASSWORD variable is not set}"
healthcheck:
test: ['CMD-SHELL', 'redis-cli --pass "$${REDIS_PASSWORD}" ping | grep PONG']
interval: 10s
timeout: 5s
retries: 5
volumes:
postgres:
Verifizieren: docker compose config --quiet gibt nichts aus, wenn die Datei gültig ist. Eine Fehlermeldung zu REDIS_PASSWORD an dieser Stelle ist normal, solange die .env noch fehlt.
Schritt 2: Geheimnisse in der .env setzen
Ghostfolio benötigt vier Zufallswerte: zwei Passwörter für PostgreSQL und Redis sowie zwei Schlüssel. ACCESS_TOKEN_SALT fließt in die Security Tokens ein, mit denen sich Benutzer anmelden; JWT_SECRET_KEY signiert die Sitzungen. Ändern Sie diese beiden Werte später, sind alle bestehenden Anmeldungen ungültig. Erzeugen Sie die Werte mit openssl rand -hex 32 und tragen Sie sie ein:
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=HIER_ZUFALLSWERT_1
POSTGRES_DB=ghostfolio-db
POSTGRES_USER=user
POSTGRES_PASSWORD=HIER_ZUFALLSWERT_2
ACCESS_TOKEN_SALT=HIER_ZUFALLSWERT_3
JWT_SECRET_KEY=HIER_ZUFALLSWERT_4
DATABASE_URL=postgresql://user:HIER_ZUFALLSWERT_2@postgres:5432/ghostfolio-db?connect_timeout=300
ROOT_URL=https://finanzen.example.de
Das PostgreSQL-Passwort steht zweimal in der Datei, einmal einzeln und einmal in DATABASE_URL. Hex-Werte vermeiden Sonderzeichen, die in der URL maskiert werden müssten. ROOT_URL ist optional und wird für Links und Rückruf-Adressen genutzt; tragen Sie die spätere HTTPS-Adresse ein. Schützen Sie die Datei mit chmod 600 .env.
Verifizieren: docker compose config | grep -c HIER_ muss 0 liefern, also kein Platzhalter mehr übrig sein.
Schritt 3: Stack starten und Migrationen abwarten
docker compose up -d
docker compose ps
Compose startet zuerst PostgreSQL und Redis und wartet, bis beide Healthchecks grün sind. Erst dann folgt der App-Container. Das Einstiegsskript führt npx prisma migrate deploy aus; im Test meldete das Log 129 migrations found in prisma/migrations, danach erschien das Banner mit der Versionsnummer 3.86.0 und Listening at http://0.0.0.0:3333.
docker compose logs --no-log-prefix ghostfolio | grep -E 'migrations found|Listening'
curl -s http://localhost:3333/api/v1/health
Verifizieren: Alle drei Dienste stehen in docker compose ps auf healthy, und der Health-Endpunkt antwortet mit {"status":"OK"}.
Schritt 4: Erstes Konto anlegen und Registrierung sperren
Öffnen Sie http://SERVER:3333 und klicken Sie auf Get Started. Ghostfolio vergibt keine Benutzernamen und Passwörter, sondern erzeugt einen Security Token. Speichern Sie ihn sofort im Passwortmanager, denn ohne ihn kommen Sie nicht mehr in das Konto. Das erste Konto erhält die Rolle ADMIN, im Test bestätigt durch die API-Antwort role=ADMIN.
Wichtig für jede erreichbare Instanz: Die Registrierung bleibt nach dem ersten Konto offen. Schalten Sie sie unter Administration, Übersicht mit dem Schalter Benutzer Registrierung ab. Im Test lieferte ein weiterer Registrierungsversuch danach 403 Forbidden.

Unter dem Avatar finden Sie die Kontoeinstellungen mit Sprache, Basiswährung und Datumsformat. Stellen Sie die Basiswährung auf EUR, sonst rechnet Ghostfolio Summen in US-Dollar. Ein Login per Token lässt sich auch per API prüfen, etwa für ein Monitoring:
curl -s -o /dev/null -w "%{http_code}\n" -X POST http://localhost:3333/api/v1/auth/anonymous \
-H 'Content-Type: application/json' -d '{"accessToken":"falsch"}'
# Ausgabe: 403
Verifizieren: Die Administrationsseite zeigt Version 3.86.0 und einen Benutzer; der Schalter Benutzer Registrierung steht auf aus. Ein falscher Token liefert 403, ein Aufruf von /api/v1/account ohne Anmeldung 401.
Schritt 5: Konten und Aktivitäten erfassen
Unter Konten legen Sie Bank- und Depotkonten mit Währung und Cash-Bestand an, unter Portfolio, Aktivitäten erfassen Sie Käufe, Verkäufe und Dividenden. Für den Test wurde ein Konto Restore-Marker Depot mit 1.234,56 EUR angelegt, das später als Nachweis für den Restore dient.

Kurse holt Ghostfolio von externen Datenquellen. Für Kryptowerte unterstützt die Anwendung optionale CoinGecko-Schlüssel (API_KEY_COINGECKO_DEMO oder API_KEY_COINGECKO_PRO). Ohne Internetzugang des Containers bleiben Kurse leer, Ihre eigenen Buchungen funktionieren trotzdem. Der Schalter Finanzmarktdaten synchronisieren in der Administration steuert den regelmäßigen Abgleich.

Verifizieren: Die Kontenliste zeigt das neue Konto mit korrektem Bestand und die Gesamtsumme in Ihrer Basiswährung.
Schritt 6: Hinter einen Reverse Proxy stellen
Port 3333 spricht nur HTTP. Für den Betrieb außerhalb des eigenen Netzes gehört ein Reverse Proxy mit TLS davor. Binden Sie den Port dann nur noch lokal, damit niemand den Proxy umgeht:
ports:
- 127.0.0.1:3333:3333
Eine minimale Caddy-Konfiguration lautet finanzen.example.de { reverse_proxy 127.0.0.1:3333 }. Setzen Sie zusätzlich TRUST_PROXY=1 in der .env, damit Ghostfolio die echte Client-Adresse für die Ratenbegrenzung der Anmeldung verwendet. Optional aktiviert ENABLE_FEATURE_SECURITY_HEADERS=true Header wie Content-Security-Policy; Strict-Transport-Security setzt Ghostfolio bewusst nicht, das übernimmt der Proxy.
Verifizieren: curl -I https://finanzen.example.de liefert 200, und ss -tlnp | grep 3333 zeigt nur noch 127.0.0.1.
Schritt 7: Backup und Restore der Datenbank
Alle Konten, Aktivitäten, Benutzer und Einstellungen liegen in PostgreSQL. Redis hält nur zwischengespeicherte Kurse und Warteschlangen und muss nicht gesichert werden. Sichern Sie zusätzlich die .env, denn ohne ACCESS_TOKEN_SALT passen die gespeicherten Security Tokens nicht mehr.
mkdir -p /opt/ghostfolio/backup
docker compose exec -T postgres sh -c 'pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc' \
> /opt/ghostfolio/backup/ghostfolio-$(date +%F).dump
Das Format -Fc ist komprimiert und lässt sich mit pg_restore gezielt einspielen; im Test war der Dump einer frischen Instanz rund 65 KB groß. Für einen Cronjob genügt dieselbe Zeile, ergänzt um eine Rotation und die Übertragung auf ein externes Ziel.
Ein Backup ist erst etwas wert, wenn der Restore funktioniert. So lief der Test mit vollständigem Datenverlust: App stoppen, Datenbank löschen und leer neu anlegen, Dump einspielen.
docker compose stop ghostfolio
docker compose exec -T postgres sh -c 'dropdb -U "$POSTGRES_USER" "$POSTGRES_DB" && createdb -U "$POSTGRES_USER" "$POSTGRES_DB"'
docker compose exec -T postgres sh -c 'pg_restore -U "$POSTGRES_USER" -d "$POSTGRES_DB" --exit-on-error --no-owner' \
< /opt/ghostfolio/backup/ghostfolio-2026-10-16.dump
docker compose start ghostfolio
Stoppen Sie die App vorher, sonst blockieren offene Verbindungen das dropdb. Nach dem Löschen enthielt die Datenbank im Test 0 Tabellen, nach dem Restore wieder 22 Tabellen mit 129 eingetragenen Migrationen. pg_restore endete mit Exit-Code 0, der Health-Endpunkt antwortete zehn Sekunden nach dem Start mit 200.
Verifizieren: Fragen Sie den Testdatensatz direkt ab:
docker compose exec -T postgres psql -U user -d ghostfolio-db -tAc \
"select a.name, b.value from \"Account\" a join \"AccountBalance\" b on b.\"accountId\"=a.id"
# Restore-Marker Depot|1234.56
Im Test erschien das Konto Restore-Marker Depot mit 1234.56 wieder, in der Datenbank wie in der Oberfläche, und der alte Sitzungstoken funktionierte weiter, weil die .env unverändert blieb.
Schritt 8: Updates einspielen
Ghostfolio erscheint sehr häufig, im Oktober 2026 lagen zwischen 3.83.0 und 3.86.0 nur drei Tage. Prüfen Sie vor jedem Sprung die Release Notes und legen Sie einen frischen Dump an. Danach erhöhen Sie den Tag in der compose.yaml und starten neu:
docker compose pull ghostfolio
docker compose up -d ghostfolio
docker compose logs --no-log-prefix ghostfolio | grep -i migration
Neue Datenbankmigrationen spielt der Container beim Start selbst ein. Gibt es keine, meldet das Log No pending migrations to apply., so auch im Test nach einem erneuten Start mit unverändertem Image. Für einen Rückweg auf die alte Version spielen Sie den vorher angelegten Dump ein. PostgreSQL bleibt auf Version 15; ein Wechsel der Hauptversion erfordert Dump und Restore in ein neues Volume.
Verifizieren: Die Administrationsseite zeigt die neue Versionsnummer, docker compose ps meldet healthy.
Troubleshooting
Health-Endpunkt antwortet nicht, Log zeigt SocketClosedUnexpectedlyError
Ohne erreichbares Redis ist Ghostfolio nicht bedienbar. Im Test führte ein gestoppter Redis-Container dazu, dass curl auf den Health-Endpunkt keine Antwort bekam (000) und das Log ERROR [RedisCacheService] SocketClosedUnexpectedlyError: Socket closed unexpectedly schrieb. Nach docker compose start redis war die Instanz ohne Neustart der App wieder da. Prüfen Sie außerdem, ob REDIS_PASSWORD in App und Redis identisch ist.
App startet nicht, PostgreSQL verweigert die Anmeldung
Meist stimmt das Passwort in DATABASE_URL nicht mit POSTGRES_PASSWORD überein, oder Sie haben das Passwort nach dem ersten Start geändert. PostgreSQL übernimmt POSTGRES_PASSWORD nur beim Anlegen des Volumes. Ändern Sie das Passwort dann per ALTER USER in der Datenbank.
Security Token verloren
Ohne Token ist das Konto nicht mehr erreichbar, eine Wiederherstellung per E-Mail gibt es nicht. Bewahren Sie den Token deshalb im Passwortmanager auf und legen Sie bei Bedarf ein zweites Administratorkonto an, bevor Sie die Registrierung sperren.
Registrierung liefert 403
Das ist das erwartete Verhalten, wenn Sie die Registrierung gesperrt haben. Für neue Benutzer schalten Sie sie kurz ein und danach wieder aus.
Häufige Fragen
Brauche ich Redis wirklich?
Ja. Ghostfolio nutzt Redis für Cache und Hintergrundjobs und ist ohne Verbindung nicht erreichbar. Die Daten darin sind aber flüchtig und gehören nicht ins Backup.
Kann ich mich mit Benutzername und Passwort anmelden?
Standard ist der Security Token. Optional unterstützt Ghostfolio OpenID Connect über ENABLE_FEATURE_AUTH_OIDC und die OIDC_*-Variablen, etwa mit Authentik oder Keycloak; der Hersteller kennzeichnet das als experimentell.
Werden meine Depotdaten an Dritte übertragen?
Ihre Buchungen bleiben in Ihrer Datenbank. Für Kurse fragt der Server allerdings externe Datenanbieter nach den Symbolen Ihrer Positionen ab.
Darf ich Ghostfolio im Unternehmen einsetzen?
Die AGPL-3.0 erlaubt den internen Betrieb. Wenn Sie den Code verändern und Dritten über das Netz bereitstellen, müssen Sie den geänderten Quelltext zugänglich machen.
Fazit
Ghostfolio lässt sich mit drei Containern in wenigen Minuten betreiben und läuft auf kleiner Hardware. Die wichtigsten Handgriffe nach der Installation sind die gesperrte Registrierung, ein sicher verwahrter Security Token und ein regelmäßiger pg_dump zusammen mit der .env. Wer den Restore einmal so durchspielt wie hier gezeigt, kann die häufigen Updates gelassen einspielen.
Weiterführende Anleitungen und Quellen
- Firefly III mit Docker: Finanzverwaltung selbst hosten
- Actual Budget mit Docker als Haushaltsbuch
- Caddy als Reverse Proxy mit automatischem HTTPS
- Container-Updates überwachen mit Diun und WUD
- Ghostfolio auf GitHub (README mit allen Umgebungsvariablen)
- Offizielle Compose-Vorlage
- Release 3.86.0
- Ghostfolio FAQ zum Selbsthosting


