Skip to content

Kubernetes Deployment

FORGE generates production-grade Kubernetes manifests with health probes, resource limits, horizontal auto-scaling, and automated TLS certificate provisioning via cert-manager. This is the recommended deployment strategy for applications that need auto-scaling, self-healing, rolling updates, and load balancing.

Generate Kubernetes Manifests

bash
forge deploy:k8s \
  --env=production \
  --ssl=letsencrypt \
  --email=admin@example.com

Command Options

OptionTypeDefaultDescription
--envstringproductionTarget environment: staging or production
--domainstringfrom forge.yamlProduction domain (e.g., myapp.com)
--sslstringletsencryptSSL method: letsencrypt, custom, or none
--emailstringrequired for LEEmail for Let's Encrypt notifications
--replicasnumber2Number of pod replicas per service
--namespacestring{app-name}Kubernetes namespace
--registrystringdocker.ioContainer image registry
--outputstring./k8sOutput directory for manifests

Examples

bash
forge deploy:k8s \
  --env=production \
  --domain=myapp.com \
  --ssl=letsencrypt \
  --email=admin@myapp.com \
  --replicas=3 \
  --namespace=myapp-prod \
  --registry=ghcr.io/myorg
bash
forge deploy:k8s \
  --env=staging \
  --domain=staging.myapp.com \
  --ssl=letsencrypt \
  --email=admin@myapp.com \
  --replicas=1 \
  --namespace=myapp-staging
bash
forge deploy:k8s \
  --env=production \
  --output=./infrastructure/k8s/production

Generated File Structure

k8s/
├── namespace.yaml              # Namespace definition
├── configmap.yaml              # Non-sensitive configuration
├── secrets.yaml                # Sensitive data (base64 encoded)

├── api/
│   ├── deployment.yaml         # API pods with health probes
│   ├── service.yaml            # ClusterIP service for API
│   └── hpa.yaml                # Horizontal Pod Autoscaler

├── web/
│   ├── deployment.yaml         # Web app pods
│   ├── service.yaml            # ClusterIP service for web
│   └── hpa.yaml                # Horizontal Pod Autoscaler

├── admin/
│   ├── deployment.yaml         # Admin app pods
│   ├── service.yaml            # ClusterIP service for admin
│   └── hpa.yaml                # Horizontal Pod Autoscaler

├── ingress.yaml                # Ingress with TLS termination

├── cert-manager/
│   ├── cluster-issuer.yaml     # Let's Encrypt ClusterIssuer
│   └── certificate.yaml        # TLS certificate request

├── postgres/
│   ├── deployment.yaml         # PostgreSQL (or use managed DB)
│   ├── service.yaml            # Database service
│   └── pvc.yaml                # Persistent Volume Claim

├── redis/
│   ├── deployment.yaml         # Redis cache
│   └── service.yaml            # Redis service

└── kustomization.yaml          # Kustomize for unified deployment

API Deployment

The API deployment includes health probes, resource limits, and environment configuration:

yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-api
  namespace: myapp
  labels:
    app: myapp
    component: api
spec:
  replicas: 2
  selector:
    matchLabels:
      app: myapp
      component: api
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxUnavailable: 0
      maxSurge: 1
  template:
    metadata:
      labels:
        app: myapp
        component: api
    spec:
      containers:
        - name: api
          image: ghcr.io/myorg/myapp-api:latest
          ports:
            - containerPort: 8080
              protocol: TCP
          envFrom:
            - configMapRef:
                name: myapp-config
            - secretRef:
                name: myapp-secrets
          resources:
            requests:
              memory: "256Mi"
              cpu: "100m"
            limits:
              memory: "512Mi"
              cpu: "500m"
          livenessProbe:
            httpGet:
              path: /health
              port: 8080
            initialDelaySeconds: 10
            periodSeconds: 10
            timeoutSeconds: 5
            failureThreshold: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
            timeoutSeconds: 3
            failureThreshold: 2
          startupProbe:
            httpGet:
              path: /health
              port: 8080
            failureThreshold: 30
            periodSeconds: 2

Health probe endpoints

The generated API includes three health endpoints:

  • /health -- basic liveness check (is the process running?)
  • /ready -- readiness check (can the service accept traffic? checks database connectivity)
  • /startup -- used by the startup probe to allow longer initialization time

Ingress with Let's Encrypt

The Ingress resource routes traffic to the correct service based on hostname and configures TLS with cert-manager:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: myapp-ingress
  namespace: myapp
  annotations:
    kubernetes.io/ingress.class: nginx
    cert-manager.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
  tls:
    - hosts:
        - myapp.com
        - admin.myapp.com
        - api.myapp.com
      secretName: myapp-tls
  rules:
    - host: myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-web
                port:
                  number: 3000

    - host: admin.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-admin
                port:
                  number: 3001

    - host: api.myapp.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: myapp-api
                port:
                  number: 8080

cert-manager Configuration

cert-manager automates TLS certificate issuance and renewal from Let's Encrypt.

ClusterIssuer

yaml
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: admin@myapp.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
      - http01:
          ingress:
            class: nginx

Certificate

yaml
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: myapp-tls
  namespace: myapp
