Files
SRCmail/deploy/k8s/README.md
T
Bernd RodlerandClaude Opus 4.8 a3d551b640 feat(deploy): k8s manifests for microk8s (vncmail.sandbox.vnc.de)
Bulwark is stateful (local /app/data) — Vercel serverless (read-only fs)
crashes it. Deploy as a container with 4 persistent volumes on microk8s,
alongside bulwark.sandbox.vnc.de. Adds deploy/k8s/ (namespace, pvc, deployment,
service, ingress, secret template, runbook) + rewrites setup doc off Vercel.

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

89 lines
3.0 KiB
Markdown

# VNCmail+ on Kubernetes (microk8s)
Deploys the VNCmail+ fork as a **new subdomain** (`vncmail.sandbox.vnc.de`),
alongside the existing `bulwark.sandbox.vnc.de`. This is Bulwark's native model:
a long-lived container + persistent volumes. (Vercel was dropped — its serverless
filesystem is read-only, which crashes Bulwark's disk-backed features.)
## Image
CI builds and pushes the image on every push to `dev`/`main`
(`.github/workflows/docker-publish.yml`):
- `dev``ghcr.io/brvncde-dotcom/vncmail-plus-dev`
- `main``ghcr.io/brvncde-dotcom/vncmail-plus-beta`
- release tag → `ghcr.io/brvncde-dotcom/vncmail-plus` (clean name)
The manifests use the `-dev` image. **For production, pin a digest** instead of
`:latest`:
```
kubectl -n vncmail set image deploy/vncmail-plus \
vncmail-plus=ghcr.io/brvncde-dotcom/vncmail-plus-dev@sha256:<digest>
```
## Prerequisites — match your cluster
These manifests use microk8s defaults; confirm they match how
`bulwark.sandbox.vnc.de` is deployed and edit if not:
| Thing | File | Check with |
|-------|------|-----------|
| StorageClass (`microk8s-hostpath`) | `pvc.yaml` | `kubectl get sc` |
| IngressClass (`public`) | `ingress.yaml` | `kubectl get ingressclass` |
| cert-manager issuer (`letsencrypt-prod`) | `ingress.yaml` | `kubectl get clusterissuer` + bulwark's ingress |
Quickest: copy bulwark's own settings —
`kubectl get ingress -A | grep bulwark` then `kubectl get ingress <name> -n <ns> -o yaml`.
## Deploy
```bash
# 1. Namespace
kubectl apply -f namespace.yaml
# 2. GHCR pull secret (package is private by default).
# Use a GitHub PAT with read:packages.
kubectl create secret docker-registry ghcr-pull \
--namespace vncmail \
--docker-server=ghcr.io \
--docker-username=brvncde-dotcom \
--docker-password=<GITHUB_PAT_with_read:packages> \
--docker-email=br@vnc.biz
# (Or make the package public in GHCR and remove imagePullSecrets from deployment.yaml.)
# 3. App config secret
cp secret.example.yaml secret.yaml
# edit secret.yaml → set SESSION_SECRET (openssl rand -base64 32)
kubectl apply -f secret.yaml
# 4. Everything else
kubectl apply -k . # kustomization: pvc, deployment, service, ingress
# 5. Watch it come up
kubectl -n vncmail rollout status deploy/vncmail-plus
kubectl -n vncmail get pods,ingress
```
## DNS
Point `vncmail.sandbox.vnc.de` at the same ingress load-balancer IP as
`bulwark.sandbox.vnc.de` (A/AAAA or CNAME). cert-manager issues the TLS cert
once DNS resolves.
## Verify
```bash
curl -sI https://vncmail.sandbox.vnc.de/api/health # expect 200
```
Then open `https://vncmail.sandbox.vnc.de` and log in with a full
`@sandbox.vnc.de` address (e.g. `bernd.rodler@sandbox.vnc.de`) — Stalwart
authenticates the **full email**, not a bare username.
## Update after a UI change
```bash
git push origin dev # CI rebuilds ghcr.io/...-dev
kubectl -n vncmail rollout restart deploy/vncmail-plus # pull new image
```
Promote to production the usual dev-first way (see ../../VNCMAIL-SETUP.md).