# Deployment mit Podman

## Bauen

```sh
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

```sh
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-data` hält die SQLite-DB (`/app/data/kver.db`), `kver-media` die
  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):

```sh
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:

```nginx
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):

```ini
[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:

```sh
systemctl --user daemon-reload
systemctl --user start kver
loginctl enable-linger "$USER"   # damit der Dienst ohne Login läuft
```

## Update einspielen

```sh
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).
