Kubernetes Deployment

Deploy Zero on Kubernetes for high availability, auto-scaling, and enterprise-grade infrastructure. This guide covers Helm chart installation and manual manifest deployment.

Prerequisites:
  • Kubernetes 1.28+ cluster
  • kubectl configured
  • Helm 3.12+ installed
  • Ingress controller (nginx-ingress or traefik)
  • cert-manager (for automatic TLS)

Helm Installation (Recommended)

Add Helm Repository

# Add Zero Helm repo
helm repo add zero https://charts.zero.io
helm repo update

# Search available versions
helm search repo zero

Create Namespace

kubectl create namespace zero

Create Secrets

# Generate secrets
JWT_SECRET=$(openssl rand -base64 32)
ENCRYPTION_KEY=$(openssl rand -base64 32)
POSTGRES_PASSWORD=$(openssl rand -base64 24)
REDIS_PASSWORD=$(openssl rand -base64 24)

# Create Kubernetes secret
kubectl create secret generic zero-secrets \
  --namespace zero \
  --from-literal=jwt-secret=$JWT_SECRET \
  --from-literal=encryption-key=$ENCRYPTION_KEY \
  --from-literal=postgres-password=$POSTGRES_PASSWORD \
  --from-literal=redis-password=$REDIS_PASSWORD

Configure values.yaml

# values.yaml
global:
  domain: zero.yourdomain.com

api:
  replicaCount: 3
  image:
    repository: ghcr.io/zero/zero-api
    tag: latest
  resources:
    requests:
      cpu: 500m
      memory: 512Mi
    limits:
      cpu: 2000m
      memory: 2Gi
  autoscaling:
    enabled: true
    minReplicas: 3
    maxReplicas: 10
    targetCPUUtilization: 70

web:
  replicaCount: 2
  image:
    repository: ghcr.io/zero/zero-web
    tag: latest

postgresql:
  enabled: true
  auth:
    existingSecret: zero-secrets
    secretKeys:
      adminPasswordKey: postgres-password
  primary:
    persistence:
      size: 100Gi
      storageClass: fast-ssd

redis:
  enabled: true
  auth:
    existingSecret: zero-secrets
    existingSecretPasswordKey: redis-password
  master:
    persistence:
      size: 10Gi

vpn:
  enabled: true
  replicaCount: 2
  service:
    type: LoadBalancer
    annotations:
      # For AWS
      service.beta.kubernetes.io/aws-load-balancer-type: nlb

ingress:
  enabled: true
  className: nginx
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
  hosts:
    - host: api.zero.yourdomain.com
      paths:
        - path: /
          pathType: Prefix
          service: api
    - host: console.zero.yourdomain.com
      paths:
        - path: /
          pathType: Prefix
          service: web
  tls:
    - secretName: zero-tls
      hosts:
        - api.zero.yourdomain.com
        - console.zero.yourdomain.com

monitoring:
  enabled: true
  serviceMonitor:
    enabled: true
  grafanaDashboard:
    enabled: true

Install Chart

# Install with custom values
helm install zero zero/zero \
  --namespace zero \
  --values values.yaml \
  --wait

# Verify installation
kubectl get pods -n zero
kubectl get svc -n zero
kubectl get ingress -n zero

Manual Manifest Deployment

For more control, deploy using raw Kubernetes manifests:

API Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: zero-api
  namespace: zero
spec:
  replicas: 3
  selector:
    matchLabels:
      app: zero-api
  template:
    metadata:
      labels:
        app: zero-api
    spec:
      containers:
      - name: api
        image: ghcr.io/zero/zero-api:latest
        ports:
        - containerPort: 8080
        env:
        - name: DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: zero-secrets
              key: database-url
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: zero-secrets
              key: jwt-secret
        resources:
          requests:
            cpu: 500m
            memory: 512Mi
          limits:
            cpu: 2000m
            memory: 2Gi
        livenessProbe:
          httpGet:
            path: /api/v1/health
            port: 8080
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /api/v1/health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: zero-api
  namespace: zero
spec:
  selector:
    app: zero-api
  ports:
  - port: 8080
    targetPort: 8080

Horizontal Pod Autoscaler

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: zero-api-hpa
  namespace: zero
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: zero-api
  minReplicas: 3
  maxReplicas: 10
  metrics:
  - type: Resource
    resource:
      name: cpu
      target:
        type: Utilization
        averageUtilization: 70
  - type: Resource
    resource:
      name: memory
      target:
        type: Utilization
        averageUtilization: 80

Pod Disruption Budget

apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
  name: zero-api-pdb
  namespace: zero
spec:
  minAvailable: 2
  selector:
    matchLabels:
      app: zero-api

Database Configuration

Using External PostgreSQL

For production, we recommend using a managed database:

  • AWS RDS PostgreSQL
  • Google Cloud SQL
  • Azure Database for PostgreSQL
  • DigitalOcean Managed Databases
# Create secret for external database
kubectl create secret generic zero-db \
  --namespace zero \
  --from-literal=database-url="postgres://user:password@host:5432/zero?sslmode=require"

Persistent Storage

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zero-data
  namespace: zero
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: fast-ssd
  resources:
    requests:
      storage: 100Gi

Network Policies

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: zero-api-policy
  namespace: zero
spec:
  podSelector:
    matchLabels:
      app: zero-api
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          name: ingress-nginx
    ports:
    - port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: postgresql
    ports:
    - port: 5432
  - to:
    - podSelector:
        matchLabels:
          app: redis
    ports:
    - port: 6379

Monitoring & Observability

Prometheus ServiceMonitor

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: zero-api
  namespace: zero
spec:
  selector:
    matchLabels:
      app: zero-api
  endpoints:
  - port: metrics
    interval: 30s
    path: /metrics

Prometheus Alerts

apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
  name: zero-alerts
  namespace: zero
spec:
  groups:
  - name: zero
    rules:
    - alert: ZeroAPIDown
      expr: up{job="zero-api"} == 0
      for: 5m
      labels:
        severity: critical
      annotations:
        summary: "Zero API is down"
    - alert: ZeroHighLatency
      expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 1
      for: 10m
      labels:
        severity: warning
      annotations:
        summary: "Zero API high latency"

Upgrading

# Helm upgrade
helm upgrade zero zero/zero \
  --namespace zero \
  --values values.yaml \
  --wait

# Rolling restart
kubectl rollout restart deployment/zero-api -n zero

# Check rollout status
kubectl rollout status deployment/zero-api -n zero

Backup Strategy

# Using Velero for cluster backup
velero backup create zero-backup \
  --include-namespaces zero \
  --wait

# Scheduled backup
velero schedule create zero-daily \
  --schedule="@daily" \
  --include-namespaces zero

Troubleshooting

# Check pod status
kubectl get pods -n zero -o wide

# View pod logs
kubectl logs -f deployment/zero-api -n zero

# Describe pod for events
kubectl describe pod -l app=zero-api -n zero

# Execute into pod
kubectl exec -it deployment/zero-api -n zero -- /bin/sh

# Check resource usage
kubectl top pods -n zero

Next Steps