Kubernetes Deployment
Nexus GSLB installs on Kubernetes with kustomize manifests in deploy/kubernetes/. The manifests use a StatefulSet where each pod runs a co-located rqlited datastore (a native sidecar) and gets its own PVC. With replicas: 1 you get a standalone single node; with 3+ (odd) replicas the pods form one rqlite Raft cluster (ordinal-0 bootstraps; higher ordinals join it via the headless service) that replicates all config and data-plane objects across pods. NATS (optional) carries only ephemeral health/membership state, not config. See Datastore.
The manifests include full production hardening: NetworkPolicy, PodDisruptionBudget, seccompProfile, non-root uid, and automountServiceAccountToken: false.
Architecture
┌─────────────────────────────────────────────────┐
│ Kubernetes cluster │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ gslbd-0 │ │ gslbd-1 │ ... │
│ │ PVC: data-0 │ │ PVC: data-1 │ │
│ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │
│ └────────┬─────────┘ │
│ │ NATS JetStream (optional) │
│ ┌───────────────▼──────────────────┐ │
│ │ LoadBalancer: gslbd-dns :5353 │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────┘
Manifest overview
| File | Resource(s) |
|---|---|
namespace.yaml |
Namespace: nexus-gslb |
serviceaccount.yaml |
ServiceAccount: gslbd (no token automount) |
configmap.yaml |
ConfigMap: gslbd-config — config.yaml |
secret.yaml |
Secret: gslbd-credentials — license keys, optional DNSSEC PEMs |
statefulset.yaml |
StatefulSet: gslbd with per-pod 1 Gi PVC |
services.yaml |
4 services: headless, DNS LoadBalancer, API ClusterIP, metrics ClusterIP |
networkpolicy.yaml |
Ingress/egress policy restricting pod traffic |
poddisruptionbudget.yaml |
maxUnavailable: 1 |
kustomization.yaml |
Kustomize entry point |
Quick start
0. Registry access
The images are published to public GitHub Container Registry — no authentication or pull secret is required:
ghcr.io/star-storm-development/nexus-gslb/gslbd
ghcr.io/star-storm-development/nexus-gslb/webui
Verify with an anonymous pull: docker pull ghcr.io/star-storm-development/nexus-gslb/gslbd:latest.
If you mirror the images to a private registry, create a pull secret and uncomment the
imagePullSecretsblock inserviceaccount.yaml.
1. Set credentials
kubectl create secret generic gslbd-credentials \
--namespace nexus-gslb \
--from-literal=GSLB_LICENSE_KEY=your-key \
--from-literal=GSLB_LICENSE_SECRET=your-secretFree-tier deployments can skip this; the daemon enforces unlicensed RPS limits automatically.
2. Edit the configuration
Edit deploy/kubernetes/configmap.yaml to set your DNS domain, load-balancer endpoints, and any other options. The ConfigMap is mounted read-only at /etc/gslb/config.yaml inside each pod.
3. Deploy
# With kustomize (recommended)
kubectl apply -k deploy/kubernetes/
# Or apply manifests directly (skip kustomization.yaml)
kubectl apply -f deploy/kubernetes/ --ignore-not-found4. Verify
# Pod status
kubectl -n nexus-gslb get pods -w
# Logs
kubectl -n nexus-gslb logs statefulset/gslbd -f
# API health
kubectl -n nexus-gslb port-forward svc/gslbd-api 8080:8080
curl http://localhost:8080/api/v1/health
# DNS query through the LoadBalancer
LB=$(kubectl -n nexus-gslb get svc gslbd-dns \
-o jsonpath='{.status.loadBalancer.ingress[0].ip}')
dig @$LB -p 5353 A gslb.example.comImage
The statefulset.yaml defaults to nexus-gslb/gslbd:latest. Update via kustomize without editing the StatefulSet directly:
# In deploy/kubernetes/kustomization.yaml:
images:
- name: nexus-gslb/gslbd
newName: ghcr.io/star-storm-development/nexus-gslb/gslbd
newTag: 0.3.0Or with the CLI:
cd deploy/kubernetes
kustomize edit set image nexus-gslb/gslbd=ghcr.io/star-storm-development/nexus-gslb/gslbd:0.3.0Scaling
Config and data-plane objects replicate automatically: the per-pod rqlited sidecars form one Raft cluster (use an odd replica count; a 3-node Raft cluster tolerates one lost pod). NATS is optional and carries only ephemeral state — without it each pod still serves the same config but only observes health from its own probes.
# Scale to 3 replicas (each gets its own PVC)
kubectl -n nexus-gslb scale statefulset/gslbd --replicas=3Optionally enable ephemeral state sync in configmap.yaml:
state:
nats:
servers: ["nats://your-nats:4222"]
healthPolicy: "prefer-local"See docs/user/StateSyncSetup.md for full NATS configuration.
DNS on port 53
The default DNS port is 5353 (no privileged port required). Most cloud load balancers can map port 53 → 5353 transparently. To run natively on port 53:
- In
configmap.yamlsetdns.port: 53 - In
statefulset.yamladd to the containersecurityContext:capabilities: add: ["NET_BIND_SERVICE"] drop: ["ALL"] - Update
services.yamlgslbd-dnsport from5353to53
DNSSEC
Pass DNSSEC key PEM content via the Secret (recommended over file mounts):
kubectl create secret generic gslbd-credentials \
--namespace nexus-gslb \
--from-literal=GSLB_LICENSE_KEY=your-key \
--from-literal=GSLB_LICENSE_SECRET=your-secret \
--from-file=GSLB_KSK_PEM=./keys/ksk.pem \
--from-file=GSLB_ZSK_PEM=./keys/zsk.pemThen in configmap.yaml:
dnssec:
enabled: true
zone: "gslb.example.com."
ksk:
envVar: "GSLB_KSK_PEM"
zsk:
envVar: "GSLB_ZSK_PEM"
signatureValidityDays: 7Uncomment the GSLB_KSK_PEM / GSLB_ZSK_PEM env var blocks in statefulset.yaml.
Export the DS record:
kubectl -n nexus-gslb exec statefulset/gslbd -- \
gslbctl dnssec ds --zone gslb.example.com. --ksk-env GSLB_KSK_PEMPrometheus metrics
The gslbd-metrics Service exposes /metrics on port 9090 with standard prometheus.io/scrape annotations. For Prometheus Operator:
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: gslbd
namespace: nexus-gslb
spec:
selector:
matchLabels:
app.kubernetes.io/component: metrics
endpoints:
- port: metrics
interval: 15sSecrets management
For production, replace secret.yaml with your preferred secrets operator:
- External Secrets Operator: create an
ExternalSecretprojecting from Vault/AWS SM/GCP SM intogslbd-credentials - Sealed Secrets: seal with
kubesealand commit theSealedSecret - Vault Agent: inject secrets as environment variables via annotation
The Secret name (gslbd-credentials) and key names (GSLB_LICENSE_KEY, GSLB_LICENSE_SECRET) must match what the StatefulSet references.
Upgrade
# Update image tag in kustomization.yaml, then:
kubectl apply -k deploy/kubernetes/
# Watch the rolling update
kubectl -n nexus-gslb rollout status statefulset/gslbdThe StatefulSet uses RollingUpdate strategy. With maxUnavailable: 1 (PDB), a single-replica deployment will briefly be unavailable — scale to 2+ for zero-downtime upgrades.
Uninstall
kubectl delete -k deploy/kubernetes/
# PVCs are not deleted by kustomize — remove manually if needed:
kubectl -n nexus-gslb delete pvc -l app.kubernetes.io/name=gslbd