Skip to main content

Production Deployment (Kubernetes)

This is the canonical reference for how Proxima Console is deployed in production.

The application tier runs in the proxima-production Kubernetes cluster, in the console-system namespace, and is deployed entirely through GitOps: the desired state lives in a separate argocd-infra repository (not the console repo), and ArgoCD continuously reconciles the cluster to match it. No one deploys by SSHing to a server.

Historical note. Earlier releases ran the application tier on a single box (89.167.8.247) as systemd-managed Docker containers, deployed over SSH with docker-compose. That model is retired for application workloads. Docker Compose now applies to local development only — see Docker. "Dev/prod parity" still holds, but at the container-image level, not the orchestration level.

Topology at a glance

 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 @ 89.167.8.247
(external) (monitoring ns) (ESO)

public (Cloudflare → istio gateway): api|app|docs|mcp|tg-console.prxm.uz
CI: GitLab build → registry → deploy:k8s-gitops (SSH→argocd-infra) → ArgoCD → Rollouts

Cluster & namespace

PropertyValue
Clusterproxima-production
Namespaceconsole-system
GitOps repoargocd-infra (separate from console)
Manifests pathclusters/production/console-system/
ReconcilerArgoCD
Progressive deliveryArgo Rollouts (blue-green, backend)

Components

Each Console service is an ArgoCD application under console-system, running the image registry.prxm.uz/proxima/console/<svc>:<git-sha>. The deployed tag is the Git SHA of the commit that built the image, so the running version is always traceable back to a single commit.

ServiceImageNotes
backend…/console/backend:<sha>REST API (:8080) + gRPC terminal/SSH (:9090). Argo Rollouts blue-green.
frontend…/console/frontend:<sha>React SPA served by nginx
docs…/console/docs:<sha>This Docusaurus documentation site
mcp…/console/mcp:<sha>MCP server for AI chat
telegram-bot…/console/telegram-bot:<sha>Telegram bot
telegram-miniapp…/console/telegram-miniapp:<sha>Telegram mini-app
Valkey(in-cluster)Redis-compatible cache (sessions, rate limits, MFA, access resolution)

Backend blue-green rollout

The backend is deployed as an Argo Rollouts Rollout (not a plain Deployment) using a blue-green strategy:

  • Two Services front the pods: console-backend-active (live traffic) and console-backend-preview (the new version under test).
  • On a new image, Rollouts brings up the new ReplicaSet behind the preview Service, waits for it to pass readiness, then auto-promotes it to active.
  • If the preview never becomes healthy, Rollouts auto-rolls-back and the active version keeps serving traffic, so a bad image never reaches users.

Public traffic reaches the backend through an istio HTTPRoute that targets the console-backend-active Service.

Database migrations (ArgoCD PreSync hook)

Schema migrations run in the cluster, not from CI, because CI has no route to the cluster-internal database. Each backend sync runs a migration Job as an ArgoCD PreSync hook (console-migrate) that executes golang-migrate migrate up before the new backend pods start. ArgoCD waits for the hook Job to succeed; if migrations fail, the sync stops and the new backend version is not rolled out.

This guarantees the database schema is always migrated ahead of the code that depends on it.

Data plane

The k8s application consumes the following backing services. They are attached resources configured via environment / secrets (Twelve-Factor), not embedded in the app.

ConcernBacking serviceEndpoint / detail
Relational DBPostgreSQL — external managed psql0110.10.3.11:30034 (PG 16), pgBackRest backups
MetricsIn-cluster VictoriaMetrics cluster (monitoring ns) via vmauth-proxyvmauth-proxy.monitoring.svc.cluster.local:8427
CacheIn-cluster ValkeyRedis-compatible
MessagingNATS (agent telemetry + commands, TLS + JWT)nats://nats-client-console.prxm.uz:4222on the box (interim)
LogsVictoriaLogs:9428 on the box — interim
TracesTempo:4317 (OTLP) on the box — interim
SecretsVault + External-Secrets Operator (ESO)vault.prxm.uz (in-cluster)
AI chatLiteLLMlitellm.prxm.uz (central)

Metrics tenancy (vmauth, project 42)

Console is multi-tenant in VictoriaMetrics — one VM accountID per client — and is isolated in projectID 42. The backend authenticates to vmauth-proxy as the vmuser-console VMUser, so it writes and reads tenant <accountID>:42. This keeps Console's series separate from the shared cluster's :0 tenants.

Secrets (Vault + External-Secrets)

There are no secrets in the manifests. The External-Secrets Operator projects values out of Vault (vault.prxm.uz) into Kubernetes Secrets via SecretStore / ExternalSecret resources. Sensitive material delivered this way includes the NATS JWT seeds and the agent-enrollment CA.

Interim: NATS, VictoriaLogs, Tempo on the box

NATS, VictoriaLogs, and Tempo still run on the old box (89.167.8.247), firewalled so only the cluster egress can reach them. This is an interim arrangement:

  • NATS moves to a dedicated VM in Phase 4.
  • VictoriaLogs and Tempo move to dedicated VMs in Phase L.

After those migrations the box is decommissioned. Everything else — the application tier, PostgreSQL, metrics, cache, secrets, and TLS — is already fully migrated off the box.

Networking & ingress

  • TLS certificates are issued and renewed by cert-manager.
  • Routing uses the istio Gateway API through the shared istio-gateway; REST endpoints are exposed as HTTPRoute resources.
  • Public hostnames are Cloudflare-proxied → istio gateway:
HostnameServes
api-console.prxm.uzBackend API (REST :8080, gRPC terminal :9090)
app-console.prxm.uzFrontend SPA
docs-console.prxm.uzThis documentation site
mcp-console.prxm.uzMCP server
tg-console.prxm.uzTelegram mini-app

gRPC terminal on the same host

The pc CLI's gRPC (terminal / SSH proxy) shares api-console.prxm.uz:443 over HTTP/2 with the REST API. A longest-prefix HTTPRoute rule sends the proxima.terminal.v1.TerminalService path to the backend's gRPC port :9090, while everything else falls through to REST on :8080. (This requires Cloudflare gRPC to be enabled for the zone.)

How a change reaches production

  1. CI builds and pushes registry.prxm.uz/proxima/console/<svc>:<git-sha>.
  2. The manual deploy:k8s-gitops job (main only) bumps the image tags in argocd-infra over an SSH deploy key and pushes.
  3. ArgoCD detects the change and syncs console-system.
  4. The console-migrate PreSync hook runs migrate up.
  5. Argo Rollouts performs the backend blue-green promotion (or auto-rollback).

See CI/CD Pipeline for the full pipeline narrative.