A local GitOps playground that spins up a kind Kubernetes cluster and installs ArgoCD using Terraform — no cloud account required.
Disclaimer: this playground is for demonstrative purposes only. It is not intended for production use. Configuration choices (local state, insecure ArgoCD, single-node cluster) are deliberately simplified to make it easy to run locally.
gitops-playground/
├── argocd/ # ArgoCD Application manifests (desired state)
│ ├── level-1/ # Helm chart source
│ │ └── kubeview.yaml
│ ├── level-2/ # Raw Kubernetes manifests
│ │ ├── echo-server.yaml
│ │ └── manifests/
│ │ ├── deployment.yaml
│ │ └── service.yaml
│ ├── level-3/ # Kustomize overlays
│ │ ├── dev.yaml
│ │ ├── prod.yaml
│ │ └── manifests/
│ │ ├── base/
│ │ │ ├── kustomization.yaml
│ │ │ ├── deployment.yaml
│ │ │ └── service.yaml
│ │ └── overlays/
│ │ ├── dev/
│ │ │ └── kustomization.yaml
│ │ └── prod/
│ │ └── kustomization.yaml
│ └── level-4/ # Infrastructure managed by ArgoCD
│ └── keda.yaml
├── terraform/ # Infrastructure as code
│ ├── .helm/
│ │ └── repositories.yaml
│ ├── main.tf
│ ├── providers.tf
│ ├── variables.tf
│ └── outputs.tf
├── .gitignore
└── README.md
| Tool | Version | Install |
|---|---|---|
| Docker | Engine or Desktop, running | docs.docker.com |
| Terraform | >= 1.5 (tested with 1.15.4) | developer.hashicorp.com |
| kind | >= 0.20 | kind.sigs.k8s.io |
| kubectl | any recent version | kubernetes.io |
Make sure Docker is running before starting. You can verify with:
docker infoMemory: Docker needs at least 4 GB of memory. On Docker Desktop go to Settings → Resources → Memory and increase it if needed. 6 GB recommended if you plan to run all levels at once.
cd terraform
terraform initterraform applyWrite the kubeconfig to a local file and point KUBECONFIG to it:
terraform output -raw kubeconfig > kubeconfig
export KUBECONFIG=$(pwd)/kubeconfigThe
kubeconfigfile is git-ignored. Run this step once per cluster — the path stays stable across applies.
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d && echoNote: ArgoCD deletes this secret once you change the password via the UI.
Start the port-forward:
kubectl -n argocd port-forward svc/argocd-server 8080:80Then open http://localhost:8080 in your browser.
Credentials:
- Username:
admin - Password: output from step 4
The argocd/ directory is structured as a progression of levels, each introducing a new GitOps concept. Make sure the cluster is up and ArgoCD is accessible before starting.
Levels 2 and 3 pull manifests from Git. The Applications point to this public repository by default — no extra setup needed, just apply and go.
Want to push your own changes and watch ArgoCD sync them? Fork the repo and run the setup script to point the Applications to your fork:
./setup.sh <your-github-username> git add . git commit -m "chore: set repoURL to my fork" git pushOnce an Application is applied, every subsequent change you push will be picked up by ArgoCD automatically. The first
kubectl applyis always required to register the Application with ArgoCD.Using a private fork? You need to add the repository to ArgoCD first. See the official docs.
Deploy KubeView, a cluster visualizer, directly from a Helm chart repository. This is the simplest ArgoCD use case: point to a chart, let ArgoCD handle the rest.
kubectl apply -f argocd/level-1/kubeview.yamlOnce synced, access it:
kubectl -n kubeview port-forward svc/kubeview 8081:8000Open http://localhost:8081.
Deploy an echo server (traefik/whoami) from plain Kubernetes manifests stored in this repo. ArgoCD watches the argocd/level-2/manifests/ directory and applies whatever is there.
kubectl apply -f argocd/level-2/echo-server.yamlOnce synced, access it:
kubectl -n echo-server port-forward svc/echo-server 8082:80curl http://localhost:8082Try it — GitOps sync: edit
argocd/level-2/manifests/deployment.yaml, changereplicasto2, push to GitHub, and watch ArgoCD sync the change automatically.
Try it — self-healing: manually force a drift by scaling the deployment down to
0with kubectl, then watch ArgoCD detect and fix it:# Force a drift kubectl -n echo-server scale deployment echo-server --replicas=0 # Watch ArgoCD reconcile it back kubectl -n echo-server get pods -wWithin seconds ArgoCD will detect that the live state diverged from the desired state in Git and restore the replicas. This is the reconciliation loop in action.
Deploy the same application to two different environments (dev and prod) from a single base using Kustomize. Each overlay customizes the base without duplicating it — a common real-world GitOps pattern.
| dev | prod | |
|---|---|---|
| Namespace | echo-server-dev |
echo-server-prod |
| Replicas | 1 | 3 |
| Label | env: dev |
env: prod |
kubectl apply -f argocd/level-3/dev.yaml
kubectl apply -f argocd/level-3/prod.yamlOnce synced, access each environment on a different port:
# dev
kubectl -n echo-server-dev port-forward svc/echo-server 8083:80
# prod (new terminal)
kubectl -n echo-server-prod port-forward svc/echo-server 8084:80Try it: edit
overlays/prod/kustomization.yaml, changereplicasto5, push to GitHub, and watch only the prod Application sync while dev stays unchanged.
GitOps is not only for applications — infrastructure components can and should be managed the same way. In this level ArgoCD deploys KEDA (Kubernetes Event-Driven Autoscaling), a CNCF Graduated project that extends Kubernetes with event-driven autoscaling capabilities. Installing it via ArgoCD means the cluster's capabilities are themselves version-controlled and reconciled from Git.
kubectl apply -f argocd/level-4/keda.yamlOnce synced, verify KEDA is running:
kubectl -n keda get podsKEDA installs its CRDs into the cluster (ScaledObject, ScaledJob, TriggerAuthentication). You can now define autoscaling rules declaratively alongside your application manifests.
terraform destroyThis removes the kind cluster and all its containers. The local terraform.tfstate file will remain.
Contributions are welcome! To propose a change:
- Fork the repository and create a branch following the naming convention:
<type>/<short-description>(e.g.feature/add-level-5,fix/kubeconfig-output) - Make your changes and commit using Conventional Commits (e.g.
feat(argocd): add level-5 app) - Open a pull request against
mainwith a clear description of what the change does and why
Please keep PRs focused — one logical change per PR makes review faster and merges cleaner.