Skip to content

GitOps install (ArgoCD)

The GitOps path deploys the same product as the helm install, but ArgoCD continuously reconciles it from a per-environment Git repo, secrets come from Vault → External Secrets Operator (never in git), and the product runs the released OCI chart pinned to a version. Identity is declarative and reproducible — a rebuilt cluster comes back with the same admin + SSO.

When to use this

Choose GitOps when you want continuous drift-correction, an audit trail of every change, and credentials that never touch git. For a straightforward one-shot install, the helm path is simpler. Both are production paths and share the same identity model.

What you'll create

  • A per-environment GitOps repo — the desired state for this cluster: the app-of-apps, the OCI-chart pin, vendored CRDs, and per-environment values. You scaffold it with the customer:init generator.
  • A bootstrap — the one imperative step that stands up ArgoCD + Vault + a few operators, seeds Vault from your secrets, and points ArgoCD at your repo. ArgoCD deploys everything else.

Prerequisites

  • A conformant Kubernetes cluster (see Requirements) with a default StorageClass and DNS for *.your-domain.
  • The product tooling checked out (the Taskfile + bootstrap/ machinery that performs the bootstrap).
  • A Git host for your environment repo + a read-only deploy key ArgoCD can use (or use the bundled in-cluster Git mirror for an air-gapped / no-external-repo install).

1. Scaffold your environment repo

bash
CUSTOMER=acme DOMAIN=gw.acme.com \
  REPO_URL=git@github.com:acme/aigw-gitops.git \
  HA=true ACCESS_MODE=lb STORAGE_CLASS=<your-default-sc> DNS_ZONE=<your-dns-zone> \
  TLS_MODE=letsencrypt \
  task customer:init

The knobs bake your environment's shape into the generated repo (validated on GKE, zonal + regional):

FlagEffect
HA=true | falseHA (3× DB/Redis, 2× control + data plane) vs standalone (1 each)
ACCESS_MODE=lb | tunnelLoadBalancer + DNS A-records vs Cloudflare Tunnel
STORAGE_CLASS=<sc>your cluster's real default RWO class (standard-rwo on GKE, etc.) — else the bootstrap PVCs hang Pending
DOMAIN / DNS_ZONEyour gateway domain (baked at scaffold time) + the DNS-01 zone

DOMAIN must be final at scaffold time

Set the customer's real domain now. Changing baseDomain after the platform is up leaves the gateway's governance plugins scoped to the old domain → /v1 requests 404 until a clean re-reconcile.

This writes a ready-to-commit repo under ./gitops-acme/:

  • config.yaml — the one file that defines this environment (domain, repo URL, TLS mode, namespace prefix).
  • version.yaml — the released component matrix + the OCI product-chart pin.
  • platform/ + platform-config/ — the ArgoCD app-of-apps + ESO/namespaces.
  • platform-values/ — foundation config re-rendered for your domain.
  • vendored manifests/crds/gateway-api + vendor/charts/mimir-standalone (the non-OCI deps).
  • secrets.example.yaml + a README.

Commit and push it, then add the deploy key to ArgoCD's repo credentials.

2. Fill in secrets

Copy secrets.example.yaml to a git-ignored secrets.yaml and fill it — provider keys, OIDC client secret, database passwords, and the identity values:

yaml
gateway:
  platformAdminPassword: "<initial break-glass admin password>"
  bootstrapIdpClientSecret: "<your SSO OAuth client secret>"
  # …the rest of the REPLACE_ME values…

These are seeded into Vault at bootstrap; ESO then materializes them as Kubernetes Secrets. They never enter git.

3. Turn on the platform admin + SSO

In your environment's product-chart overlay (platform-values/gitops-overrides-prod.yaml in the generated repo — customer:init seeds it with these blocks), enable the declarative identity:

yaml
keycloak:
  platformAdmin:
    enabled: true
    username: admin@acme.example    # created once into the admin group; console-authoritative after
  bootstrapIdp:
    enabled: true
    providerId: google              # google | oidc
    clientId: "<your-oauth-client-id>"
    hostedDomain: acme.example       # optional Google workspace gate

