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
forge deploy:k8s \
--env=production \
--ssl=letsencrypt \
--email=admin@example.comCommand Options
| Option | Type | Default | Description |
|---|---|---|---|
--env | string | production | Target environment: staging or production |
--domain | string | from forge.yaml | Production domain (e.g., myapp.com) |
--ssl | string | letsencrypt | SSL method: letsencrypt, custom, or none |
--email | string | required for LE | Email for Let's Encrypt notifications |
--replicas | number | 2 | Number of pod replicas per service |
--namespace | string | {app-name} | Kubernetes namespace |
--registry | string | docker.io | Container image registry |
--output | string | ./k8s | Output directory for manifests |
Examples
forge deploy:k8s \
--env=production \
--domain=myapp.com \
--ssl=letsencrypt \
--email=admin@myapp.com \
--replicas=3 \
--namespace=myapp-prod \
--registry=ghcr.io/myorgforge deploy:k8s \
--env=staging \
--domain=staging.myapp.com \
--ssl=letsencrypt \
--email=admin@myapp.com \
--replicas=1 \
--namespace=myapp-stagingforge deploy:k8s \
--env=production \
--output=./infrastructure/k8s/productionGenerated 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 deploymentAPI Deployment
The API deployment includes health probes, resource limits, and environment configuration:
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: 2Health 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:
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: 8080cert-manager Configuration
cert-manager automates TLS certificate issuance and renewal from Let's Encrypt.
ClusterIssuer
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: nginxCertificate
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.comInstall cert-manager first
cert-manager must be installed in your cluster before applying these manifests:
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=120sHorizontal Pod Autoscaler
Each service includes an HPA that scales pods based on CPU and memory utilization:
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: 120ConfigMap and Secrets
Non-sensitive configuration goes in a ConfigMap:
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:6379Sensitive values go in a Secret:
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 encodedNever 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.
# Encode a value for use in secrets.yaml
echo -n "my-strong-password" | base64Deploying to the Cluster
Apply all manifests with Kustomize
# Apply everything at once
kubectl apply -k ./k8s/Step-by-step deployment
# 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.yamlVerify deployment
# 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 seedRolling updates
# 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 myappProduction 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.
| Service | CPU Request | CPU Limit | Memory Request | Memory Limit |
|---|---|---|---|---|
| API | 100m | 500m | 256Mi | 512Mi |
| Web | 100m | 300m | 256Mi | 512Mi |
| Admin | 100m | 300m | 256Mi | 512Mi |
| PostgreSQL | 250m | 1000m | 512Mi | 1Gi |
| Redis | 50m | 200m | 128Mi | 256Mi |
Monitoring
Integrate with your cluster's monitoring stack:
# 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
- Docker Compose Deployment -- Simpler single-server deployment
- SSH Deployment -- Deploy without containers
- SSL Certificates -- Detailed SSL configuration options