Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
3.6 KiB
Deployment mit Podman
Bauen
podman build -t kver .
Multi-Stage-Build (Containerfile): statisches Go-Binary (CGO aus, modernc-
SQLite ist pures Go), Laufzeit-Image ist Alpine mit unprivilegiertem Nutzer.
Die Warnung HEALTHCHECK is not supported for OCI image format ist erwartbar:
Podman baut OCI-Images, dort gibt es kein eingebettetes HEALTHCHECK. Der
Healthcheck kommt stattdessen aus der Quadlet-Unit (s.u.); alternativ
podman build --format=docker oder podman run --health-cmd=....
Starten
podman run -d --name kver \
-p 127.0.0.1:8080:8080 \
-v kver-data:/app/data \
-v kver-media:/app/static/media \
kver
kver-datahält die SQLite-DB (/app/data/kver.db),kver-mediadie hochgeladenen Bilder. Beides Named Volumes -> überleben Image-Updates.- Port nur an localhost binden; nach außen geht es über den Reverse-Proxy.
Konfiguration über Umgebungsvariablen:
| Variable | Default | Bedeutung |
|---|---|---|
KVER_ADDR |
:8080 |
Listen-Adresse |
KVER_DB |
/app/data/kver.db |
Pfad zur SQLite-Datei |
Bestehende Daten übernehmen
DB und Medien einmalig in die Volumes kopieren (Container muss dafür kurz gelaufen sein bzw. die Volumes existieren):
podman volume create kver-data
podman volume create kver-media
podman cp kver.db kver:/app/data/kver.db # bei laufendem Container, oder:
cp kver.db "$(podman volume inspect kver-data -f '{{.Mountpoint}}')/"
cp static/media/* "$(podman volume inspect kver-media -f '{{.Mountpoint}}')/"
Danach Container neu starten. (Rootless: podman unshare cp ..., damit die
UID-Zuordnung ins User-Namespace-Mapping passt.)
Reverse-Proxy (TLS)
Der Container spricht nur HTTP. Davor gehört ein TLS-terminierender Proxy
(Caddy/nginx), der X-Forwarded-Proto: https setzt — daran erkennt die App
HTTPS und setzt das Secure-Flag auf den Session-Cookie (siehe isHTTPS).
nginx-Beispiel:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
client_max_body_size 20m; # Uploads bis 17 MB durchlassen
}
client_max_body_size muss über dem App-Limit (17 MB, maxUploadBytes)
liegen, sonst kappt nginx vor der App.
Hinweis Rate-Limiting: httprate.LimitByIP auf den Auth-Routen sieht hinter
einem Proxy die Proxy-IP. Entweder den Proxy die echte Client-IP als
X-Real-IP/X-Forwarded-For setzen lassen und chi's middleware.RealIP
vorschalten — oder damit leben, dass das Limit global statt pro IP wirkt.
Autostart mit systemd (Quadlet)
~/.config/containers/systemd/kver.container (rootless):
[Unit]
Description=kver (Kontrollverlust)
[Container]
Image=localhost/kver:latest
PublishPort=127.0.0.1:8080:8080
Volume=kver-data:/app/data
Volume=kver-media:/app/static/media
# Healthcheck hier statt im Containerfile: Podman baut standardmäßig
# OCI-Images, die kennen kein HEALTHCHECK (daher die Warnung beim Build).
HealthCmd=wget -qO /dev/null http://localhost:8080/entry/feed/0
HealthInterval=30s
[Service]
Restart=on-failure
[Install]
WantedBy=default.target
Aktivieren:
systemctl --user daemon-reload
systemctl --user start kver
loginctl enable-linger "$USER" # damit der Dienst ohne Login läuft
Update einspielen
podman build -t kver .
systemctl --user restart kver # bzw. podman stop kver && podman run ...
Die App fährt bei SIGTERM sauber herunter (laufende Requests werden zu Ende bedient, WAL-Checkpoint beim DB-Close).