Deployment
Kubernetes configuration and secrets management.
Kubernetes Resources
Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: crm-backend
namespace: crm
spec:
replicas: 2
selector:
matchLabels:
app: crm-backend
template:
metadata:
labels:
app: crm-backend
spec:
containers:
- name: crm-backend
image: harbor.digiwedge.dev/crm/backend:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: production
- name: CRM_DATABASE_URL
valueFrom:
secretKeyRef:
name: crm-secrets
key: DATABASE_URL
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: crm-secrets
key: REDIS_URL
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: crm-secrets
key: JWT_SECRET
- name: ENCRYPTION_KEY
valueFrom:
secretKeyRef:
name: crm-secrets
key: ENCRYPTION_KEY
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Service
apiVersion: v1
kind: Service
metadata:
name: crm-backend
namespace: crm
spec:
selector:
app: crm-backend
ports:
- port: 80
targetPort: 3000
type: ClusterIP
Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: crm-backend
namespace: crm
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
ingressClassName: cilium
tls:
- hosts:
- crm-api.digiwedge.dev
secretName: crm-tls
rules:
- host: crm-api.digiwedge.dev
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: crm-backend
port:
number: 80
Worker Deployment
Separate deployment for background job processing:
apiVersion: apps/v1
kind: Deployment
metadata:
name: crm-worker
namespace: crm
spec:
replicas: 2
selector:
matchLabels:
app: crm-worker
template:
metadata:
labels:
app: crm-worker
spec:
containers:
- name: crm-worker
image: harbor.digiwedge.dev/crm/backend:latest
command: ["node", "dist/worker.js"]
env:
- name: CRM_DATABASE_URL
valueFrom:
secretKeyRef:
name: crm-secrets
key: DATABASE_URL
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: crm-secrets
key: REDIS_URL
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "512Mi"
cpu: "500m"
Infisical Secrets
Required Secrets
/crm/backend:
- CRM_DATABASE_URL # PostgreSQL connection string
- REDIS_URL # Redis connection string
- JWT_SECRET # JWT signing secret
- ENCRYPTION_KEY # AES-256 key for token encryption (64 hex chars)
/crm/social:
- FACEBOOK_APP_ID # Facebook App ID
- FACEBOOK_APP_SECRET # Facebook App Secret
- INSTAGRAM_APP_ID # Instagram App ID
- INSTAGRAM_APP_SECRET # Instagram App Secret
- TWITTER_CLIENT_ID # Twitter Client ID
- TWITTER_CLIENT_SECRET # Twitter Client Secret
External Secret Operator
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: crm-secrets
namespace: crm
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: infisical-store
target:
name: crm-secrets
creationPolicy: Owner
data:
- secretKey: DATABASE_URL
remoteRef:
key: crm/backend
property: CRM_DATABASE_URL
- secretKey: REDIS_URL
remoteRef:
key: crm/backend
property: REDIS_URL
- secretKey: JWT_SECRET
remoteRef:
key: crm/backend
property: JWT_SECRET
- secretKey: ENCRYPTION_KEY
remoteRef:
key: crm/backend
property: ENCRYPTION_KEY
Database Migration
Migration Job
apiVersion: batch/v1
kind: Job
metadata:
name: crm-migrate
namespace: crm
spec:
template:
spec:
containers:
- name: migrate
image: harbor.digiwedge.dev/crm/backend:latest
command: ["npx", "prisma", "migrate", "deploy"]
env:
- name: CRM_DATABASE_URL
valueFrom:
secretKeyRef:
name: crm-secrets
key: DATABASE_URL
restartPolicy: Never
backoffLimit: 3
Manual Deployment
- Build and push using
tools/scripts/containers/push-versioned.sh. - Pin the digest in the app
kustomization.yaml(see docs/internal/release/versioning-and-images.md). - Sync via ArgoCD.
- Run migrations:
kubectl apply -f k8s/migrate-job.yaml
kubectl wait --for=condition=complete job/crm-migrate
Monitoring
Prometheus ServiceMonitor
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: crm-backend
namespace: crm
spec:
selector:
matchLabels:
app: crm-backend
endpoints:
- port: http
path: /metrics
interval: 30s
Grafana Dashboard
Key metrics to visualize:
- Request rate and latency
- Event processing rate
- Queue depth
- Database connection pool
- Error rates