Skip to main content

GitOps with ArgoCD

Overview​

ArgoCD is a declarative, GitOps continuous delivery tool for Kubernetes. This cluster uses ArgoCD to manage all applications through Git-based configuration, ensuring infrastructure-as-code principles and automated synchronization.

Installation​

ArgoCD is installed using Helm with custom values that integrate with Vault for secret management and Traefik for ingress.

Prerequisites​

  • k3s cluster running
  • cert-manager installed (for TLS certificates)
  • Traefik ingress controller (default in k3s)
  • HashiCorp Vault configured (for secret management)

Install ArgoCD​

# Add the Argo CD Helm repo
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update

# Create namespace
kubectl create namespace argocd

# Install Argo CD with custom values
helm install argocd argo/argo-cd \
--namespace argocd \
--values k3s-argocd/values.yaml

The custom values.yaml configures:

  • Vault integration via ArgoCD Vault Plugin (AVP)
  • Vault agent sidecar for token management
  • Repository server configuration for secret injection

Configuration​

Insecure Mode with TLS Termination​

ArgoCD is configured to run in insecure (HTTP) mode internally, with TLS termination handled by Traefik at the ingress level. This is a common pattern that simplifies certificate management.

ConfigMap (k3s-argocd/argocd-config-map.yaml):

apiVersion: v1
kind: ConfigMap
metadata:
name: argocd-cmd-params-cm
namespace: argocd
data:
server.insecure: 'true'

Apply the config:

kubectl apply -f k3s-argocd/argocd-config-map.yaml
kubectl rollout restart deployment argocd-server -n argocd

TLS Certificate with cert-manager​

ArgoCD uses cert-manager to automatically provision Let's Encrypt certificates via DNS-01 challenge with Cloudflare.

ClusterIssuer (k3s-argocd/clusterissuer.yaml):

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-dns01
spec:
acme:
email: your-[email protected]
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-dns01-account-key
solvers:
- dns01:
cloudflare:
email: your-[email protected]
apiTokenSecretRef:
name: cloudflare-api-token-secret
key: api-token

Certificate (k3s-argocd/argocd-certificate.yaml):

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: argocd-tls
namespace: argocd
spec:
secretName: argocd-server-tls
issuerRef:
name: letsencrypt-dns01
kind: ClusterIssuer
dnsNames:
- argocd.yourdomain.com

Replace argocd.yourdomain.com with your actual ArgoCD domain.

Cloudflare API Token Secret:

kubectl create secret generic cloudflare-api-token-secret \
--from-literal=api-token=<YOUR_CLOUDFLARE_API_TOKEN> \
-n cert-manager

Apply the certificate:

kubectl apply -f k3s-argocd/clusterissuer.yaml
kubectl apply -f k3s-argocd/argocd-certificate.yaml

Ingress Configuration​

The ingress resource (k3s-argocd/argocd-ingress.yaml) exposes ArgoCD through Traefik with TLS:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
cert-manager.io/cluster-issuer: 'letsencrypt-dns01'
traefik.ingress.kubernetes.io/router.entrypoints: 'websecure'
traefik.ingress.kubernetes.io/router.tls: 'true'
spec:
ingressClassName: traefik
rules:
- host: argocd.yourdomain.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 80
tls:
- hosts:
- argocd.yourdomain.com
secretName: argocd-server-tls

Replace argocd.yourdomain.com with your actual ArgoCD domain.

Apply the ingress:

kubectl apply -f k3s-argocd/argocd-ingress.yaml

Verify Certificate Status​

kubectl describe certificate argocd-tls -n argocd

App-of-Apps Pattern​

This cluster uses the App-of-Apps pattern to manage all applications declaratively through Git. The root applications manage child applications, creating a hierarchical structure.

Root Applications​

Two root applications manage the entire application landscape:

  1. Root App (k3s-argocd-app-of-apps/root-app.yaml) - Manages application deployments
  2. Root Projects App (k3s-argocd-app-of-apps/root-projects-app.yaml) - Manages ArgoCD projects

Root App:

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

Root Projects App:

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

Replace your-org/argocd-app-of-apps with your actual Git repository.

Application Structure​

The root app manages applications defined in your Git repository (from k3s-argocd-app-of-apps/apps/). Examples of common applications you might manage:

  • longhorn - Distributed block storage
  • cloudnative-pg-operator - PostgreSQL operator
  • redis - Redis deployment
  • monitoring - Prometheus/Grafana stack
  • your-app-dev - Your application development environment
  • your-app-prod - Your application production environment

