Files
SRCmail/deploy/k8s
Bernd RodlerandClaude Opus 4.8 22e5e97da9 feat(theme): VNClagoon brand theme (navy + cyan) as default, dark-first
Add builtin-vnclagoon theme (cyan #00D4FF accent on navy #0A0E1A, DM Sans body
+ Syne headings, self-hosted OFL fonts); set as default theme policy; default
mode dark. Add VNCmail wordmark SVGs (on-dark/on-light) + wire logo/company via
k8s secret template. Placeholder wordmark — swap official styleguide SVG.

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

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:

# 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)

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

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

# 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.