Skip to content

Onboard an app

Onboarding an app onto Ultron Infra (the ultron node) means wiring it through the platform’s standard GitOps pipeline: build an image, push it to GHCR, point an Argo CD Application at a workloads/<app>/ folder, and let Argo Rollouts ship it behind a Prometheus gate. Nothing is applied by hand — you commit YAML and Argo CD reconciles.

This page is the reusable recipe. Every value below is a placeholder: <app> is your app/namespace, <owner> your GitHub owner, <domain> / <auth-domain> your hostnames. Swap them and the same steps onboard any app. See /examples/ for a filled-in worked example.

flowchart LR
  Code([push to app repo]) --> CI[GitHub Actions<br/>buildx linux/arm64]
  CI --> Img[/ghcr.io/owner/app:sha-XXXX/]
  Img --> Bump[bump tag in<br/>workloads/app/rollout.yaml]
  Bump --> Sync[Argo CD sync]
  Sync --> Pre[migrations Job<br/>PreSync hook]
  Pre --> Roll[Rollout canary<br/>25 → 50 → 75 → 100%]
  Roll --> Gate{Prometheus<br/>success-rate ≥ 95%?}
  Gate -->|yes| Prod[promoted]
  Gate -->|no| RB[auto-rollback]

The image tag in Git is the deploy trigger: CI produces sha-<short>, you bump that tag in rollout.yaml, push, and Argo CD does the rest. Migrations run before new pods start (a PreSync hook); the canary then steps traffic up while an AnalysisTemplate watches the success rate and aborts on regression.

Work through these in order — each is a copy-paste template keyed on <app>:

  1. Build & CI — multi-stage distroless Dockerfile (cross-compiled arm64, migrate CLI bundled), .dockerignore, and a GitHub Actions workflow that pushes to GHCR.
  2. GitOps & deploy — the apps/<app>.yaml Argo Application, the workloads/<app>/ skeleton, and the Rollout / Service / Ingress manifests.
  3. Database & migrations — a CNPG Cluster, reading DB_URL from its auto-generated Secret, and the migrations PreSync Job.
  4. Canary & metrics — exposing /metrics, the ServiceMonitor, the AnalysisTemplate, and the Rollout canary gate.
  5. Secrets — the out-of-band secret pattern and the GHCR public-package vs imagePullSecret choice.

A new app touches exactly two places in the gitops repo:

gitops/
├── apps/
│ └── <app>.yaml # Argo CD Application → workloads/<app>
└── workloads/
└── <app>/
├── namespace.yaml
├── postgres-cluster.yaml # if the app needs a DB
├── migrations-job.yaml # if the app has migrations
├── configmap.yaml # non-secret env
├── rollout.yaml
├── service.yaml
├── ingress.yaml
├── servicemonitor.yaml
├── analysistemplate.yaml
└── scheduledbackup.yaml # if the app has a DB

Because the root app watches apps/, the new apps/<app>.yaml is picked up automatically — no extra registration step.