Files
SRCmail/deploy/k8s/README.md
T
Bernd RodlerandClaude Opus 4.8 476c76c420 docs(deploy): sharpen k8s admin runbook — inventory, pre-flight, ordered apply
Self-contained guide: exactly what to deploy (7 objects + image), 3 cluster
values to match against bulwark, copy-paste apply order, verify, update/rollback,
troubleshooting table. Plain kubectl apply (no GitOps).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 17:13:51 +02:00

130 lines
4.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VNCmail+ — Admin Deployment Guide (microk8s)
Deploy VNCmail+ (VNC's Bulwark fork) as a container at **`vncmail.sandbox.vnc.de`**,
**alongside** the existing `bulwark.sandbox.vnc.de`. Plain `kubectl apply` — no
GitOps needed.
> Why a container (not Vercel): Bulwark is stateful — it writes settings/admin/
> telemetry to `/app/data`, which needs persistent volumes.
---
## 1. What you are deploying
| # | Object | File | Purpose |
|---|--------|------|---------|
| 1 | Namespace `vncmail` | `namespace.yaml` | Isolates the app |
| 2 | 4× PersistentVolumeClaim | `pvc.yaml` | `/app/data/{settings,admin,admin-state,telemetry}` |
| 3 | Secret `vncmail-env` | `secret.yaml` *(you create it)* | App config (JMAP URL, session secret, branding) |
| 4 | Secret `ghcr-pull` | *(you create it — command below)* | Pull the private image from GHCR |
| 5 | Deployment `vncmail-plus` | `deployment.yaml` | The app pod |
| 6 | Service `vncmail-plus` | `service.yaml` | ClusterIP :80 → pod :3000 |
| 7 | Ingress `vncmail-plus` | `ingress.yaml` | TLS host `vncmail.sandbox.vnc.de` |
**Image:** `ghcr.io/brvncde-dotcom/vncmail-plus-dev:latest`
(built automatically by CI from the `dev` branch). For anything beyond the
sandbox, pin a digest — see §5.
---
## 2. Pre-flight — confirm 3 cluster values (2 min)
The manifests use microk8s defaults. **Copy the exact values the existing
Bulwark uses** so VNCmail+ matches your cluster:
```bash
# Find bulwark's ingress and read off its class + cert-manager annotations:
kubectl get ingress -A | grep -i bulwark
kubectl get ingress <bulwark-ingress-name> -n <bulwark-ns> -o yaml
# List available storage classes and ingress classes:
kubectl get sc
kubectl get ingressclass
kubectl get clusterissuer # cert-manager issuers (if used)
```
Then edit if they differ from the defaults below:
| Value | Default in manifests | File to edit |
|-------|----------------------|--------------|
| StorageClass | `microk8s-hostpath` | `pvc.yaml` (all 4) |
| IngressClass | `public` | `ingress.yaml` |
| cert-manager issuer | `letsencrypt-prod` | `ingress.yaml` |
---
## 3. Deploy (copy-paste, in order)
```bash
cd deploy/k8s
# a) Namespace
kubectl apply -f namespace.yaml
# b) Image-pull secret — the GHCR package is private.
# Use a GitHub PAT (classic) with the read:packages scope.
kubectl create secret docker-registry ghcr-pull \
--namespace vncmail \
--docker-server=ghcr.io \
--docker-username=brvncde-dotcom \
--docker-password='<GITHUB_PAT_read:packages>' \
--docker-email=br@vnc.biz
# c) App config secret — copy the template, set a real SESSION_SECRET, apply.
cp secret.example.yaml secret.yaml
# edit secret.yaml: SESSION_SECRET: "$(openssl rand -base64 32)"
kubectl apply -f secret.yaml
# d) Everything else (PVCs, Deployment, Service, Ingress)
kubectl apply -k .
```
> Alternative to (b): make the GHCR package public
> (GitHub → Packages → vncmail-plus-dev → Package settings → Change visibility),
> then delete the `imagePullSecrets:` block from `deployment.yaml`.
---
## 4. Verify
```bash
kubectl -n vncmail rollout status deploy/vncmail-plus # -> successfully rolled out
kubectl -n vncmail get pods,pvc,ingress
# DNS: point vncmail.sandbox.vnc.de at the same ingress IP as bulwark.sandbox.vnc.de.
# cert-manager issues TLS once DNS resolves. Then:
curl -sI https://vncmail.sandbox.vnc.de/api/health # -> HTTP/2 200
```
Open `https://vncmail.sandbox.vnc.de` and log in with a **full** email address
(e.g. `bernd.rodler@sandbox.vnc.de`) — Stalwart authenticates the full email, not
a bare username.
---
## 5. Update to a new build
```bash
# CI rebuilds ghcr.io/brvncde-dotcom/vncmail-plus-dev on every push to `dev`.
kubectl -n vncmail rollout restart deploy/vncmail-plus # pulls :latest (imagePullPolicy: Always)
# Production: pin a digest instead of :latest so rollouts are deterministic.
kubectl -n vncmail set image deploy/vncmail-plus \
vncmail-plus=ghcr.io/brvncde-dotcom/vncmail-plus-dev@sha256:<digest>
```
Rollback: `kubectl -n vncmail rollout undo deploy/vncmail-plus`
---
## 6. Troubleshooting
| Symptom | Cause / fix |
|---------|-------------|
| Pod `ImagePullBackOff` | `ghcr-pull` secret missing/expired, or package still private. Recreate the secret (§3b) or make the package public. |
| Pod `CrashLoopBackOff`, logs show `EACCES`/permission on `/app/data` | Volume not writable by uid 1001. `securityContext.fsGroup: 1001` is set in `deployment.yaml` — keep it; some storage drivers also need it on the PVC. |
| PVC stuck `Pending` | Wrong `storageClassName` in `pvc.yaml`. Set it to one from `kubectl get sc`. |
| Ingress has no address / no cert | Wrong `ingressClassName` or cert issuer. Match bulwark's (§2). Check `kubectl -n vncmail describe ingress vncmail-plus`. |
| Login shows "Ein Fehler ist aufgetreten" | Use the **full** email (`user@sandbox.vnc.de`), not a bare username. |
| Can't reach Stalwart | Check `JMAP_SERVER_URL` in the secret = `https://stalwart.sandbox.vnc.de`. |