Sicherung und Wiederherstellung
Alles Schützenswerte liegt in PostgreSQL: Zeitbuchungen, Zeitkonten, Abwesenheiten, Verrechnungssätze, Prüfprotokoll. Ein Objektspeicher ist zwar vorgesehen, wird von der Anwendung aber nicht benutzt — es gibt keinen Datei-Upload. Eine Datenbanksicherung ist damit vollständig.
Sicherung aus dem Browser
Unter Einstellungen → Sicherung kann die Administration (Recht *) den gesamten Bestand des eigenen Betriebs als Datei herunterladen (worktime-sicherung-<betrieb>-<datum>.json.gz) und eine solche Datei dort auch wieder einspielen.
- Umfang: alle Tabellen des Mandanten — Mitarbeiter, Benutzer, Rollen, Zeiten, Projekte, Abwesenheiten, Einstellungen … Nicht enthalten: Anmeldesitzungen, das Prüfprotokoll und die Abrechnung des Abonnements.
- Einspielen ersetzt den gesamten aktuellen Bestand durch den Stand der Datei, in einer einzigen Transaktion. Scheitert etwas, bleibt alles, wie es war. Zur Bestätigung muss
EINSPIELENeingetippt werden. - Das Prüfprotokoll bleibt unverändert; das Einspielen wird darin festgehalten. Benutzer, die es zur Zeit der Sicherung noch nicht gab und die im Protokoll vorkommen, werden stillgelegt statt gelöscht.
- Geprüft wird vor dem Einspielen: Prüfsumme (veränderte oder beschädigte Datei), Betrieb (eine Sicherung eines anderen Betriebs wird abgewiesen) und Programmfassung (eine Sicherung aus einer neueren Fassung wird abgewiesen; ältere gehen).
- Nach dem Einspielen bitte neu anmelden.
Diese Sicherung ersetzt nicht die Sicherung der ganzen Datenbank auf dem Server (unten) — sie ist der schnelle Weg für den Betrieb selbst, ohne Zugang zum Server.
Sichern
docker compose exec -T postgres pg_dump -U worktime -Fc worktime > worktime.dump
Außerhalb von Docker liegt im Repositorium ein Skript unter packages/database/scripts/sichern.sh, das denselben Weg geht und die Stolpersteine unten abfängt.
Wer sichert, braucht BYPASSRLS
Die Mandantentrennung steht als FORCE ROW LEVEL SECURITY in der Datenbank, und „FORCE“ gilt auch für den Eigentümer der Tabellen. Ein schlichtes pg_dump mit dem Anwendungs- oder Eigentümerkonto bricht ab:
ERROR: query would be affected by row-level security policy
Im Docker-Aufbau ist der voreingestellte Benutzer bereits Superuser. Sonst einmalig eine eigene Rolle nur fürs Sichern anlegen; das Skript druckt die nötigen Befehle, wenn es darüber stolpert.
Nicht mit --enable-row-security behelfen
pg_dump schreibt dann nur, was die Richtlinien der eigenen Rolle sehen lassen — und meldet Erfolg.
Die Datei ist dann kaum kleiner als eine vollständige, enthält aber keine einzige Zeitbuchung — und das fiele erst am Tag der Wiederherstellung auf.
Was nicht in der Sicherung ist
- Die
.envmitJWT_SECRETundLICENSE_KEY. Getrennt aufbewahren. Ohne das alteJWT_SECRETsind nach dem Zurückspielen lediglich alle Sitzungen ungültig — anmelden kann sich danach jeder wieder. - Der Anwendungsbenutzer. Rollen gelten für den ganzen Datenbank-Cluster und gehören nicht in die Sicherung einer einzelnen Datenbank. Ohne ihn verbindet sich die Anwendung als Eigentümer der Tabellen, und dann greift die Row-Level-Security nicht.
Was enthalten ist
Schema, alle Daten, die Row-Level-Security samt app_current_tenant(), die Trigger für die Unveränderlichkeit des Prüfprotokolls und die Erweiterungen (citext, pg_trgm, pgcrypto, btree_gist).
Zurückspielen
Nur in eine leere Datenbank:
createdb worktime_wiederhergestellt
APP_DB_PASSWORD='…' \
DATABASE_URL_MIGRATIONS="postgresql://…/worktime_wiederhergestellt" \
sh packages/database/scripts/zurueckspielen.sh worktime-20260830-120000.dump
Das Skript richtet den Anwendungsbenutzer wieder ein, sofern APP_DB_PASSWORD gesetzt ist.
Eine Sicherung, die nie zurückgespielt wurde, ist eine Vermutung
Nach dem Zurückspielen gehören vier Proben dazu: Startet die Anwendung gegen den wiederhergestellten Stand? Nennt der Zeitbericht dieselben Summen? Meldet die Hash-Kette des Prüfprotokolls {"valid":true}? Und bleibt mit einem fremden Mandantenkontext jede Zeile unsichtbar?
Diese vier Proben stehen am Ende von zurueckspielen.sh. Spielen Sie den Weg einmal probeweise durch, bevor Sie ihn im Ernstfall brauchen.
Wie lange
§16 Abs. 2 ArbZG verlangt, die Arbeitszeitnachweise zwei Jahre aufzubewahren. §257 HGB und §147 AO kennen für lohnrelevante Unterlagen sechs und zehn Jahre.
Eine Sicherung, die nur auf demselben Rechner liegt, überlebt dessen Ausfall nicht.
Nächtliche Sicherung des ganzen Servers
scripts/sicherung-server.sh sichert jede Nacht die Datenbank aller Betriebe, den Lizenzserver (Lizenzdatenbank und Signaturschlüssel) und die .env. Die Sicherungen liegen 14 Tage unter /var/backups/worktime/, auf Wunsch zusätzlich auf einem zweiten Speicher: SICHERUNG_ZIEL=benutzer@host:pfad für rsync oder SICHERUNG_HOCHLADEN mit einem eigenen Befehl, etwa für rclone ({} steht für die Sicherungsdatei).
cp /opt/worktime/scripts/sicherung-server.sh /usr/local/bin/worktime-sicherung
chmod 700 /usr/local/bin/worktime-sicherung
echo '30 2 * * * root /usr/local/bin/worktime-sicherung >> /var/log/worktime-sicherung.log 2>&1' \
> /etc/cron.d/worktime-sicherung
Zurückspielen der Datenbank (überschreibt den aktuellen Stand):
docker compose -p worktime -f docker-compose.selfhost.yml stop api web
docker exec -i worktime-postgres pg_restore -U worktime -d worktime --clean --if-exists \
< /var/backups/worktime/<DATUM>/worktime.dump
docker compose -p worktime -f docker-compose.selfhost.yml start api web