Skip to content

Installation

tom-schorn edited this page Sep 26, 2026 · 4 revisions

Installation

Duofy läuft in drei Containern: Oberfläche, Backend und Datenbank. Du brauchst weder Python noch Node noch eine eigene Postgres-Installation — das steckt alles in den Abbildern.

Zusammen belegen sie im Leerlauf etwa 100 MB Arbeitsspeicher, die Obergrenze liegt bei 256 MB. Das läuft auf jedem kleinen Server und auf einem Raspberry Pi.


Was du brauchst

  • Docker mit Compose v2 — der Befehl heißt docker compose (mit Leerzeichen), nicht docker-compose. Docker Desktop bringt beides mit; unter Linux docker und docker-compose-plugin
  • Arbeitsspeicher: etwa 256 MB frei. Duofy belegt im Leerlauf rund 100 MB, die drei Container sind auf zusammen 256 MB begrenzt (Datenbank 128, Backend 96, Oberfläche 32)
  • Einen freien Port, Vorgabe 8080; ein anderer lässt sich mit DUOFY_PORT einstellen
  • Plattenplatz für die Abbilder (einige hundert MB) und die Datenbank, die für einen Haushalt klein bleibt
  • Zwei Dateien aus dem Repo, siehe unten
  • Nur wenn Duofy von außen erreichbar sein soll: eine eigene Domain und einen Reverse Proxy für HTTPS, siehe „Hinter der eigenen Domain“

1. Die zwei Dateien holen

mkdir duofy && cd duofy
curl -O https://raw.githubusercontent.com/tom-schorn/Duofy/main/docker-compose.yml
curl -O https://raw.githubusercontent.com/tom-schorn/Duofy/main/.env.example

2. Die eigene .env anlegen

cp .env.example .env

Zwei Werte sind Pflicht, der Rest hat Vorgaben, die für die meisten passen:

Wert Was hineingehört
POSTGRES_PASSWORD frei wählbar, aber nicht leer
JWT_SECRET mit openssl rand -hex 32 erzeugen, nicht ausdenken
REFRESH_LIFETIME_SECONDS wie lange man angemeldet bleibt, Vorgabe 30 Tage
COOKIE_SECURE false nur, wenn Duofy über einfaches http läuft
DUOFY_TAG leer lassen für die neueste Version
DUOFY_PORT unter welchem Port Duofy erreichbar sein soll, Standard 8080
REGISTRATION_MODE wer sich registrieren darf: invite (Vorgabe), open oder closed
ADMIN_EMAIL die Adresse des ersten Admins, siehe „Wer sich registrieren darf“
IMPRINT_FILE, PRIVACY_FILE, TERMS_FILE Impressum, Datenschutz, AGB der Instanz, siehe „Rechtstexte“

Zum JWT_SECRET: damit werden die Anmelde-Token unterschrieben. Wer es kennt, kann sich als jeder beliebige Nutzer ausgeben. Es gehört in die .env und nirgendwo sonst hin.

Zum Angemeldetbleiben: die 30 Tage zählen ab dem letzten Besuch, nicht ab der Anmeldung. Jeder Aufruf schiebt das Ende nach hinten — wer Duofy regelmäßig benutzt, meldet sich nie wieder an, eine vergessene Sitzung läuft ab. Abmelden beendet sie sofort und nicht erst, wenn sie ohnehin abgelaufen wäre.

Läuft Duofy bei dir über einfaches http, etwa nur im Heimnetz, dann COOKIE_SECURE=false setzen — Browser verwerfen sichere Cookies auf http-Adressen, und ohne Cookie bleibt niemand angemeldet.

Zu DUOFY_TAG: wer selbst hostet, fährt mit einer festen Version am ruhigsten — DUOFY_TAG=1.2.0. Dann ändert sich nichts unter dir, solange du nicht selbst ziehst. Wer mitlesen will, was gerade entsteht, kann dev eintragen; das ist der Stand des Hauptzweigs main und darf kaputt sein.


3. Starten

docker compose up -d

Beim ersten Mal dauert es einen Moment: die Abbilder werden geladen, die Datenbank legt sich an, und das Backend richtet seine Tabellen selbst ein.

Danach läuft Duofy auf http://localhost:8080. Registriere dich dort gleich nach dem ersten Start mit der Adresse, die du als ADMIN_EMAIL eingetragen hast: wer diese Adresse zuerst registriert, ist Admin. Wie es weitergeht, steht unter „Wer sich registrieren darf“.

Deinen Haushalt legst du in der Oberfläche an und lädst die anderen über ihre E-Mail-Adresse ein. Es wird keine E-Mail verschickt: wer sich mit der eingeladenen Adresse anmeldet, findet die Einladung unter Haushalt.


Wer sich registrieren darf

REGISTRATION_MODE bestimmt, wer auf dieser Duofy-Instanz ein Konto anlegen darf:

