Repository navigation
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.
-
Docker mit Compose v2 — der Befehl heißt
docker compose(mit Leerzeichen), nichtdocker-compose. Docker Desktop bringt beides mit; unter Linuxdockerunddocker-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_PORTeinstellen - 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“
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.examplecp .env.example .envZwei 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.
docker compose up -dBeim 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.
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.
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:roFehlt eine Datei oder ist sie leer, gibt es weder die Seite noch den Link.
docker compose psDie Datenbank sollte healthy melden, die anderen beiden Up. Wenn nicht:
docker compose logs backenddocker compose pull
docker compose up -dVorher 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.
Das ist eine Finanz-App. Mach ein Backup, bevor du es brauchst.
docker compose exec db pg_dump -U duofy duofy > duofy-$(date +%F).sqlZurückspielen:
cat duofy-2026-08-05.sql | docker compose exec -T db psql -U duofy duofyEin Abzug ist besser, als das Volume zu kopieren: der Abzug ist in sich stimmig, auch wenn währenddessen jemand etwas einträgt.
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.
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.
Wer an Duofy arbeiten will, braucht diesen Weg nicht — der lokale Aufbau mit
uv und npm steht in den
Programmierrichtlinien.