ArgoCD GitOps repository. Contains all application and platform definitions deployed to Kubernetes clusters.
The companion infrastructure repository (Terraform + Minikube) bootstraps ArgoCD and points it here.
Two logins to redo regularly (not one-time setup) — both expire, so run them again whenever a command starts failing with an auth error:
# Terraform state (S3 backend, infrastructure repo) — needed before any terraform command.
aws sso login --profile infrastructure
# OpenBao CLI (bao) — needed to read/write secrets directly with `bao kv ...`
# against the OIDC-gated OpenBao instance this repo deploys (platform/<env>/openbao.yml).
# Requires BAO_ADDR pointed at the exposed UI (export BAO_ADDR="https://openbao.scalepack.fr/").
# Token TTL is short (role-dependent, ~1h for the admin role) — relogin when it expires.
bao login -method=oidcArgoCD manages itself entirely through this repo, one directory per service, via two discovery mechanisms per cluster instead of a hand-maintained app-of-apps chart:
[Terraform] ──bootstraps──> bootstrap (ArgoCD Application)
│
└──creates, for env=local──┐
│
┌────────────────────────────────────┴───────────────────────────────┐
▼ ▼
services-vendor-local services-app-local
(Application, directory recurse) (ApplicationSet)
│ │
watches every services/*/*/applications/local/*.vendor.yaml expands every services/*/*/applications/local/*.app.yaml
and applies it as-is (external Helm charts: into a full Application (in-house charts: demo,
ingress-nginx, kube-prometheus-stack, openbao...) openbao-init, secrets-sync...), injecting repoURL/revision
- Terraform creates a single seed Application called
bootstrap bootstrapreadsbootstrap/templates/local.yaml, which createsservices-vendor-localandservices-app-localdirectly (no intermediateclusters/local/chart)- Each service's own
services/<bucket>/<name>/applications/local/folder is what "registers" its Applications — nothing outside that folder needs to change to add or remove one
gitops/
│
├── bootstrap/
│ └── templates/local.yaml # creates services-vendor-local + services-app-local
│
├── services/
│ ├── platform/ # infra: openbao, cert-manager, gateway, dex, monitoring, ...
│ │ └── openbao/
│ │ ├── init/ # in-house chart (restore/unseal Job, ClusterSecretStore)
│ │ │ ├── Chart.yaml
│ │ │ ├── values.yaml
│ │ │ ├── values-local.yaml
│ │ │ └── templates/...
│ │ └── applications/
│ │ └── local/
│ │ ├── chart.vendor.yaml # loads the openbao-helm chart, as-is
│ │ └── init.app.yaml # {name, namespace, chartPath, valueFile, syncWave}
│ │
│ └── products/ # revenue-generating apps
│ └── demo/
│ ├── chart/ # the demo nginx Helm chart
│ └── applications/
│ └── local/app.app.yaml
│
└── charts/
└── sso-guard/ # shared library chart, consumed as a dependency (not deployed on its own)
- Create the Helm chart under
services/<bucket>/<your-service>/<subfolder>/(<bucket>isplatformfor infra,productsfor revenue apps):
services/products/your-app/chart/
├── Chart.yaml
├── values.yaml # sensible defaults, treat as prod baseline
├── values-local.yaml # local overrides
└── templates/
└── ...
- Register it by adding a params file — no other file to touch:
# services/products/your-app/applications/local/app.app.yaml
name: your-app
namespace: your-app
chartPath: services/products/your-app/chart
valueFile: values-local.yaml
syncWave: "0"The services-app-local ApplicationSet picks it up automatically — no manual sync needed.
Say you're adding staging:
-
Add environment values for each chart that needs overrides:
services/products/demo/chart/values-staging.yaml -
Register every service that should run on staging, under its own
applications/staging/:services/platform/ingress-nginx/applications/staging/chart.vendor.yaml services/products/demo/applications/staging/app.app.yaml -
Add a bootstrap manifest in
bootstrap/templates/staging.yaml— copylocal.yaml, renameservices-vendor-local/services-app-localto-staging, and change thedirectory.include/ generatorfiles.pathglob fromlocaltostaging. -
Point Terraform (or your cluster provisioner) at
bootstrap/withenv=stagingto seed it.
Only the values files and applications/staging/ entries differ — the charts themselves are never duplicated.
Vendor tools are ArgoCD Applications that install Helm charts from external repositories — written as
a complete, self-contained manifest (no shared template, since these tend to need very different
things: inline values, ignoreDifferences, OCI sources...). Add one file per tool per env:
# services/platform/cert-manager/applications/local/chart.vendor.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: cert-manager
namespace: argocd
spec:
project: default
source:
repoURL: https://charts.jetstack.io
chart: cert-manager
targetRevision: v1.17.0
helm:
values: |
installCRDs: true
destination:
server: https://kubernetes.default.svc
namespace: cert-manager
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueThe services-vendor-local Application (a plain directory-recurse source) picks it up automatically.
Smoke test (destroy + rebuild everything from scratch):
# from the infrastructure repo
mise run reset && mise run devArgoCD takes a few minutes to reconcile all apps — be patient. The difference between "still syncing" and "actually stuck" is visible in:
kubectl --context minikube get applications -n argocdImportant: before running, check infrastructure/02-cluster/local/nico.auto.tfvars — it contains the gitops_revision variable that controls which branch of this repo ArgoCD will deploy from. If you're testing a feature branch, set it there:
gitops_revision = "feat/your-branch"If it points to main and your changes aren't merged yet, ArgoCD will deploy the old code.
Validate OpenBao is up:
kubectl --context minikube get clustersecretstore openbao
# READY=True → OpenBao running, unsealed, ESO connectedAfter mise run reset && mise run dev in the infrastructure repo:
# Add demo.local to /etc/hosts
echo "$(minikube ip) demo.local" | sudo tee -a /etc/hosts
# Check it's up
curl http://demo.local