The exact applications depend on your infrastructure needs and what you define in your Git repository.

Projects​

ArgoCD projects (k3s-argocd-app-of-apps/projects/) provide isolation and access control:

  • cloudnativepg-project - For CloudNativePG operator and managed clusters
  • redis-project - For Redis deployments

Example project:

apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: cloudnativepg-project
namespace: argocd
spec:
description: Project for CloudNativePG operator and managed Postgres clusters
sourceRepos:
- '*'
destinations:
- namespace: argocd
server: https://kubernetes.default.svc
- namespace: cnpg-system
server: https://kubernetes.default.svc
- namespace: '*'
clusterResourceWhitelist:
- group: '*'
kind: '*'
namespaceResourceWhitelist:
- group: '*'
kind: '*'

Managing Applications​

Adding a New Application​

  1. Create application manifest in your Git repository (e.g., argocd-app-of-apps/apps/):
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: my-new-app
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/your-org/your-repo.git
targetRevision: main
path: k8s
destination:
server: https://kubernetes.default.svc
namespace: my-app
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
  1. Commit and push to the Git repository

  2. ArgoCD automatically syncs the new application (if automated sync is enabled)

Manual Sync​

If automated sync is disabled, sync manually:

# Via CLI
argocd app sync my-new-app

# Via UI
# Navigate to the application and click "Sync"

Sync Policies​

Common sync policy configurations:

Automated with Self-Heal:

syncPolicy:
automated:
prune: true # Delete resources removed from Git
selfHeal: true # Automatically sync if cluster state drifts

Manual Sync:

syncPolicy:
syncOptions:
- CreateNamespace=true

Server-Side Apply:

syncPolicy:
syncOptions:
- ServerSideApply=true
- ApplyOutOfSyncOnly=true

Accessing ArgoCD​

Web UI​

Once the ingress is configured, access ArgoCD at:

  • https://argocd.yourdomain.com (replace with your actual domain)

Initial Admin Password​

Get the initial admin password:

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

CLI Access​

Install ArgoCD CLI:

# Linux
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj-labs/argocd-operator/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd

Login:

argocd login argocd.yourdomain.com

Replace argocd.yourdomain.com with your actual ArgoCD domain.

Vault Integration​

ArgoCD is configured to use HashiCorp Vault for secret management via the ArgoCD Vault Plugin (AVP). This allows secrets to be stored in Vault and injected during application sync.

Configuration​

The values.yaml configures:

  • Vault agent sidecar for token management
  • AVP plugin for secret injection
  • Vault connection details

See k3s-argocd/values.yaml and k3s-argocd/vault-agent-config.yaml for details.

Using Vault Secrets in Applications​

In your Helm values or Kubernetes manifests, reference Vault secrets:

# In values.yaml
database:
password: <vault:secret/data/myapp#password>

The AVP plugin will replace these placeholders with actual values from Vault during sync.

Verification​

Check ArgoCD Status​

# Check pods
kubectl get pods -n argocd

# Check applications
kubectl get applications -n argocd

# Check sync status
argocd app list

Verify Root Applications​

kubectl get applications -n argocd | grep root

You should see:

  • root-app - Synced
  • root-projects-app - Synced

Check Application Health​

# Via CLI
argocd app get <app-name>

# Via UI
# Navigate to the application in the ArgoCD UI

Troubleshooting​

Application Stuck in Syncing​

  1. Check application events:

    kubectl describe application <app-name> -n argocd
  2. Check ArgoCD logs:

    kubectl logs -n argocd deployment/argocd-application-controller
    kubectl logs -n argocd deployment/argocd-repo-server
  3. Check sync status:

    argocd app get <app-name>

Certificate Issues​

  1. Check certificate status:

    kubectl describe certificate argocd-tls -n argocd
  2. Check cert-manager logs:

    kubectl logs -n cert-manager deployment/cert-manager
  3. Verify DNS configuration:

    dig argocd.yourdomain.com

Vault Integration Issues​

  1. Check Vault agent logs:

    kubectl logs -n argocd deployment/argocd-repo-server -c vault-agent
  2. Verify Vault connection:

    kubectl exec -n argocd deployment/argocd-repo-server -c vault-agent -- vault status
  3. Check AVP plugin:

    kubectl logs -n argocd deployment/argocd-repo-server | grep avp

References​