3b. Optional: the platform-engineering agent runtime

The platform-engineering chat is powered by a separate, licensed agent runtime. It ships as an independent GitOps app — its own ArgoCD Application, its own namespace, and its own version pin — that you enable only if you license the module. When it's off, nothing about it is deployed and the rest of the product is unaffected.

To turn it on in your environment's config.yaml:

yaml
peRuntime:
  enabled: true      # off by default

The runtime container image is a private, licensed artifact hosted on GitHub Container Registry (ghcr.io/opsta/opsta-pe-agent, internal visibility). Every cluster pulls it with an image-pull secret that External Secrets builds from your credentials, alongside the runtime's own LLM and GitHub-App credentials — add them to your git-ignored secrets.yaml:

yaml
peRuntime:
  ghcrPull:                       # a read:packages token for the licensed image
    username: "<ghcr-username>"
    token: "<ghcr-read-token>"
  llm:                            # the LLM endpoint the runtime uses to scaffold
    baseUrl: "https://…"
    apiKey: "<key>"
  githubApp:                      # the GitHub App that opens the scaffold PRs
    appId: "<id>"
    installationId: "<id>"
    org: "<github-org>"
    privateKeyPem: |
      <the App private key PEM>

These are seeded into Vault at bootstrap; the runtime chart's ExternalSecrets materialize them in its namespace. On upgrade, the runtime is pinned in the component matrix (version.yaml) and promoted like any other component — no separate release step.

4. Bootstrap

One command stands up ArgoCD + Vault + External Secrets + the operators, seeds Vault from your secrets file, adds the read-only deploy key as ArgoCD's repo credentials, and applies the root app-of-apps pointed at your repo. ArgoCD reconciles everything else. It auto-detects a reachable GKCTX_PROD and skips the local-k3d provisioning (validated on GKE), so on your own cluster it just runs the foundation steps against it.

bash
GHCR_USER=<user> GHCR_TOKEN=<token-with-read:packages> \
  task gitops:bootstrap:prod \
    GKCTX_PROD=<your-kube-context> GCLUSTER_PROD=<cluster-name> \
    PROD_REPO_URL=git@github.com:acme/aigw-gitops.git \
    PROD_DEPLOY_KEY=~/.ssh/acme-aigw \
    GITOPS_SECRETS=./gitops-acme/secrets.yaml \
    NS_PREFIX=acme- REGISTRY=ghcr.io STORAGE_CLASS=<your-default-sc>

Pass the routing vars as CLI args, and set STORAGE_CLASS

  • The GKCTX_PROD / PROD_REPO_URL / … routing vars are safest passed as CLI args after the task name (as above), not as leading env vars — only GHCR_USER/GHCR_TOKEN are env. The task prints a target banner (context / repo / secrets) before it acts — read it to confirm you're aiming at the right cluster.
  • Set STORAGE_CLASS to your cluster's real default RWO class, or the argocd-redis / vault-raft bootstrap PVCs hang Pending.

ArgoCD then reconciles the whole stack. Watch it converge:

bash
kubectl -n argocd get applications -w

5. Verify and sign in

When every Application is Synced / Healthy, point wildcard DNS at the gateway and open https://console.your-domain:

  • Sign in with SSO (if you enabled bootstrapIdp), or
  • the platform adminkeycloak.platformAdmin.username with the initial password you set.

Add/remove admins and identity providers in the console afterward — those changes persist across upgrades.

Realm changes need one extra sync

Enabling platformAdmin/bootstrapIdp (or any realm change) updates a ConfigMap the identity importer reads. Because the importer runs before the product app updates that ConfigMap in a combined sync, trigger re-run the identity import after the deploy settles so the change is applied — then verify the admin exists and can sign in. Use the task (it patches the keycloak Application to re-run config-cli against the current realm ConfigMap):

bash
KCTX=<your-kube-context> task gitops:apply-realm

Air-gapped

The bundled in-cluster Git mirror removes the need for an external Git host, and the product chart + images can be mirrored into your own registry. See Air-gapped install.

Next steps

Enterprise AI governance, on infrastructure you own.