A reference implementation of a modern, secure, GitOps-driven CI/CD platform on Kubernetes.
Terraform-provisioned EKS Β· Argo CD app-of-apps Β· Argo Rollouts canary Β· Kyverno policy Β· signed images Β· supply-chain secured.
A complete, opinionated answer to: "How does a modern team ship to production safely in 2026?"
It's not a single tool β it's a coordinated system of tools, each picked for a specific job, wired together so that a git push becomes a progressively-rolled-out, signed, policy-enforced production deployment with full observability and instant rollback.
This bundle also demonstrates event-driven CD (Argo Events + Argo Workflows) for sub-second deploy reaction times, and Grafana deploy annotations so every dashboard shows a vertical line at the moment of each release β making "did the latency spike correlate with the deploy?" answerable in 5 seconds.
flowchart LR
DEV[Developer<br/>git push] --> PR[Pull Request]
subgraph CI["CI β GitHub Actions"]
LINT[Lint & test]
BUILD[Multi-stage<br/>container build]
SCAN[Trivy vuln<br/>+ SBOM]
SIGN[Cosign sign<br/>+ attestation]
end
PR --> LINT --> BUILD --> SCAN --> SIGN
SIGN --> REG[(Container<br/>registry)]
SIGN --> GIT[(Deployments<br/>repo bump)]
subgraph CD["CD β Argo CD on EKS"]
APP[ApplicationSet]
SYNC[Sync to cluster]
end
GIT --> APP --> SYNC
subgraph K8s["EKS cluster"]
ADM[Kyverno admission<br/>verify signature<br/>require labels<br/>ban :latest]
ROLL[Argo Rollouts<br/>canary 5β25β50β100%]
APPS[Pods]
end
SYNC --> ADM --> ROLL --> APPS
subgraph Sec["Secrets"]
ESO[External Secrets<br/>Operator]
ASM[(AWS Secrets<br/>Manager)]
end
ASM --> ESO --> APPS
subgraph Obs["Observability"]
PROM[Prometheus]
GRAF[Grafana]
SLACK[Slack notifs]
end
ROLL --> PROM --> GRAF
ROLL --> SLACK
| Concern | Choice | Why |
|---|---|---|
| Infrastructure provisioning | Terraform | Industry standard, EKS module mature, providers chain cleanly |
| Cluster type | Amazon EKS | Module swappable for AKS/GKE; pattern is the same |
| Continuous delivery | Argo CD | UI maturity, RBAC, multi-tenancy, ApplicationSet generators |
| Event-driven CD | Argo Events + Argo Workflows | Sub-second deploy reaction; per-deploy audit trail as Workflow object; composable verifyβbumpβsyncβannotate graph |
| Progressive delivery | Argo Rollouts | Native canary, analysis templates, traffic shifting via Ingress |
| Manifests | Kustomize (no Helm in app repos) | One language, no template-engine cognitive load; Helm reserved for platform components |
| CI | GitHub Actions | Reusable workflows, OIDC into AWS, native cosign/syft action support |
| Code quality / SAST | SonarCloud | Quality gate fails CI on coverage drop, code smells, vulns; standard enterprise checkbox |
| Image signing | Cosign + Sigstore (keyless) | Keyless OIDC = no key management; verifiable provenance |
| Image scanning | Trivy | Fast, broad coverage, SBOM in CycloneDX |
| Admission control | Kyverno | YAML policies (no Rego learning curve), built-in image-signature verification |
| Secret backend | Dual: AWS Secrets Manager + HashiCorp Vault | Cloud-native default; Vault for multi-cloud, dynamic creds, regulated workloads β see ADR-004 |
| Secret sync | External Secrets Operator | Same consumer pattern regardless of backend; no plaintext in Git |
| Dependency updates | Renovate | More precise than Dependabot, groups updates, supports Terraform/Helm/K8s |
| Pre-merge guardrails | pre-commit + conftest | Catches issues before CI even runs |
See docs/adr/ for full architecture decision records.
gitops-cicd-platform/
βββ apps/demo-api/ # Sample application (FastAPI)
β βββ app/ # Source code
β βββ tests/ # pytest suite
β βββ Dockerfile # Multi-stage, non-root, distroless final
β βββ pyproject.toml
β
βββ infrastructure/ # Terraform β provision the platform
β βββ modules/eks-platform/ # EKS + Argo CD + addons bootstrap
β βββ environments/dev/ # Dev environment instance
β
βββ platform/ # Platform components (deployed by bootstrap)
β βββ argocd/ # AppProject + ApplicationSet
β βββ argo-events/ # Event-driven CD: EventBus + EventSource + Sensor + Workflow
β βββ kyverno-policies/ # Admission policies
β βββ external-secrets/ # ESO ClusterSecretStore (AWS SM + Vault) + sample ExternalSecret
β
βββ deployments/ # App manifests (Argo CD source of truth)
β βββ apps/demo-api/
β βββ base/ # Common manifests
β βββ overlays/{dev,staging,prod}/ # Per-env Kustomize patches
β
βββ .github/workflows/ # CI: build, scan, sign; Terraform; policy checks
βββ policies/ # Kyverno/conftest test fixtures
βββ scripts/ # Bootstrap, smoke-test helpers
βββ docs/ # Architecture, ADRs, runbooks
- AWS account with admin (for
terraform apply) terraform,kubectl,argocd,cosignCLIs installed- A container registry (GHCR works out of the box)
cd infrastructure/environments/dev
terraform init
terraform apply # ~15 min: VPC, EKS, Argo CD, addons
aws eks update-kubeconfig --name gitops-platform-dev --region us-east-1kubectl get pods -n argocd
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Visit https://localhost:8080 (initial admin password printed by Terraform output)kubectl apply -f platform/argocd/projects/
kubectl apply -f platform/argocd/applicationset.yamlArgo CD will now sync everything under deployments/apps/ into the cluster, one Application per overlay.
# Cut a release: bump the image tag in deployments/apps/demo-api/overlays/dev/kustomization.yaml
# Push to main. CI builds, scans, signs, pushes the image.
# A bot PR bumps the tag in deployments/. Once merged, Argo CD syncs.
# In prod, Argo Rollouts handles the 5β25β50β100% progression.
kubectl argo rollouts get rollout demo-api -n demo-prod --watchThis repo demonstrates SLSA-aligned practices:
- Source integrity β commits required to be signed (branch protection rule, not enforced here but documented)
- Build provenance β GitHub Actions emits SLSA provenance attestation via
actions/attest-build-provenance - Vulnerability gate β Trivy fails CI on
HIGH/CRITICALwith a fixed version available - SBOM β Syft generates CycloneDX SBOM, attached as a Cosign attestation
- Image signing β Cosign keyless (OIDC) signs every image; identity = workflow + repo
- Admission verification β Kyverno blocks unsigned images at deploy time (
verifyImagesrule)
The prod overlay uses an Argo Rollout, not a plain Deployment:
# Excerpt from deployments/apps/demo-api/overlays/prod/rollout-patch.yaml
strategy:
canary:
steps:
- setWeight: 5
- pause: { duration: 2m }
- setWeight: 25
- pause: { duration: 5m }
- analysis:
templates:
- templateName: success-rate
- setWeight: 50
- pause: { duration: 5m }
- setWeight: 100Combined with the AnalysisTemplate (success-rate queried against Prometheus), a bad release is auto-rolled-back without paging anyone.
- ADR-001 β Argo CD over Flux
- ADR-002 β Kustomize over Helm for app manifests
- ADR-003 β Kyverno over OPA Gatekeeper
- ADR-004 β Vault vs cloud-native secret managers
- Argo Workflows for CI-as-K8s alternative
- Tekton Chains as alternate signing path
- Spinnaker-style multi-cloud deploy demo
- OpenTofu variant of
infrastructure/ - GitOps for the platform itself (Argo CD manages Argo CD)
MIT Β© Mohammed Aboud