DEV Community

DevOps Start
DevOps Start

Posted on • Originally published at devopsstart.com

How to Set Up Argo CD GitOps for Kubernetes Automation

Stop relying on manual kubectl applies and start treating your cluster as code. This comprehensive guide, originally published on devopsstart.com, walks you through setting up Argo CD for true GitOps automation.

Introduction

If you are still running kubectl apply -f manifests/ from your local machine or a Jenkins pipeline, you are operating in a "Push" model. In this model, your CI tool needs high-privileged credentials to your cluster, and you have no guarantee that what is actually running in production matches what is in your Git repository. One rogue developer running a manual kubectl edit can create a "configuration drift" that haunts you for months.

This is where GitOps comes in. GitOps is a paradigm where Git is the single source of truth for your infrastructure and application state. Instead of pushing changes to the cluster, a controller inside the cluster constantly monitors your Git repo and "pulls" the state to match.

In this tutorial (Part 1 of our series), we will move from imperative deployments to declarative continuous delivery using Argo CD v2.11.0. You'll learn how to install Argo CD, connect your repositories, handle configuration drift, and scale your deployments using ApplicationSets. By the end, you'll have a production-ready GitOps engine that ensures your cluster is always in the desired state. For a deeper dive into how this compares to other tools, check out our guide on /blog/argo-cd-vs-flux-a-guide-for-multi-cluster-gitops.

Prerequisites

Before we start, you need a working Kubernetes cluster. This can be a managed service like EKS, GKE, or AKS, or a local setup like Kind or Minikube. For this tutorial, we assume you are using Kubernetes v1.30 or newer.

You will need the following tools installed on your local workstation:

  • kubectl v1.30+: The standard Kubernetes CLI. Ensure your context is set to the correct cluster.
  • Git v2.40+: Required for managing the manifests that Argo CD will track.
  • A GitHub or GitLab account: You need a repository to store your Kubernetes YAML manifests.
  • Basic YAML knowledge: You should know how to write a basic Deployment and Service manifest. If you are totally new to this, refer to our /blog/kubernetes-for-beginners-deploy-your-first-application.

You don't need to install the Argo CD CLI for the basic setup, as we will use the Web UI and kubectl for most operations, but having it installed is helpful for advanced automation.

Overview

The goal of this tutorial is to build a fully automated deployment pipeline where a Git commit is the only trigger needed to update your application. We aren't just deploying a "Hello World" app; we are building a scalable architecture.

Here is the architecture we will implement:

  1. The Manifest Repo: A dedicated Git repository containing the desired state of your cluster (YAML files).
  2. Argo CD Controller: Installed in the argocd namespace, acting as the GitOps operator.
  3. The Application CRD: A custom resource that tells Argo CD: "Watch this folder in Git and make sure it exists in this namespace in the cluster."
  4. Automated Sync: A policy that automatically corrects any manual changes (drift) made to the cluster.
  5. ApplicationSets: A template-based approach to deploy the same application across multiple namespaces (e.g., dev, staging, prod) without duplicating YAML files.

By moving to this "Pull" model, you eliminate the need to store Kubeconfigs in your CI tool (like GitHub Actions or GitLab CI), which reduces the attack surface of your infrastructure by removing high-privileged secrets from external runners.

Step 1: Installing Argo CD

Argo CD is installed as a set of deployments and services within your cluster. We will use the official manifests provided by the Argo project.

First, create a dedicated namespace for Argo CD to keep the installation isolated.

kubectl create namespace argocd
Enter fullscreen mode Exit fullscreen mode

Now, apply the installation manifest. We will use the stable release manifest for v2.11.0.

kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.11.0/manifests/install.yaml
Enter fullscreen mode Exit fullscreen mode

Wait for all pods to reach the Running state. You can monitor the progress with this command:

kubectl get pods -n argocd
Enter fullscreen mode Exit fullscreen mode

The output should look like this:

