Zum Hauptinhalt springen
S-EDV news
← Alle Anleitungen
📘 Anleitung Docker 16.10.2026 · 10 min Lesezeit

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

Titelbild: Ghostfolio selbst hosten mit Docker Compose, PostgreSQL und Redis sowie Backup und Restore

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/arm64 und linux/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

PunktWert
Imagedocker.io/ghostfolio/ghostfolio:3.86.0 (auch :3 und :latest)
Port3333 (HTTP, Weboberfläche und API)
Volumespostgres für /var/lib/postgresql/data
Pflicht-VariablenDATABASE_URL, REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, ACCESS_TOKEN_SALT, JWT_SECRET_KEY, POSTGRES_*
DatenbankPostgreSQL 15 (Alpine), Cache Redis
LizenzAGPL-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.

Ghostfolio-Administration mit Version 3.86.0, einem Benutzer und ausgeschalteter Benutzerregistrierung
Administration in Ghostfolio 3.86.0: Der Schalter Benutzer Registrierung ist nach dem ersten Konto ausgeschaltet.

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.

Kontenliste in Ghostfolio mit dem Testkonto Restore-Marker Depot und 1.234,56 Euro
Kontenliste mit dem Testkonto Restore-Marker Depot, das 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.

Startseite von Ghostfolio mit den Einstiegsschritten Konten einrichten, Aktivitäten erfassen und Portfolio überwachen
Die Übersicht führt neue Benutzer durch Konten, Aktivitäten und Auswertung.

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

GhostfolioPortfolioFinanzenDocker ComposePostgreSQLRedisSelfhostingBackup