Skip to content

ArgoCD — GitOps Continuous Delivery ​

ArgoCD is the GitOps controller for this cluster. It watches your infra repo and automatically applies any changes to Kubernetes.


What ArgoCD does here ​

ResponsibilityDetail
Continuous deliverySyncs cluster state to Git on every push
Self-healingReverts manual kubectl changes automatically
PruningRemoves cluster resources deleted from Git
Web UIVisual overview of all apps and their sync status
Webhook receiverInstant sync triggered by GitHub push events

Architecture ​

GitHub (infra repo)
      │  push
      │  webhook → https://<ARGOCD_DOMAIN>/api/webhook
      ▼
ArgoCD (argocd namespace)
  ├── repo-server   ← clones infra via SSH deploy key
  ├── application-controller ← diffs desired vs actual state
  ├── server        ← UI + API (--insecure, Traefik handles TLS)
  └── redis         ← cache

      │  kubectl apply (server-side)
      ▼
Kubernetes (apps, monitoring, … namespaces)

Installation ​

ArgoCD is deployed via Helm from infra/:

bash
task argocd:deploy

This installs argo/argo-cd chart at the version pinned in ARGOCD_VERSION (default: 7.8.26) with values from platform/argocd/values.yaml (consuming repo override) or charts/platform-argocd/values.yaml (k3s-lab defaults).

Key configuration choices ​

SettingValueWhy
server.insecure: trueenabledTraefik terminates TLS; ArgoCD runs plain HTTP behind it
dex.enabled: falsedisabledSingle-user setup; no SSO needed
redis in-clusterenabledRequired for app state cache

Access ​

MethodURL / Command
Web UIhttps://<ARGOCD_DOMAIN>
Initial passwordkubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath='{.data.password}' | base64 -d
CLI loginargocd login <ARGOCD_DOMAIN> --username admin

Change the default password after first login.


Repo registration ​

ArgoCD accesses your private infra repo via an SSH deploy key stored as a Kubernetes Secret:

bash
task argocd:add-repo GITHUB_DEPLOY_KEY=~/.ssh/argocd_deploy_key

This creates (or updates) the argocd-infra-repo secret in the argocd namespace with label argocd.argoproj.io/secret-type=repository.

The public key must be added to GitHub: https://github.com/KevinDeBenedetti/infra/settings/keys (read-only).

The repoURL in every ArgoCD Application manifest must use the SSH format (git@github.com:KevinDeBenedetti/infra.git) to match this secret. Using https:// will result in authentication required errors.


Applications ​

ArgoCD Applications are managed via ApplicationSets that auto-discover app directories. In the consuming infra repo:

infra/
  argocd/
    projects/         ← AppProjects (RBAC boundaries)
    applicationsets/  ← Git directory generators (auto-discover apps/ and platform/)
    applications/     ← Standalone Applications (security, secrets)
  apps/               ← App manifests (one dir per app)
  platform/           ← Platform component overrides

ApplicationSets are applied once during bootstrap:

bash
kubectl --context k3s-infra apply -f argocd/projects/
kubectl --context k3s-infra apply -f argocd/applicationsets/
kubectl --context k3s-infra apply -f argocd/applications/

After that, adding a new app is just mkdir apps/myapp/ && git push.

Example ApplicationSet ​

yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: apps
  namespace: argocd
spec:
  generators:
    - git:
        repoURL: git@github.com:KevinDeBenedetti/infra.git
        revision: main
        directories:
          - path: apps/*
  template:
    metadata:
      name: "{{path.basename}}"
    spec:
      project: apps
      source:
        repoURL: git@github.com:KevinDeBenedetti/infra.git
        targetRevision: main
        path: "{{path}}"
      destination:
        server: https://kubernetes.default.svc
        namespace: apps
      syncPolicy:
        automated:
          prune: true
          selfHeal: true

GitHub Webhook ​

For instant sync (instead of polling every 3 minutes), configure a webhook:

FieldValue
URLhttps://<ARGOCD_DOMAIN>/api/webhook
Content typeapplication/json
EventsJust the push event
Secret(leave empty)

https://github.com/KevinDeBenedetti/infra/settings/hooks


Traefik routing ​

ArgoCD requires two IngressRoute routes because the CLI uses gRPC (HTTP/2):

yaml
routes:
  # UI + REST API
  - match: Host(`<ARGOCD_DOMAIN>`)
    priority: 10
    services:
      - name: argocd-server
        port: 80

  # gRPC (argocd CLI)
  - match: Host(`<ARGOCD_DOMAIN>`) && Header(`Content-Type`, `application/grpc`)
    priority: 11
    services:
      - name: argocd-server
        port: 80
        scheme: h2c   # plain HTTP/2 (no TLS — Traefik handles it)

The gRPC route has higher priority and is selected by the Content-Type header.


Useful commands ​

bash
# List all apps and their sync/health status
kubectl --context k3s-infra get applications -n argocd

# Force sync an app immediately
kubectl --context k3s-infra patch application whoami -n argocd \
  --type merge -p '{"operation":{"sync":{}}}'

# See sync error details
kubectl --context k3s-infra describe application whoami -n argocd

# Tail ArgoCD server logs
kubectl --context k3s-infra logs -n argocd deploy/argocd-server -f

# List registered repos
kubectl --context k3s-infra get secrets -n argocd \
  -l argocd.argoproj.io/secret-type=repository

See also ​