Skip to main content

Deployment

The Proxima Console backend runs in the proxima-production Kubernetes cluster, in the console-system namespace, deployed entirely via GitOps. The desired state lives in a separate argocd-infra repository (not the console repo), and ArgoCD reconciles the cluster to match it. This page covers the container image, the deployment topology, and operational considerations.

Production is Kubernetes; docker-compose is local-dev only

The single-box docker compose + SSH deploy is retired for the application tier. docker-compose / make docker-up now apply to local development only — see Quick Start. Dev/prod parity holds at the container-image level, not the orchestration level.

Docker Image

The backend uses a multi-stage Docker build to produce a minimal container image:

  1. Build stage: Uses golang:1.26 to compile the Go binary with all dependencies.
  2. Runtime stage: Uses a minimal base image (scratch-like) containing only the compiled binary and CA certificates.

The Dockerfile is located at:

infra/docker/backend/Dockerfile

Building the Image

docker build -f infra/docker/backend/Dockerfile -t registry.prxm.uz/proxima/console/backend:latest .

The CI pipeline builds and pushes a per-commit image to the registry on every push:

registry.prxm.uz/proxima/console/backend:<git-sha>

The <git-sha> (CI_COMMIT_SHA) tag is what GitOps pins — see How a Deploy Works below.

Deployment Topology

The backend is one of several ArgoCD apps under console-system (alongside frontend, docs, mcp, telegram-bot, telegram-miniapp, and an in-cluster Valkey cache). Each app's image is registry.prxm.uz/proxima/console/<svc>:<git-sha>.

 agents ──NATS TLS+JWT──► NATS (box, interim) ──► backend (k8s)
──gRPC terminal (api-console:443/h2)───► backend :9090 (k8s)

┌ k8s proxima-production · ns console-system · GitOps (ArgoCD + Argo Rollouts) ┐
│ backend(blue-green) · frontend · docs · mcp · telegram-bot · telegram-miniapp │
│ · Valkey images: registry.prxm.uz/proxima/console/<svc>:<sha> │
│ platform: External-Secrets←Vault · cert-manager · istio Gateway API │
└───┬───────────────┬─────────────────┬────────────────┬───────────────────────┘
SQL │ metrics │ vmauth :8427 │ secrets │ logs/traces (interim, box)
▼ ▼ (project 42) ▼ ▼
psql01:30034 in-cluster VM vault.prxm.uz VictoriaLogs/Tempo @ box
(external) (monitoring ns) (ESO)

Argo Rollouts (blue-green)

The backend is deployed as an Argo Rollout using a blue-green strategy rather than a plain Kubernetes Deployment:

  • A new revision is brought up as the preview Service while the current revision continues serving the active Service.
  • Argo Rollouts waits for the preview pods to pass their readiness probe (/readyz), then auto-promotes the preview to active.
  • If the preview never becomes ready, the rollout auto-rolls-back — production traffic stays on the old, healthy revision the whole time.

Health Probes

The backend exposes two health endpoints, wired directly to the pod's Kubernetes probes:

EndpointProbeBehavior
/healthzlivenessReturns 200 OK if the process is alive. A failing liveness probe restarts the pod.
/readyzreadinessReturns 200 OK only when the backend can serve traffic (PostgreSQL and NATS connected); 503 otherwise. Gates whether the pod receives traffic and whether a blue-green preview is promoted.

Configuration via External Secrets

The backend receives no plaintext secrets in its manifests. Environment variables and secrets are injected by the External-Secrets Operator (ESO), which syncs values from the in-cluster Vault (vault.prxm.uz) into Kubernetes Secrets that the Rollout consumes as env.

  • Non-secret defaults and endpoints are set as plain env on the Rollout pod spec.
  • Secret material (PROXIMA_DB_URL, PROXIMA_JWT_SECRET, NATS JWT seeds, the agent-enrollment CA, Vault AppRole credentials, etc.) flows Vault → ESO → Kubernetes Secret → pod env.

See Environment Variables for the full variable list and the production endpoints they point at.

Database Migrations

Migrations are never run by the backend application itself, and are not run from CI (CI cannot reach the cluster-internal database). Instead, they run as an ArgoCD PreSync-hook Job (console-migrate) that executes before each backend sync:

  • The PreSync Job runs golang-migrate (migrate up) against psql01 using the same image/build as the backend.
  • ArgoCD only proceeds to sync the backend Rollout after the migration Job completes successfully.
  • This keeps the schema ahead of the new code, and means a single GitOps sync atomically migrates and then rolls out the new backend.

Running migrations as a separate Job (rather than inside the app) ensures that:

  • Migration failures block the rollout instead of crash-looping the backend.
  • Multiple backend replicas never race to apply migrations.
  • Migration state can be inspected independently of the application.

How a Deploy Works

A deploy is a GitOps flow, not an SSH/docker run on a server:

  1. Merge to main — the change lands in the console repo.
  2. CI builds + pushes registry.prxm.uz/proxima/console/<svc>:<git-sha> for each service (docker:* jobs).
  3. deploy:k8s-gitops (a manual gate, main only) clones the argocd-infra repo over an SSH deploy key, bumps every console image tag under clusters/production/console-system/ to the new <git-sha>, then commits + pushes.
  4. ArgoCD detects the change and syncs: it first runs the console-migrate PreSync Job, then applies the updated backend Rollout.
  5. Argo Rollouts performs the blue-green promotion (preview → active) once /readyz passes.

Verifying a Deploy

Use kubectl / ArgoCD against the console-system namespace (there is no box to SSH into):

# Rollout status and blue-green promotion state
kubectl -n console-system argo rollouts get rollout console-backend

# Pod readiness
kubectl -n console-system get pods -l app=console-backend

# Backend logs
kubectl -n console-system logs deploy/console-backend -f

# ArgoCD sync / health status
argocd app get console-backend

A healthy deploy ends with the new revision promoted to active, all pods Ready, and the public endpoint https://api-console.prxm.uz/readyz returning 200 OK.

Rollback

Because state is GitOps-managed, roll back by reverting the image-tag bump in argocd-infra (or using ArgoCD's history) so ArgoCD syncs the previous <git-sha>. Argo Rollouts brings the prior revision back as active. If a schema change must also be reversed, run the corresponding down migration via a one-off migrate down Job — migrations are applied independently of the app, so a database rollback can be performed separately from the image rollback.