NAME                                                 READY   STATUS    RESTARTS   AGE
argocd-server-7d5f8f8f5-abc12                       1/1     Running   0          2m
argocd-repo-server-5f4d7e9-def34                     1/1     Running   0          2m
argocd-application-controller-f7e8d9-ghi56           1/1     Running   0          2m
argocd-redis-7c8b9a0-jkl78                           1/1     Running   0          2m
argocd-notifications-controller-h9i0j1-mno90         1/1     Running   0          2m
Enter fullscreen mode Exit fullscreen mode

If any pods stay in Pending or CrashLoopBackOff, you can diagnose the issue using our guide on /troubleshooting/crashloopbackoff-kubernetes.

Step 2: Initial Access and Authentication

By default, the Argo CD API server is not exposed to the public internet. For this tutorial, we will use port-forwarding to access the UI.

Start the port-forward in a separate terminal window:

kubectl port-forward svc/argocd-server -n argocd 8080:443
Enter fullscreen mode Exit fullscreen mode

Now, open your browser and go to https://localhost:8080. You will see a login screen. The default username is admin. The password, however, is automatically generated and stored in a Kubernetes secret.

Run the following command to retrieve the initial admin password:

kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d; echo
Enter fullscreen mode Exit fullscreen mode

The output will be a plain-text string, for example: xYz123AbC456DefG. Copy this password and use it to log in to the UI.

Once you log in, change the admin password under the "User Management" settings. For production environments, avoid using the initial admin account and instead integrate with an OIDC provider like Okta or GitHub. You can find more details in the official Argo CD documentation.

Step 3: Connecting Your Git Repository

Argo CD needs permission to read your manifests. If your repository is public, you can just provide the URL. If it is private, you need to provide SSH keys or HTTPS credentials.

Let's assume you have a private GitHub repository located at git@github.com:your-org/gitops-manifests.git.

  1. Log in to the Argo CD UI.
  2. Go to Settings $\rightarrow$ Repositories.
  3. Click Connect Repo.
  4. Select via SSH.
  5. Enter the Repository URL: git@github.com:your-org/gitops-manifests.git.
  6. Paste your private SSH key (the one that has read access to the repo).
  7. Click Connect.

If the connection is successful, the status will change to Successful. If you see Failed, ensure your SSH key is correct and that the Argo CD pod has outbound network access to GitHub.

Step 4: Creating Your First GitOps Application

In Argo CD, an "Application" is a Custom Resource (CRD) that defines the link between a source (Git) and a destination (Cluster).

We will create a simple application that deploys a guestbook app. First, ensure your Git repo has a folder named guestbook containing a deployment.yaml and a service.yaml.

Using YAML is the "GitOps way" because you can store the Application definition itself in Git (the App-of-Apps pattern). Save the following as guestbook-app.yaml:

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: guestbook-gitops
  namespace: argocd
spec:
  project: default
  source:
    repoURL: 'git@github.com:your-org/gitops-manifests.git'
    targetRevision: HEAD
    path: guestbook
  destination:
    server: 'https://kubernetes.default.svc'
    namespace: guestbook-demo
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
Enter fullscreen mode Exit fullscreen mode

Component Breakdown

  • source: This tells Argo CD where to look. targetRevision: HEAD tracks the latest commit on the default branch.
  • destination: https://kubernetes.default.svc refers to the cluster where Argo CD is currently installed.
  • syncPolicy:
    • automated: Argo CD will automatically apply changes from Git to the cluster.
    • prune: true: If you delete a file from Git, Argo CD will delete the corresponding resource from Kubernetes.
    • selfHeal: true: If someone manually edits a resource in the cluster, Argo CD will instantly overwrite it with the Git version.
    • CreateNamespace=true: Ensures the guestbook-demo namespace is created if it doesn't exist.

Apply this manifest:

kubectl apply -f guestbook-app.yaml
Enter fullscreen mode Exit fullscreen mode

