5.8 KiB
Produktivbetrieb mit Gitea und Docker Compose
Dieses Repository veröffentlicht versionierte Container-Images unter:
git.j-mahler.de/admin/domain-control-webinterface-prod:vX.Y.Z
Das Image enthält ausschließlich die Anwendung. SQLite-Datenbank und Master-Key liegen dauerhaft auf dem Produktionsserver und bleiben bei Image-Updates erhalten.
1. Gitea für Releases vorbereiten
In den Repository-Einstellungen unter Actions → Secrets diese zwei Secrets anlegen:
REGISTRY_USERNAME: Gitea-Benutzer oder eigener Release-BotREGISTRY_TOKEN: persönlicher Gitea-Token mit Schreibrecht für Container-Pakete
Auf der Gitea-Instanz muss ein Actions-Runner mit dem Label ubuntu-latest registriert sein. Jeder Git-Tag, der mit v beginnt, startet anschließend .gitea/workflows/release.yml und baut Images für AMD64 und ARM64.
Erstes Release veröffentlichen:
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
Nach erfolgreicher Action stehen v1.0.0 und der bewegliche Tag stable in der Gitea Container Registry. Auf Produktion immer den unveränderlichen Versionstag verwenden.
Image alternativ manuell veröffentlichen
Falls Gitea Actions noch nicht eingerichtet ist:
echo "$GITEA_TOKEN" | docker login git.j-mahler.de --username DEIN_BENUTZER --password-stdin
docker buildx build \
--platform linux/amd64,linux/arm64 \
-t git.j-mahler.de/admin/domain-control-webinterface-prod:v1.0.0 \
-t git.j-mahler.de/admin/domain-control-webinterface-prod:stable \
--push .
2. Produktionsserver vorbereiten
Voraussetzungen:
- Linux-Server mit Docker Engine und Docker-Compose-Plugin
- HTTPS-Reverse-Proxy wie Caddy, nginx oder Traefik
- DNS-Name, der auf den Server zeigt
Repository installieren:
sudo mkdir -p /opt/domain-control
sudo chown "$USER":"$USER" /opt/domain-control
git clone https://git.j-mahler.de/admin/domain-control-webinterface-prod.git /opt/domain-control
cd /opt/domain-control
cp .env.production.example .env.production
mkdir -p secrets backups
chmod 700 secrets backups
openssl rand -base64 32 > secrets/dyndns_master_key
chmod 644 secrets/dyndns_master_key
Der Master-Key wird vom unprivilegierten Benutzer im Container gelesen. Deshalb ist die Datei lesbar, während das übergeordnete Verzeichnis auf dem Host nur für seinen Besitzer zugänglich bleibt. Den Schlüssel niemals nachträglich ersetzen: Ohne ihn lassen sich gespeicherte Hetzner-Tokens nicht mehr entschlüsseln.
Bei einer privaten Registry auf dem Produktionsserver einmalig mit einem Token anmelden, der nur Leserechte auf Container-Pakete besitzt:
echo "$GITEA_PULL_TOKEN" | docker login git.j-mahler.de --username deploy-bot --password-stdin
In .env.production mindestens IMAGE_TAG kontrollieren:
DYNDNS_IMAGE=git.j-mahler.de/admin/domain-control-webinterface-prod
IMAGE_TAG=v1.0.0
BIND_ADDRESS=127.0.0.1
WEB_PORT=8080
APP_MODE=https
MASTER_KEY_FILE=./secrets/dyndns_master_key
Anwendung laden und starten:
docker compose --env-file .env.production -f compose.production.yml pull
docker compose --env-file .env.production -f compose.production.yml up -d
docker compose --env-file .env.production -f compose.production.yml ps
curl --fail http://127.0.0.1:8080/healthz
3. HTTPS-Reverse-Proxy
Port 8080 bleibt mit BIND_ADDRESS=127.0.0.1 ausschließlich lokal erreichbar. Ein minimales Caddy-Beispiel:
dns.example.de {
reverse_proxy 127.0.0.1:8080
}
Danach https://dns.example.de öffnen und den ersten Administrator anlegen. APP_MODE=https sorgt dabei für HTTPS-gebundene Session-Cookies.
Nur in einem isolierten, vertrauenswürdigen LAN darf stattdessen eine private Server-IP und APP_MODE=lan verwendet werden. Den Dienst nicht unverschlüsselt über 0.0.0.0 ins Internet stellen.
4. Backup vor jedem Update
Für eine konsistente SQLite-Kopie wird der Container kurz gestoppt:
cd /opt/domain-control
docker compose --env-file .env.production -f compose.production.yml stop dyndns
docker run --rm \
-v domain-control-data:/data:ro \
-v "$PWD/backups:/backup" \
alpine:3.23.2 \
cp /data/dyndns.db /backup/dyndns-before-update.db
docker compose --env-file .env.production -f compose.production.yml start dyndns
Zusätzlich secrets/dyndns_master_key verschlüsselt und außerhalb dieses Servers sichern. Eine Datenbanksicherung ohne den dazugehörigen Master-Key ist für gespeicherte API-Tokens unbrauchbar.
5. Update
Nach Veröffentlichung beispielsweise von v1.1.0:
-
Backup wie oben erstellen.
-
In
.env.productionsetzen:IMAGE_TAG=v1.1.0 -
Neues Image laden und Container ersetzen:
docker compose --env-file .env.production -f compose.production.yml pull docker compose --env-file .env.production -f compose.production.yml up -d docker compose --env-file .env.production -f compose.production.yml ps docker compose --env-file .env.production -f compose.production.yml logs --tail=100 curl --fail http://127.0.0.1:8080/healthz
Das benannte Volume domain-control-data wird dabei nicht ersetzt.
6. Rollback
Ohne Datenbankmigration reicht es, in .env.production den vorherigen IMAGE_TAG einzutragen und erneut pull sowie up -d auszuführen.
Wenn die neue Version eine Datenbankmigration ausgeführt hat, gemeinsam auf das vorherige Image und die unmittelbar davor erstellte Datenbanksicherung zurückgehen. Automatische, unbeaufsichtigte Updates sind deshalb nicht empfohlen.
7. Nützliche Betriebsbefehle
# Status
docker compose --env-file .env.production -f compose.production.yml ps
# Logs
docker compose --env-file .env.production -f compose.production.yml logs -f --tail=100
# Neustart
docker compose --env-file .env.production -f compose.production.yml restart dyndns
# Bereits heruntergeladene, ungenutzte Images aufräumen
docker image prune