Wert Bedeutung
invite nur mit einer Einladung von einem Admin — die Vorgabe
open jede und jeder
closed niemand, außer der Adresse unter ADMIN_EMAIL

Die Vorgabe ist invite, damit eine frisch aufgesetzte Instanz nicht aus Versehen für alle offen steht. Wer offen will, stellt es bewusst ein.

Der Admin. Wer sich mit der ADMIN_EMAIL registriert, ist Admin — in jedem Modus, auch bei closed, damit die Instanz nie ohne Admin dasteht. Ein Admin verwaltet die Instanz (die Einladungen); er sieht keine Pläne, Bücher oder Konten anderer. Die Adresse lässt sich später nicht übernehmen, indem jemand seine E-Mail-Adresse ändert. Wer keinen Admin will, lässt ADMIN_EMAIL leer.

Zur Unterscheidung: Ein Haushalt ist etwas anderes. Er entsteht in der Oberfläche, und seine Mitglieder lädst du über die E-Mail-Adresse ein; das hat mit dem Registrierungsmodus der Instanz nichts zu tun.


Rechtstexte

Impressum, Datenschutzerklärung und AGB dieser Instanz gehören dem Betreiber, nicht dem Projekt. Wer eine öffentliche Instanz betreibt, braucht ein Impressum (§ 5 DDG); eine private Familieninstanz meist nicht.

Jeder der drei Werte ist der Pfad einer reinen Textdatei im Backend-Container (kein Markdown-Rendering, Adressen werden nicht zu Links):

IMPRINT_FILE=/config/impressum.txt   # Seite /impressum
PRIVACY_FILE=/config/datenschutz.txt # Seite /datenschutz
TERMS_FILE=/config/agb.txt           # Seite /agb

Die Dateien müssen im Container liegen; das geht mit einem Ordner als Volume, den du in der Compose-Datei beim Backend einträgst:

    volumes:
      - ./config:/config:ro

Fehlt eine Datei oder ist sie leer, gibt es weder die Seite noch den Link.


4. Nachsehen, ob alles steht

docker compose ps

Die Datenbank sollte healthy melden, die anderen beiden Up. Wenn nicht:

docker compose logs backend

Aktualisieren

docker compose pull
docker compose up -d

Vorher einen Blick in den Changelog. Steht dort BREAKING CHANGE, gibt es einen Schritt, den du von Hand tun musst — er steht dann dabei.

Die Daten bleiben beim Update erhalten, sie liegen im Volume und nicht im Container.


Sichern

Das ist eine Finanz-App. Mach ein Backup, bevor du es brauchst.

docker compose exec db pg_dump -U duofy duofy > duofy-$(date +%F).sql

Zurückspielen:

cat duofy-2026-08-05.sql | docker compose exec -T db psql -U duofy duofy

Ein Abzug ist besser, als das Volume zu kopieren: der Abzug ist in sich stimmig, auch wenn währenddessen jemand etwas einträgt.


Hinter der eigenen Domain

Duofy gibt einen Port heraus, mehr nicht. Für HTTPS und einen eigenen Namen stellst du den Reverse Proxy davor, den du ohnehin schon hast. Mit Caddy reichen zwei Zeilen:

duofy.example.org {
    reverse_proxy localhost:8080
}

Nimm HTTPS. Ohne Verschlüsselung geht dein Passwort im Klartext durchs Netz, und die Anmelde-Token gleich mit.

Einen Proxy liefert Duofy bewusst nicht mit — der eine hat Caddy, der nächste nginx proxy manager, Traefik oder einen Cloudflare-Tunnel. Alles mitzuliefern würde mit dem kollidieren, was schon läuft.


Wenn etwas klemmt

manifest unknown beim Start — den Wert in DUOFY_TAG gibt es nicht. Leer lassen oder eine Version aus den Releases eintragen.

Nach einem Update sind alle Daten weg — dann hing das Volume am falschen Pfad. Seit Postgres 18 gehört es an /var/lib/postgresql, nicht mehr an /var/lib/postgresql/data. Die mitgelieferte Compose-Datei macht das richtig; wer sie angepasst hat, sollte dort nachsehen.

Passwort in der .env geändert, nichts passiert — die Postgres-Werte gelten nur beim ersten Start, danach liegt die Datenbank im Volume. Ein Passwort ändert man in der Datenbank selbst.

Port schon belegt — DUOFY_PORT auf etwas anderes setzen, dann docker compose up -d.

Das Backend startet nicht, die Datenbank schon — meist ein leeres JWT_SECRET. docker compose logs backend sagt es.


Für Entwickler

Wer an Duofy arbeiten will, braucht diesen Weg nicht — der lokale Aufbau mit uv und npm steht in den Programmierrichtlinien.

Clone this wiki locally