Kubernetes Deployment
Deploy the NAT self-hosted server on Kubernetes using the manifests in deploy/k8s/. The default configuration runs 2 replicas behind a ClusterIP Service on port 8080 with three PersistentVolumeClaims for scan data, model weights, and regression results.
For simpler single-host deployments, see the Docker Deployment guide. Use this guide when you need horizontal scaling, rolling updates, or cluster-native ingress.
Prerequisites
kubectlconfigured and pointed at your target cluster- Cluster version Kubernetes 1.24+
- A default StorageClass that supports
ReadWriteOnce(or update the PVC specs for your storage provider) - The
deploy/k8s/manifests from the NAT self-hosted distribution
The deploy/k8s/ manifests are included with NAT's self-hosted distribution. If you installed NAT via pip install nat-engine, the manifests are available in your site-packages directory. You can also download them from your NAT dashboard under Settings → Downloads or contact sales@nat-testing.io for access.
Quick-start deployment
Create the namespace
kubectl create namespace natApply the manifests
kubectl apply -f configmap.yaml -n nat
kubectl apply -f deployment.yaml -n nat
kubectl apply -f service.yaml -n natVerify the rollout
kubectl rollout status deployment/nat -n nat
# deployment "nat" successfully rolled out
kubectl get pods -n nat
# NAME READY STATUS RESTARTS AGE
# nat-7d4f9b8c6-abcde 1/1 Running 0 2m
# nat-7d4f9b8c6-fghij 1/1 Running 0 2mConfirm health
kubectl port-forward svc/nat 8080:8080 -n nat &
curl http://localhost:8080/api/v1/health
# {"status":"ok","version":"1.x.x"}Configuration via ConfigMap
The configmap.yaml sets environment variables for all NAT pods:
# deploy/k8s/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: nat-config
data:
NAT_HOST: "0.0.0.0"
NAT_PORT: "8080"
NAT_LOG_LEVEL: "info"
NAT_WORKERS: "4"Edit the ConfigMap to change settings, then roll out the deployment:
kubectl edit configmap nat-config -n nat
kubectl rollout restart deployment/nat -n nat| Variable | Description | Default |
|---|---|---|
NAT_HOST | Bind address | 0.0.0.0 |
NAT_PORT | Listen port | 8080 |
NAT_LOG_LEVEL | Log verbosity (debug, info, warn, error) | info |
NAT_WORKERS | Number of worker processes | 4 |
Sensitive configuration
Store secrets (database URL, API keys) in a Kubernetes Secret rather than the ConfigMap:
kubectl create secret generic nat-secrets \
--from-literal=DATABASE_URL='postgresql://user:pass@postgres:5432/nat' \
--from-literal=NAT_SECRET_KEY='your-secret-key' \
-n natReference the secret in your Deployment's envFrom:
envFrom:
- configMapRef:
name: nat-config
- secretRef:
name: nat-secretsAlways run alembic upgrade head against the database before deploying a new NAT version that includes schema changes. Run it as a Kubernetes Job or init container to keep migrations in sync with the rollout.
Persistent storage
The deployment uses three PersistentVolumeClaims:
| PVC | Mount path | Default size | Purpose |
|---|---|---|---|
nat-scans-pvc | /data/scans | 10Gi | Scan results and HTML reports |
nat-weights-pvc | /data/weights | 5Gi | Model weights cache |
nat-regression-pvc | /data/regression | 5Gi | Regression test baselines |
PVCs are created automatically when you apply deployment.yaml. To inspect them:
kubectl get pvc -n natTo resize a PVC, edit it and patch the request (requires a StorageClass that supports volume expansion):
kubectl patch pvc nat-scans-pvc -n nat \
-p '{"spec":{"resources":{"requests":{"storage":"20Gi"}}}}'Exposing the service
Quick access for local development or debugging — no DNS or load balancer needed:
kubectl port-forward svc/nat 8080:8080 -n nat
# NAT dashboard available at http://localhost:8080Scaling
Manual scaling
kubectl scale deployment nat --replicas=4 -n natHorizontal Pod Autoscaler
Create an HPA to scale automatically based on CPU utilization:
# hpa.yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nat
namespace: nat
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nat
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 60 # scale up when average CPU exceeds 60%kubectl apply -f hpa.yaml -n nat
kubectl get hpa -n natUpdating the image
Rolling updates with zero downtime:
kubectl set image deployment/nat nat=natengine/nat:1.3.0 -n nat
kubectl rollout status deployment/nat -n natRoll back if the new version is unhealthy:
kubectl rollout undo deployment/nat -n natTroubleshooting
Pods stuck in Pending
kubectl describe pod <pod-name> -n natCommon causes:
- Insufficient cluster resources — check node capacity
- PVC can't be provisioned — check StorageClass and PVC events
- Image pull failure — verify the image name and any registry credentials
Pods in CrashLoopBackOff
kubectl logs <pod-name> -n nat --previousCommon causes:
- Missing
DATABASE_URLsecret — ensure the Secret exists and is referenced - Failed database migration — run
alembic upgrade headand check for errors - Wrong
NAT_HOST— must be0.0.0.0(not127.0.0.1) inside a container
Health probe failures
The deployment configures liveness and readiness probes at GET /api/v1/health. If probes fail:
# Check the health endpoint directly from inside a pod
kubectl exec -it <pod-name> -n nat -- curl http://localhost:8080/api/v1/healthAdjust initialDelaySeconds in the Deployment spec if the server takes longer than expected to start.
Next steps
- Self-Hosted Setup — complete self-hosted guide including pip install and configuration
- Docker Deployment — Docker and Docker Compose deployment
- CLI Reference — full reference for
natandnat-admincommands