Skip to content

Self-host dengan Kubernetes

Cocok untuk team yang sudah punya K8s cluster (GKE, EKS, AKS, DigitalOcean, atau self-managed).

Prerequisites

  • Cluster Kubernetes 1.28+
  • kubectl configured
  • Ingress controller (nginx-ingress, traefik, atau cloud-managed)
  • cert-manager (untuk TLS otomatis) — opsional tapi direkomendasikan
  • PostgreSQL 15+ (bisa in-cluster pakai operator, atau managed RDS/Cloud SQL)

Quick deploy

Terminal window
# Clone public distribution
git clone https://github.com/RenzyArmstrong/Calvery-Vault.git
cd Calvery-Vault/deploy/k8s
# Generate secrets
kubectl create namespace cvsm
kubectl -n cvsm create secret generic cvsm-env \
--from-literal=JWT_SECRET=$(openssl rand -hex 32) \
--from-literal=ENCRYPTION_KEY=$(openssl rand -hex 32) \
--from-literal=DATABASE_URL='postgres://cvsm:PASS@db:5432/cvsm' \
--from-literal=SMTP_PASSWORD='re_xxxxxxxx'
# Apply manifests
kubectl apply -f postgres.yaml # skip kalau pakai managed DB
kubectl apply -f api.yaml
kubectl apply -f ingress.yaml

Architecture

┌─────────────────────────────────────────────┐
│ Ingress (nginx/traefik) + cert-manager │
│ vault.company.com │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Service: cvsm-api (ClusterIP) │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ Deployment: cvsm-api (2-3 replicas) │
│ - containerPort: 8080 │
│ - readinessProbe: /health │
│ - resources: 100m CPU, 128Mi RAM │
└────────────┬────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ StatefulSet: postgres (1 replica) │
│ - PersistentVolumeClaim 10Gi │
└─────────────────────────────────────────────┘

Example manifests

Deployment + Service

api.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: cvsm-api
namespace: cvsm
spec:
replicas: 2
selector:
matchLabels: { app: cvsm-api }
template:
metadata:
labels: { app: cvsm-api }
spec:
containers:
- name: api
image: ghcr.io/renzyarmstrong/cvsm-api:v0.1.0
ports:
- containerPort: 8080
envFrom:
- secretRef:
name: cvsm-env
env:
- name: APP_URL
value: "https://vault.company.com"
- name: ALLOWED_ORIGINS
value: "https://vault.company.com"
resources:
requests: { cpu: 100m, memory: 128Mi }
limits: { cpu: 500m, memory: 512Mi }
readinessProbe:
httpGet: { path: /health, port: 8080 }
initialDelaySeconds: 5
livenessProbe:
httpGet: { path: /health, port: 8080 }
periodSeconds: 30
---
apiVersion: v1
kind: Service
metadata:
name: cvsm-api
namespace: cvsm
spec:
selector: { app: cvsm-api }
ports:
- port: 80
targetPort: 8080

Ingress dengan cert-manager

ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: cvsm-api
namespace: cvsm
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/proxy-body-size: "1m"
spec:
ingressClassName: nginx
tls:
- hosts: [vault.company.com]
secretName: cvsm-tls
rules:
- host: vault.company.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: cvsm-api
port: { number: 80 }

Run migrations

Terminal window
# Apply migration dari dalam pod
POD=$(kubectl -n cvsm get pod -l app=postgres -o name | head -1)
kubectl -n cvsm exec -i $POD -- psql -U cvsm -d cvsm < migrations/009_oauth.sql

Atau pakai Job sekali-pakai untuk migrations:

apiVersion: batch/v1
kind: Job
metadata:
name: cvsm-migrate
namespace: cvsm
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: postgres:16-alpine
command: ["sh", "-c"]
args:
- |
for f in /migrations/*.sql; do
echo "Applying $f"
psql $DATABASE_URL < $f
done
env:
- name: DATABASE_URL
valueFrom: { secretKeyRef: { name: cvsm-env, key: DATABASE_URL } }
volumeMounts:
- name: migrations
mountPath: /migrations
volumes:
- name: migrations
configMap: { name: cvsm-migrations }

Scaling

Horizontal

apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: cvsm-api
namespace: cvsm
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: cvsm-api
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target: { type: Utilization, averageUtilization: 70 }

Database

Managed database (Cloud SQL, RDS, Neon, Supabase) direkomendasikan untuk production. Set DATABASE_URL di secret → Pod otomatis konek.

Helm chart

Helm chart resmi coming v0.3. Sementara, pakai manifests manual di github.com/RenzyArmstrong/Calvery-Vault/tree/main/deploy/k8s.