spec:
  secretName: myapp-tls
  issuerRef:
    name: letsencrypt-prod
    kind: ClusterIssuer
  dnsNames:
    - myapp.com
    - admin.myapp.com
    - api.myapp.com

Install cert-manager first

cert-manager must be installed in your cluster before applying these manifests:

bash
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.0/cert-manager.yaml

# Wait for cert-manager pods to be ready
kubectl wait --for=condition=ready pod \
  -l app.kubernetes.io/instance=cert-manager \
  -n cert-manager --timeout=120s

Horizontal Pod Autoscaler

Each service includes an HPA that scales pods based on CPU and memory utilization:

yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
  name: myapp-api-hpa
  namespace: myapp
spec:
  scaleTargetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: myapp-api
  minReplicas: 2
  maxReplicas: 10
  metrics:
    - type: Resource
      resource:
        name: cpu
        target:
          type: Utilization
          averageUtilization: 70
    - type: Resource
      resource:
        name: memory
        target:
          type: Utilization
          averageUtilization: 80
  behavior:
    scaleUp:
      stabilizationWindowSeconds: 60
      policies:
        - type: Pods
          value: 2
          periodSeconds: 60
    scaleDown:
      stabilizationWindowSeconds: 300
      policies:
        - type: Pods
          value: 1
          periodSeconds: 120

ConfigMap and Secrets

Non-sensitive configuration goes in a ConfigMap:

yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
  namespace: myapp
data:
  APP_NAME: myapp
  RUST_LOG: info
  API_PORT: "8080"
  DB_HOST: myapp-postgres
  DB_PORT: "5432"
  DB_NAME: myapp
  REDIS_URL: redis://myapp-redis:6379

Sensitive values go in a Secret:

yaml
apiVersion: v1
kind: Secret
metadata:
  name: myapp-secrets
  namespace: myapp
type: Opaque
data:
  DB_USER: bXlhcHA=                    # base64 encoded
  DB_PASSWORD: c3Ryb25nLXBhc3N3b3Jk    # base64 encoded
  JWT_SECRET: ...                       # base64 encoded
  FORGE_ENCRYPTION_KEY: ...             # base64 encoded

Never commit secrets to version control

The generated secrets.yaml contains placeholder values. Replace them with real credentials encoded in base64 before applying. Consider using a secrets management tool like Sealed Secrets or External Secrets Operator for production.

bash
# Encode a value for use in secrets.yaml
echo -n "my-strong-password" | base64

Deploying to the Cluster

Apply all manifests with Kustomize

bash
# Apply everything at once
kubectl apply -k ./k8s/

Step-by-step deployment

bash
# 1. Create namespace
kubectl apply -f k8s/namespace.yaml

# 2. Apply configuration and secrets
kubectl apply -f k8s/configmap.yaml
kubectl apply -f k8s/secrets.yaml

# 3. Deploy cert-manager resources
kubectl apply -f k8s/cert-manager/

# 4. Deploy databases
kubectl apply -f k8s/postgres/
kubectl apply -f k8s/redis/

# 5. Wait for databases to be ready
kubectl wait --for=condition=ready pod \
  -l component=postgres -n myapp --timeout=120s

# 6. Deploy application services
kubectl apply -f k8s/api/
kubectl apply -f k8s/web/
kubectl apply -f k8s/admin/

# 7. Apply ingress
kubectl apply -f k8s/ingress.yaml

Verify deployment

bash
# Check all pods are running
kubectl get pods -n myapp

# Check services
kubectl get svc -n myapp

# Check ingress and TLS
kubectl get ingress -n myapp
kubectl get certificate -n myapp

# View API logs
kubectl logs -f deployment/myapp-api -n myapp

# Run migrations
kubectl exec -it deployment/myapp-api -n myapp -- \
  ./forge-cli migrate

# Seed database
kubectl exec -it deployment/myapp-api -n myapp -- \
  ./forge-cli seed

Rolling updates

bash
# Update API image
kubectl set image deployment/myapp-api \
  api=ghcr.io/myorg/myapp-api:v1.1.0 -n myapp

# Watch rollout progress
kubectl rollout status deployment/myapp-api -n myapp

# Rollback if needed
kubectl rollout undo deployment/myapp-api -n myapp

Production Considerations

Use a managed database

For production workloads, replace the PostgreSQL deployment with a managed database service (AWS RDS, Google Cloud SQL, Azure Database for PostgreSQL). Update the ConfigMap with the managed database connection details and remove the k8s/postgres/ manifests.

Resource tuning

The default resource requests and limits are starting points. Monitor your pods with kubectl top pods -n myapp and adjust based on actual usage.

ServiceCPU RequestCPU LimitMemory RequestMemory Limit
API100m500m256Mi512Mi
Web100m300m256Mi512Mi
Admin100m300m256Mi512Mi
PostgreSQL250m1000m512Mi1Gi
Redis50m200m128Mi256Mi

Monitoring

Integrate with your cluster's monitoring stack:

bash
# View resource usage
kubectl top pods -n myapp

# View HPA status
kubectl get hpa -n myapp

# View events for troubleshooting
kubectl get events -n myapp --sort-by='.lastTimestamp'

Next Steps

Released under the MIT License.