Why GitOps on a POC Cluster#

Setting up ArgoCD on minikube is not about automating deployments for a local cluster – you could just run kubectl apply. The point is to prove the deployment workflow before production. If your Git repo structure, Helm values, and sync policies work on minikube, they will work on EKS or GKE. If you skip this and bolt on GitOps later, you will spend days restructuring your repo and debugging sync failures under production pressure.

Installing ArgoCD via Helm#

helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

helm upgrade --install argocd argo/argo-cd \
  --namespace argocd \
  --create-namespace \
  --set server.service.type=NodePort \
  --set configs.params."server\.insecure"=true \
  --wait --timeout 300s

Setting server.insecure=true disables TLS on the ArgoCD server, which is fine for minikube where you access it via port-forward. Do not do this in production.

Retrieving the Admin Password#

ArgoCD generates an initial admin password stored in a Kubernetes secret:

kubectl -n argocd get secret argocd-initial-admin-secret \
  -o jsonpath="{.data.password}" | base64 -d

Access the UI:

kubectl port-forward svc/argocd-server -n argocd 8080:80
# Open http://localhost:8080
# Login: admin / <password from above>

Install the CLI for programmatic access:

# macOS
brew install argocd

# Login to your local instance
argocd login localhost:8080 --insecure --username admin --password <password>

The Repo Structure ArgoCD Expects#

ArgoCD is flexible about repo layout, but the most maintainable pattern separates application manifests from ArgoCD configuration:

my-gitops-repo/
├── apps/                          # ArgoCD Application definitions
│   ├── root-app.yaml              # The "app of apps"
│   ├── web-api.yaml               # Application resource for web-api
│   ├── database.yaml              # Application resource for PostgreSQL
│   └── monitoring.yaml            # Application resource for monitoring stack
├── manifests/                     # Actual Kubernetes manifests
│   ├── web-api/
│   │   ├── deployment.yaml
│   │   ├── service.yaml
│   │   └── ingress.yaml
│   ├── database/
│   │   └── helm-values.yaml
│   └── monitoring/
│       └── helm-values.yaml
└── README.md

The apps/ directory contains ArgoCD Application custom resources. The manifests/ directory contains what actually gets deployed to Kubernetes. ArgoCD reads from apps/, which points to manifests/.

Connecting a Git Repo#

For a public repo:

argocd repo add https://github.com/yourorg/gitops-repo.git

For a private repo, use an SSH key or a personal access token:

# SSH
argocd repo add git@github.com:yourorg/gitops-repo.git \
  --ssh-private-key-path ~/.ssh/id_ed25519

# HTTPS with token
argocd repo add https://github.com/yourorg/gitops-repo.git \
  --username git --password <github-pat>

The App-of-Apps Pattern#

Instead of creating each ArgoCD Application manually, define one “root” Application that points to the apps/ directory. ArgoCD reads that directory, finds Application manifests inside it, and creates them automatically.

# apps/root-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: root
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/yourorg/gitops-repo.git
    targetRevision: main
    path: apps
  destination:
    server: https://kubernetes.default.svc
    namespace: argocd
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

Now define individual applications:

# apps/web-api.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: web-api
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/yourorg/gitops-repo.git
    targetRevision: main
    path: manifests/web-api
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true

Apply the root app once:

kubectl apply -f apps/root-app.yaml

From this point, adding a new application means committing a new YAML file to the apps/ directory. ArgoCD picks it up automatically.

How Auto-Sync Works#

When syncPolicy.automated is set, ArgoCD polls the Git repository (default every 3 minutes) and compares the live cluster state against the desired state in Git.

  • prune: true – If you delete a manifest from Git, ArgoCD deletes the corresponding resource from the cluster. Without this, orphaned resources accumulate.
  • selfHeal: true – If someone manually edits a resource with kubectl edit, ArgoCD reverts it to match Git. This enforces Git as the single source of truth.

To trigger an immediate sync without waiting for the poll interval:

argocd app sync web-api

Watching for Drift#

The ArgoCD UI shows each application as a card with a sync status:

  • Synced (green) – Cluster matches Git.
  • OutOfSync (yellow) – Git has changes not yet applied.
  • Degraded (red) – Resources exist but are unhealthy (pods crashing, etc.).

From the CLI:

argocd app list
argocd app get web-api

From Minikube to Production#

When you move to a production cluster, the ArgoCD setup changes minimally:

  1. Enable TLS on the ArgoCD server (remove the server.insecure flag, add a cert).
  2. Add RBAC to restrict who can sync which applications.
  3. Configure SSO to replace the admin password with your identity provider.
  4. Point destination.server to your production cluster API endpoint instead of kubernetes.default.svc.

The app-of-apps structure, sync policies, and repo layout remain identical. That is the payoff of setting it up on minikube first.