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>
89 lines
3.0 KiB
Markdown
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).
|