Skip to content

Argo CD

Argo CD is installed by Terraform via the official Helm chart, into the argocd namespace, on the same cluster it then manages. The chart version is pinned and the values are inlined in infrastructure/gcp/argocd.tf.

This page covers what Terraform creates. For day-to-day operations (logging in, syncing apps, debugging stuck syncs), see the Argo CD operational page.

Install

Field Value
Helm repo https://argoproj.github.io/argo-helm
Chart argo-cd
Version 7.7.13 (pinned)
Namespace argocd (created by Helm)
Depends on google_container_node_pool.spot_nodes — pool must exist before Helm can schedule pods

The pin matters. Argo CD's CRDs change between minor versions, and the chart is the only thing that controls them. Bump version deliberately, never with a "latest" or wildcard.

Values

The Terraform values block (yamlencode'd into the chart) overrides three things:

Logging

global:
  logging:
    level: error

The whole platform runs at error level. Useful for keeping log volume down on a single-node cluster, painful when something goes wrong and you need INFO. To temporarily turn it up, either edit argocd.tf and apply, or kubectl patch configmap argocd-cmd-params-cm -n argocd directly (Argo CD reloads on its own).

Server

server:
  service:
    type: ClusterIP
  extraArgs: ["--insecure"]
  extraEnvs:
    - name: GOOGLE_CLIENT_SECRET
      valueFrom:
        secretKeyRef:
          name: google-oauth-credentials
          key: client-secret
  • ClusterIP — the server is not directly exposed. Traefik routes argo-app.tomoda.life through oauth2-proxy and then into the Argo CD ClusterIP service. There is no LoadBalancer for Argo CD.
  • --insecure — server runs HTTP only; TLS is terminated by Traefik upstream.
  • GOOGLE_CLIENT_SECRET is loaded from the K8s secret google-oauth-credentials (key client-secret). The argocd.tf comment notes this is "for safety" — the secret is actually consumed by Dex, but the server pod gets it too in case future Argo CD versions move OAuth handling out of Dex.

Dex

dex:
  env:
    - name: GOOGLE_CLIENT_SECRET
      valueFrom:
        secretKeyRef:
          name: google-oauth-credentials
          key: client-secret

The same secret is mounted as an env var in the Dex pod. The dex.config below references $GOOGLE_CLIENT_SECRET, which Dex expands at runtime — the literal secret is never written into a ConfigMap.

Auth config

configs:
  cm:
    exec.enabled: "true"
    url: "https://argo-app.tomoda.life"
    dex.config: |
      connectors:
        - type: google
          id: google
          name: Google
          config:
            clientID: "929386103976-15r47b9idc981ig2socuvkdpvrhm6j3b.apps.googleusercontent.com"
            clientSecret: "$GOOGLE_CLIENT_SECRET"
            hostedDomains:
              - "tomoda.life"
  rbac:
    policy.default: "role:admin"
Field Effect
url The public origin Argo CD generates redirect URLs against. Must match the oauth2-proxy / Traefik host exactly.
exec.enabled Lets Argo CD use the Kubernetes API "exec" plugin auth flow — required for the local CLI.
Dex clientID The internal-environment OAuth client (the literal value is checked into argocd.tf; this is intentional, it is a public client ID, not a secret).
hostedDomains Only tomoda.life Google accounts can log in. External Google accounts are rejected by Dex before Argo CD ever sees them.
policy.default: role:admin Every successfully-authenticated tomoda.life user is admin. Fine for internal tooling, would be a problem if Argo CD ever became multi-tenant.

How the secret reaches Dex

The K8s secret google-oauth-credentials in the argocd namespace is created by an ExternalSecret (k8s/envs/dev/sys/manifests/external-secrets-config.yaml) that pulls tomoda-google-client-secret from GCP Secret Manager. The first-time provisioning (creating the OAuth client in the GCP Console and storing both ID + secret in GCP SM) is manual — see OAuth & Dex and Secrets Management → Provisioning the Google OAuth client.

Exposure

The Argo CD server is ClusterIP only. The public URL https://argo-app.tomoda.life is served by:

  1. Cloudflare DNS resolves to the Traefik LoadBalancer IP on GKE.
  2. Traefik terminates TLS and forwards to the oauth2-proxy service.
  3. oauth2-proxy enforces Google login (using the same OAuth client as Dex).
  4. After successful auth, oauth2-proxy proxies to the Argo CD ClusterIP.
  5. Argo CD itself sees the request as already-authenticated and matches the user against its Dex config a second time.

The double-OAuth ("oauth2-proxy at the edge, Dex inside Argo CD") is redundant but harmless: oauth2-proxy gates the network, Dex gates Argo CD's authorisation.

What lives outside this Terraform

  • google-oauth-credentials secret — populated manually or by External Secrets from Secret Manager. Not in Terraform.
  • Argo CD Applications — every app the platform deploys is configured by manifests in the GitOps repo (k8s/), not by argocd.tf. This file only installs Argo CD itself.
  • Image Updater — installed as a separate workload in the argocd namespace (manifests in k8s/). The GCP-side IAM is in image_updater_iam.tf, not argocd.tf.