Now, go to the Argo CD UI. You will see the guestbook-gitops application. Initially, it will be "OutOfSync" while it calculates the difference, then it will transition to "Synced" and "Healthy" as it creates the pods and services.

Step 5: Handling Configuration Drift

Configuration drift occurs when the actual state of the cluster deviates from the desired state defined in Git. This usually happens when a developer uses kubectl edit to fix a production bug quickly but forgets to update the Git repo.

Let's simulate this. We will manually scale our deployment to 5 replicas using the CLI.

kubectl scale deployment guestbook --replicas=5 -n guestbook-demo
Enter fullscreen mode Exit fullscreen mode

If you check the Argo CD UI now, the application status has changed from Synced to OutOfSync. The UI will highlight the exact difference in yellow: "Desired: 3 replicas, Actual: 5 replicas."

Because we enabled selfHeal: true, you won't have to do anything. Within a few seconds, Argo CD will detect the drift and automatically scale the deployment back down to 3 replicas to match Git.

If selfHeal were disabled, the application would stay OutOfSync. You would then have two choices:

  1. Sync: Click the "Sync" button in the UI to force the cluster to match Git.
  2. Update Git: Change the replica count in your Git repo to 5, commit, and push. Argo CD would then see the new desired state and update the cluster.

This mechanism ensures that your Git history is an audit log of every change ever made to your environment.

Step 6: Scaling with ApplicationSets

Creating one Application manifest for one app is easy. But if you have 50 microservices across dev, staging, and prod clusters, creating 150 Application manifests is a maintenance nightmare.

ApplicationSets allow you to use a template to generate multiple Applications automatically. They use "generators" to discover targets.

Let's use a List Generator to deploy the same guestbook app into three different namespaces. Save this as guestbook-appset.yaml:

apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: guestbook-environments
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: engineering-dev
            namespace: guestbook-dev
          - cluster: engineering-staging
            namespace: guestbook-staging
          - cluster: engineering-prod
            namespace: guestbook-prod
  template:
    metadata:
      name: '{{cluster}}-guestbook'
    spec:
      project: default
      source:
        repoURL: 'git@github.com:your-org/gitops-manifests.git'
        targetRevision: HEAD
        path: guestbook
      destination:
        server: 'https://kubernetes.default.svc'
        namespace: '{{namespace}}'
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
Enter fullscreen mode Exit fullscreen mode

Apply the ApplicationSet:

kubectl apply -f guestbook-appset.yaml
Enter fullscreen mode Exit fullscreen mode

Argo CD iterates through the list and creates three separate Application resources: engineering-dev-guestbook, engineering-staging-guestbook, and engineering-prod-guestbook.

If you need to add a new environment (e.g., qa), you simply add one line to the elements list in the ApplicationSet and commit. For teams managing huge fleets of clusters, the Cluster Generator is even more powerful; it can automatically detect every cluster registered in Argo CD and deploy a "base" set of tools to all of them without any manual listing.

Step 7: Integrating the Full CI/CD Pipeline

Now that the "CD" (Continuous Delivery) part is handled by Argo CD, how does the "CI" (Continuous Integration) part fit in?

A common mistake is letting the CI tool (GitHub Actions, Jenkins, CircleCI) call kubectl apply. This breaks the GitOps model. Instead, the CI tool should only be responsible for updating the manifest repository.

Here is the professional workflow:

  1. Developer Pushes Code: A developer pushes a change to the application source code.
  2. CI Pipeline Runs: GitHub Actions triggers a build, runs tests, and builds a Docker image.
  3. Image Push: The CI tool pushes the image to a registry (e.g., Amazon ECR) with a unique tag (the Git SHA).
  4. Manifest Update: The CI tool clones the gitops-manifests repo and updates the image tag in the deployment.yaml file.
  5. Git Commit: The CI tool commits and pushes the change back to the manifest repo.
  6. Argo CD Pull: Argo CD detects the commit in the manifest repo and pulls the change into the cluster.

Example GitHub Action snippet for manifest update

