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 start0. 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 imagePullSecrets block in serviceaccount.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-secret

Free-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-found

4. Verify

# Pod statuskubectl -n nexus-gslb get pods -w# Logskubectl -n nexus-gslb logs statefulset/gslbd -f# API healthkubectl -n nexus-gslb port-forward svc/gslbd-api 8080:8080curl http://localhost:8080/api/v1/health# DNS query through the LoadBalancerLB=$(kubectl -n nexus-gslb get svc gslbd-dns \ -o jsonpath='{.status.loadBalancer.ingress[0].ip}')dig @$LB -p 5353 A gslb.example.com

Image

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

Or with the CLI:

cd deploy/kuberneteskustomize edit set image nexus-gslb/gslbd=ghcr.io/star-storm-development/nexus-gslb/gslbd:0.3.0

Scaling

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=3

Optionally 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:

  1. In configmap.yaml set dns.port: 53

  2. In statefulset.yaml add to the container securityContext:

    capabilities: add: ["NET_BIND_SERVICE"] drop: ["ALL"]
  3. Update services.yaml gslbd-dns port from 5353 to 53

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

Then in configmap.yaml:

dnssec: enabled: true zone: "gslb.example.com." ksk: envVar: "GSLB_KSK_PEM" zsk: envVar: "GSLB_ZSK_PEM" signatureValidityDays: 7

Uncomment 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_PEM

Prometheus metrics

The gslbd-metrics Service exposes /metrics on port 9090 with standard prometheus.io/scrape annotations. For Prometheus Operator:

apiVersion: monitoring.coreos.com/v1kind: ServiceMonitormetadata: name: gslbd namespace: nexus-gslbspec: selector: matchLabels: app.kubernetes.io/component: metrics endpoints: - port: metrics interval: 15s

Secrets management

For production, replace secret.yaml with your preferred secrets operator:

  • External Secrets Operator: create an ExternalSecret projecting from Vault/AWS SM/GCP SM into gslbd-credentials

  • Sealed Secrets: seal with kubeseal and commit the SealedSecret

  • 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 updatekubectl -n nexus-gslb rollout status statefulset/gslbd

The 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


Was this article helpful?