Kubernetes Deployment Guide — HenKaiPan ASPM
This guide covers deploying HenKaiPan ASPM to a Kubernetes cluster.
Prerequisites
- Kubernetes cluster (kind, minikube, EKS, GKE, AKS, etc.)
- kubectl configured to access your cluster
- StorageClass configured for PersistentVolumes
- (Optional) NGINX Ingress Controller
- (Optional) cert-manager for TLS
Quick Deployment (Testing Only)
For a quick test deployment:
# Apply all-in-one manifest
kubectl apply -f kubernetes/all-in-one.yaml
# Check status
kubectl get pods -n henkaipan
kubectl get services -n henkaipan
# Access the API
kubectl port-forward svc/henkaipan-api 8080:8080 -n henkaipan
# Open http://localhost:8080Default credentials: Admin password is auto-generated (random UUID v4). Check the API pod logs or the henkaipan-secret for the initial password.
Production Deployment
1. Configure Secrets
Edit kubernetes/secrets.yaml and set:
stringData:
JWT_SECRET: "<random-32-char-string>"
SECRET_ENCRYPTION_KEY: "<random-64-char-hex-string>"
ADMIN_PASS: "<your-admin-password>"Generate secure values:
# JWT_SECRET
openssl rand -base64 32
# SECRET_ENCRYPTION_KEY
openssl rand -hex 32Apply secrets:
kubectl apply -f kubernetes/secrets.yaml2. Configure ConfigMap
Edit kubernetes/configmap.yaml to customize:
CORS_ALLOWED_ORIGINS— Your frontend domainsCOOKIE_SECURE— Set to"true"behind HTTPS- AI provider settings (Ollama, OpenRouter, Cloudflare)
Apply:
kubectl apply -f kubernetes/configmap.yaml3. Deploy Infrastructure
# Namespace
kubectl apply -f kubernetes/namespace.yaml
# PostgreSQL
kubectl apply -f kubernetes/postgres.yaml
# Redis
kubectl apply -f kubernetes/redis.yaml4. Deploy Application
# API
kubectl apply -f kubernetes/api.yaml
# Worker
kubectl apply -f kubernetes/worker.yaml5. Configure Ingress (Optional)
Edit kubernetes/ingress.yaml:
spec:
tls:
- hosts:
- aspm.yourcompany.com # Change this
secretName: henkaipan-tls
rules:
- host: aspm.yourcompany.com # Change thisApply:
kubectl apply -f kubernetes/ingress.yamlAccessing the Application
Via Port Forward (Testing)
kubectl port-forward svc/henkaipan-api 8080:8080 -n henkaipan
# Open http://localhost:8080Via Ingress (Production)
kubectl get ingress -n henkaipan
# Access https://aspm.yourcompany.comVia NodePort
If using the all-in-one manifest:
kubectl get nodes -o wide
# Access http://<node-ip>:30080Monitoring
Prometheus Metrics
Metrics are exposed on port 9090:
kubectl port-forward svc/henkaipan-api 9090:9090 -n henkaipan
# Open http://localhost:9090/metricsHealth Check
kubectl port-forward svc/henkaipan-api 8080:8080 -n henkaipan
curl http://localhost:8080/api/healthLogs
# API logs
kubectl logs -n henkaipan -l app=henkaipan-api -f
# Worker logs
kubectl logs -n henkaipan -l app=henkaipan-worker -f
# PostgreSQL logs
kubectl logs -n henkaipan -l app=postgres -fScaling
Horizontal Scaling (API)
kubectl scale deployment api -n henkaipan --replicas=3Note: Worker should typically remain at 1 replica to avoid duplicate job processing.
API replicas are interchangeable — no sticky sessions required. All shared state lives in PostgreSQL + Redis, not in the API process:
- Auth: API tokens are validated per request against Postgres (bcrypt); JWT token versions are read from the database.
- Rate limiting: Redis-backed token bucket (atomic Lua script), shared by all replicas. Note: if Redis is unreachable the limiter fails closed and requests are denied.
- Real-time updates (SSE): fanned out over the Redis
aspm:eventschannel, so events reach clients regardless of which replica they are connected to. An SSE stream is a long-lived connection pinned to one replica, but that is not state — on reconnect the client lands on any replica. - MCP (
/v1/mcp): fully stateless — noMCP-Session-Idis ever minted. Every JSON-RPC request carries its ownX-API-Keyand protocol version, so any replica can serve any request. - No in-process caches: the API keeps no local caches.
- Migrations run at API startup under Postgres advisory locks, so several replicas starting at once don't conflict.
Requirement: all replicas must share the same PostgreSQL, Redis, JWT_SECRET,
and SECRET_ENCRYPTION_KEY.
Resource Limits
Adjust resource requests/limits in the deployment manifests based on your workload:
- API: 512Mi-2Gi memory, 250m-1000m CPU
- Worker: 1-4Gi memory, 500m-2000m CPU (scanner execution is memory-intensive)
- PostgreSQL: 512Mi-2Gi memory, 250m-1000m CPU
- Redis: 256-512Mi memory, 100-500m CPU
Backup & Restore
Database Backup
# Create backup
kubectl exec -n henkaipan $(kubectl get pod -n henkaipan -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
-- pg_dump -U aspm aspm > backup.sql
# Restore
kubectl exec -n henkaipan $(kubectl get pod -n henkaipan -l app=postgres -o jsonpath='{.items[0].metadata.name}') \
-- psql -U aspm aspm < backup.sqlFor automated backups, consider using tools like Velero or Stash.
Updating
# Pull latest images
kubectl rollout restart deployment/api -n henkaipan
kubectl rollout restart deployment/worker -n henkaipan
# Check status
kubectl rollout status deployment/api -n henkaipan
kubectl rollout status deployment/worker -n henkaipanTroubleshooting
Worker cannot run scans
Verify scanner binaries are available:
kubectl exec -n henkaipan $(kubectl get pod -n henkaipan -l app=henkaipan-worker -o jsonpath='{.items[0].metadata.name}') \
-- which semgrep trivy gitleaksIf this fails, ensure the worker image includes all scanner binaries.
Database connection errors
Check PostgreSQL is running:
kubectl get pods -n henkaipan -l app=postgres
kubectl logs -n henkaipan -l app=postgresVerify connection string in ConfigMap:
kubectl get configmap henkaipan-config -n henkaipan -o yamlAPI crashes on startup
Check logs:
kubectl logs -n henkaipan -l app=henkaipan-apiCommon causes:
- Missing required env vars (
DATABASE_URL,JWT_SECRET,SECRET_ENCRYPTION_KEY) - Database not ready (check PostgreSQL health)
- Invalid configuration values
Security Considerations
- Run as non-root: All deployments are configured with
runAsNonRoot: true - Drop capabilities:
capabilities.drop: ALLis set - Read-only root filesystem: Consider adding for additional hardening
- Network policies: Implement network policies to restrict pod-to-pod communication
- Secrets management: Consider using external secret managers (HashiCorp Vault, AWS Secrets Manager)
- Pod Security Standards: Ensure your cluster enforces appropriate PSS levels
Architecture
┌─────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ Ingress │ │ PostgreSQL│ │
│ │ (optional) │ │ (Stateful)│ │
│ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │
│ ┌──────▼──────┐ ┌──────▼──────┐ │
│ │ API │◀───┤ Redis │ │
│ │ (Port 8080)│ │ (Cache/QL) │ │
│ └──────┬──────┘ └─────────────┘ │
│ │ │
│ ┌──────▼──────┐ │
│ │ Worker │ │
│ │ (No ports) │ (embedded scanner binaries) │
│ └─────────────┘ │
└─────────────────────────────────────────────────────┘File Reference
| File | Purpose |
|---|---|
kubernetes/namespace.yaml |
Namespace definition |
kubernetes/configmap.yaml |
Non-sensitive configuration |
kubernetes/secrets.yaml |
Sensitive credentials |
kubernetes/postgres.yaml |
PostgreSQL deployment + PVC + service |
kubernetes/redis.yaml |
Redis deployment + service |
kubernetes/api.yaml |
API deployment + service |
kubernetes/worker.yaml |
Worker deployment (no service) |
kubernetes/ingress.yaml |
Ingress with TLS (optional) |
kubernetes/all-in-one.yaml |
Single manifest for testing |
Support
- Documentation: https://henkaipan.dyallab.com.ar/docs/
- GitHub Issues: Report bugs or feature requests