- name: Update Kubernetes image tag
  run: |
    git clone https://x-token-auth:${{ secrets.GITOPS_TOKEN }}@github.com/your-org/gitops-manifests.git
    cd gitops-manifests
    sed -i "s|image: my-app:.*|image: my-app:${{ github.sha }}|g" guestbook/deployment.yaml
    git config user.name "GitHub Action"
    git config user.email "action@github.com"
    git add .
    git commit -m "Update guestbook image to ${{ github.sha }}"
    git push
Enter fullscreen mode Exit fullscreen mode

This separation of concerns is critical. The CI tool has no access to the cluster; it only has access to a Git repository. If your CI tool is compromised, the attacker cannot delete your production pods; they can only propose changes to Git, which can be blocked by a Pull Request review.

Troubleshooting

1. Application stuck in "Progressing" status

If your application is "Synced" but stays in "Progressing", the pods are likely failing to start. Argo CD is waiting for the Kubernetes health check to return Healthy.

Check the pod events:

kubectl get pods -n guestbook-demo
kubectl describe pod <pod-name> -n guestbook-demo
Enter fullscreen mode Exit fullscreen mode

Look for ImagePullBackOff or CrashLoopBackOff. If you see the latter, use the tips in /tips/debug-crashloopbackoff to find the root cause.

2. Repository Connection Failed

If Argo CD cannot connect to your Git repo, check the argocd-repo-server logs:

kubectl logs -n argocd -l app.kubernetes.io/name=argocd-repo-server
Enter fullscreen mode Exit fullscreen mode

Common causes include:

  • Incorrect SSH private key.
  • Firewall rules blocking port 22 (SSH) or 443 (HTTPS) from the cluster to GitHub.
  • Using a GitHub Deploy Key that doesn't have read access to the repository.

3. "OutOfSync" loop

Sometimes an application flips between Synced and OutOfSync rapidly. This is often caused by a conflict between Argo CD and another controller (like a Horizontal Pod Autoscaler).

If HPA is changing the replica count and Argo CD is trying to force it back to the Git value, they will fight forever. To fix this, ignore the replicas field in the Application spec:

spec:
  ignoreDifferences:
    - group: apps
      kind: Deployment
      jsonPointers:
        - /spec/replicas
Enter fullscreen mode Exit fullscreen mode

FAQ

Q: Can Argo CD manage Helm charts?
A: Yes. Argo CD natively supports Helm. You can point the source to a Helm repository or a folder containing a Chart.yaml. You can also provide a values.yaml file in Git to override default settings.

Q: What is the "App-of-Apps" pattern?
A: This is a pattern where you create one "Root" Argo CD Application that points to a folder containing other Application manifests. This allows you to manage your entire cluster state (including other apps) using a single GitOps entry point.

Q: Does Argo CD support multi-cluster management?
A: Yes. You can add external clusters to Argo CD via the CLI or UI. Once added, you can set the destination.server in your Application manifest to the API server URL of the remote cluster.

Conclusion

You have now transitioned from manual kubectl deployments to a professional GitOps workflow. We have installed Argo CD v2.11.0, connected a private repository, and deployed an application using the declarative model. By implementing selfHeal, you've ensured that your cluster is resilient to manual configuration drift. Furthermore, by using ApplicationSets, you've built a foundation that can scale from one application to hundreds across multiple environments.

The key takeaway is the shift in trust. You no longer trust the state of the cluster; you trust the state of Git. This makes your deployments repeatable, auditable, and significantly more secure.

Actionable next steps:

  1. Migrate one existing production service to Argo CD.
  2. Set up a "Management" repo that uses the App-of-Apps pattern to manage all your other Application manifests.
  3. Implement a PR-based workflow where no one is allowed to push directly to the main branch of your manifest repo.

In Part 2 of this series, we will cover advanced Argo CD features, including Blue/Green and Canary deployments using Argo Rollouts, and how to integrate Prometheus metrics to trigger automatic rollbacks if a new release increases your error rate.

Top comments (0)