Deployment¶
Nexivo runs on a single-node MicroK8s cluster per environment. All platform services are deployed via Helm. Secrets are applied manually with kubectl. Databases are self-hosted.
Environments¶
| Environment | Purpose | Kubeconfig |
|---|---|---|
| dev | Development & integration testing | ~/dok8s/dev_tunnel.yaml |
| uat | User acceptance testing | ~/dok8s/uat.yaml |
| live | Production (UK) | ~/dok8s/live-uk-kubeconfig.yaml |
# dev
export KUBECONFIG=~/dok8s/dev_tunnel.yaml
# uat
export KUBECONFIG=~/dok8s/uat.yaml
# live
export KUBECONFIG=~/dok8s/live-uk-kubeconfig.yaml
Useful aliases for your shell profile:
alias kdev='KUBECONFIG=~/dok8s/dev_tunnel.yaml kubectl'
alias kuat='KUBECONFIG=~/dok8s/uat.yaml kubectl'
alias klive='KUBECONFIG=~/dok8s/live-uk-kubeconfig.yaml kubectl'
Cluster Layout¶
Services are split across two primary namespaces:
| Namespace | Contents |
|---|---|
nexivo |
All platform microservices, frontends, datastores |
livekit |
LiveKit server, SIP gateway, Kamailio SBC, Atlas, Herald, KEDA |
keycloak |
Keycloak authentication service |
ingress-nginx |
NGINX ingress controller |
cert-manager |
TLS certificate management |
cnpg-system |
CloudNativePG operator |
minio |
MinIO object storage |
monitoring |
kube-state-metrics |
Deployment Diagram¶
graph TB
Internet((Internet))
subgraph Ingress["ingress-nginx"]
NG[NGINX Ingress\nTLS termination]
end
subgraph KeycloakNS["keycloak"]
KC[Keycloak\nauth.nexivo.corecognitics.cc]
end
subgraph LiveKitNS["livekit namespace"]
LK_SRV[livekit-server\nvoice.nexivo.corecognitics.cc]
LK_SIP[livekit-sip-gateway\nsip.nexivo.corecognitics.cc]
KAMA[kamailio-sbc\nSIP Session Border Controller]
ATLAS[livekit-multi-agent-service\nAtlas · 2–10 pods KEDA]
MEDIAB[livekit-communication-service\nMedia Bot · 8 pods]
HERALD[text-multi-agent-service\nHerald · 1 pod]
OE[orchestrator-engine\n3 pods]
EGRESS[livekit-egress\n1–6 pods KEDA]
LK_VK[(livekit-valkey\n1 primary + 3 replicas)]
end
subgraph NexivoNS["nexivo namespace"]
subgraph Frontends["Frontends"]
PORTAL[nexivo-portal\napp.nexivo.corecognitics.cc]
ADMIN[nexivo-admin\nadmin.nexivo.corecognitics.cc]
WIDGET[nexivo-widget\n5 pods]
INSIGHTS_FE[call-insights-frontend\ninsights.nexivo.corecognitics.cc]
REPORTS[nexivo-reports\nreports.nexivo.corecognitics.cc]
DASHBOARD[nexivo-dashboard\nops.nexivo.corecognitics.cc]
CAMPAIGN_HUB[campaign-hub\ncampaign.nexivo.corecognitics.cc]
end
subgraph CoreSvcs["Core Services"]
CS[call-service · 3]
WAS[whatsapp-service · 1]
BL[billing-service · 3]
AC[app-connect · 1]
COMPASS[ai-provisioning-service\nCompass · 1]
MCP_SRV[app-mcp-server · 1]
TMS[tool-management-service · 2]
NOTIF[notification-service · 3]
end
subgraph CommSvcs["Communication Services"]
CHAT[chat-service · 3]
EMAIL[email-service · 3]
CAMP[campaign-service · 3]
SCHED[communication-scheduler · 3]
MSG[message-service · 1]
CONTACT[contact-service · 3]
PHONE[phone-number-service · 3]
TRANSCRIPT[transcript-service · 3]
INSIGHTS_SVC[call-insights-service · 1]
WIDGET_SVC[communication-widget-service · 3]
end
subgraph DataStores["Datastores"]
PG[(nexivo-db-cluster\nCloudNativePG · 2 nodes · 10 Gi each)]
REDIS[(nexivo-redis · 8 Gi)]
VALKEY[(nexivo-valkey\n1 primary + 3 replicas · 8 Gi each)]
QDRANT[(nexivo-qdrant\nVector DB · 10 Gi)]
CH[(ClickHouse\nReporting · 2 shards · 20 Gi each)]
end
end
Internet --> NG
NG --> PORTAL & ADMIN & INSIGHTS_FE & REPORTS & DASHBOARD & CAMPAIGN_HUB
NG --> KC
NG --> LK_SRV & LK_SIP
LK_SIP --> KAMA
LK_SRV --> ATLAS & MEDIAB
CS --> LK_SRV
WAS --> HERALD
CoreSvcs --> PG
CommSvcs --> PG
CoreSvcs --> REDIS
CoreSvcs --> VALKEY
QDRANT -.->|vector search| COMPASS
CH -.->|analytics| INSIGHTS_SVC
Services by Namespace¶
nexivo namespace — Platform Services¶
Core¶
| Deployment | Replicas | Image | Ingress |
|---|---|---|---|
call-service |
3 | communication-services/call-service |
— |
whatsapp-service |
1 | communication-services/whatsapp-service |
— |
billing-service |
3 | communication-services/billing-service |
— |
app-connect |
1 | service-gateway/app-connect |
— |
ai-provisioning-service (Compass) |
1 | intento/tenantator/compass |
— |
app-mcp-server |
1 | intento/mcp-servers/intento-rest-api-services |
— |
tool-management-service |
2 | intento/tool-management-service |
— |
notification-service |
3 | communication-services/notification-service |
— |
conductor |
1 | common-services/conductor |
— |
communication-ai-service |
2 | communication-services/ai-service |
— |
feedback-service |
2 | communication-services/feedback-service |
feedback.nexivo.corecognitics.cc |
opportunity-service |
1 | communication-services/opportunity-service |
— |
Communication¶
| Deployment | Replicas | Image |
|---|---|---|
chat-service |
3 | communication-services/chat-service |
email-service |
3 | communication-services/email-service |
campaign-service |
3 | campaign-services/campaign-service |
communication-scheduler |
3 | communication-services/communication-scheduler |
message-service |
1 | communication-services/message-service |
contact-service |
3 | communication-services/contact-service |
contact-status-service |
2 | communication-services/contact-status-service |
phone-number-service |
3 | communication-services/phone-number-service |
transcript-service |
3 | communication-services/transcript-service |
call-insights-service |
1 | communication-services/call-insights-service |
communication-widget-service |
3 | communication-services/widget-service |
hls-s3-proxy |
2 | intento/hls-s3-proxy |
Frontends¶
| Deployment | Replicas | URL |
|---|---|---|
nexivo-portal |
1 | app.nexivo.corecognitics.cc |
nexivo-admin |
1 | admin.nexivo.corecognitics.cc |
nexivo-widget |
5 | — |
nexivo-dashboard |
1 | ops.nexivo.corecognitics.cc |
nexivo-reports |
1 | reports.nexivo.corecognitics.cc |
call-insights-frontend |
1 | insights.nexivo.corecognitics.cc |
campaign-hub |
1 | campaign.nexivo.corecognitics.cc |
Datastores (nexivo namespace)¶
| Resource | Type | Size |
|---|---|---|
nexivo-db-cluster |
CloudNativePG — 2 instances (1 primary, 1 standby) | 10 Gi each |
nexivo-redis-master |
Redis StatefulSet | 8 Gi |
nexivo-valkey-primary + 3 replicas |
Valkey StatefulSet | 8 Gi each |
nexivo-qdrant |
Qdrant vector DB | 10 Gi |
chi-clickhouse-reporting |
ClickHouse — 2 shards + 3 Keeper nodes | 20 Gi each shard |
livekit namespace — Voice & Text AI¶
| Deployment | Replicas | Notes |
|---|---|---|
livekit-server |
1 | voice.nexivo.corecognitics.cc |
livekit-sip-gateway |
1 | sip.nexivo.corecognitics.cc |
kamailio-sbc |
1 | SIP session border controller |
livekit-multi-agent-service (Atlas) |
2–10 | KEDA autoscale · mem req 3.5 Gi |
livekit-communication-service (Media Bot) |
8 | Fixed replica count |
text-multi-agent-service (Herald) |
1 | mem req 1 Gi |
orchestrator-engine |
3 | AI orchestration layer |
livekit-egress |
1–6 | KEDA autoscale · mem req 2 Gi |
livekit-valkey-primary + 3 replicas |
Valkey StatefulSet | 8 Gi each |
KEDA autoscaling is active in the livekit namespace:
| ScaledObject | Min | Max | Trigger |
|---|---|---|---|
livekit-multi-agent-service |
2 | 10 | Active |
livekit-egress |
1 | 6 | — |
CI/CD Pipeline¶
Every service follows the same pattern (GitLab CI):
- build — Docker image built, pushed to
registry.freston.iotagged with$CI_COMMIT_SHORT_SHA - deploy —
helm upgrade --installagainst target cluster, thenkubectl rollout status - notify — Teams Adaptive Card on success or failure
Deploy only runs on main. Feature branches build the image but do not deploy.
Deploying a Service¶
Standard Helm deploy¶
export KUBECONFIG=~/dok8s/uat.yaml # or dev / live
helm upgrade --install <release-name> ./helm \
--namespace nexivo \
--set image.tag=<commit-sha> \
--values helm/values.<env>.yaml
Roll a new image manually¶
kubectl set image deployment/<name> <container>=registry.freston.io/<path>/<service>:<sha> -n nexivo
kubectl rollout status deployment/<name> -n nexivo --timeout=120s
livekit namespace services¶
kubectl set image deployment/livekit-multi-agent-service \
livekit-multi-agent-service=registry.freston.io/intento/tenantator/atlas:<sha> \
-n livekit
Secrets¶
Secrets are applied manually with kubectl and are not stored in Git.
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: <secret-name>
namespace: nexivo # or livekit
type: Opaque
stringData:
KEY: "value"
EOF
# List secrets
kubectl get secrets -n nexivo
kubectl get secrets -n livekit
# Update a single key
kubectl patch secret <name> -n nexivo \
--type='json' \
-p='[{"op":"replace","path":"/data/KEY","value":"'$(echo -n "newvalue" | base64)'"}]'
Warning
Update secrets before rolling out a deployment that depends on them. Pods pick up secret changes only on restart.
Database¶
CloudNativePG (PostgreSQL)¶
The nexivo-db-cluster CloudNativePG cluster runs in the nexivo namespace with 2 instances (1 primary, 1 standby replica).
# Check cluster health
kubectl get clusters.postgresql.cnpg.io -n nexivo
# Connect to primary (port-forward)
kubectl port-forward svc/nexivo-db-cluster-rw 5432:5432 -n nexivo
# psql -h localhost -U <user> -d <dbname>
# Connect to read-only replica
kubectl port-forward svc/nexivo-db-cluster-ro 5432:5432 -n nexivo
ClickHouse (Reporting)¶
ClickHouse runs as a 2-shard cluster with 3 ClickHouse Keeper nodes for coordination, managed by the Altinity ClickHouse Operator.
kubectl get chi -n nexivo # list ClickHouse installations
kubectl get pods -n nexivo -l app=clickhouse
Qdrant (Vector DB)¶
kubectl get pods -n nexivo -l app=nexivo-qdrant
kubectl port-forward svc/nexivo-qdrant 6333:6333 -n nexivo
Rollback¶
# Roll back to a specific image
kubectl set image deployment/<name> \
<container>=registry.freston.io/<path>/<service>:<previous-sha> \
-n nexivo
# Roll back to previous Kubernetes revision
kubectl rollout undo deployment/<name> -n nexivo
# Check rollout history
kubectl rollout history deployment/<name> -n nexivo
Previous image SHAs are in GitLab under Registry → Container Registry for each project, or from pipeline history.
Useful Commands¶
# All pods across both main namespaces
kuat get pods -n nexivo
kuat get pods -n livekit
# Tail logs
kuat logs deployment/call-service -n nexivo --tail=100 -f
kuat logs deployment/livekit-multi-agent-service -n livekit --tail=100 -f
# Check ingresses
kuat get ingress -n nexivo
kuat get ingress -n livekit
# KEDA scaling status
kuat get scaledobject -n livekit
# CloudNativePG cluster health
kuat get clusters.postgresql.cnpg.io -n nexivo