Skip to main content

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

  1. Build and push using tools/scripts/containers/push-versioned.sh.
  2. Pin the digest in the app kustomization.yaml (see docs/internal/release/versioning-and-images.md).
  3. Sync via ArgoCD.
  4. 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