Cloud Native 37 min read

How to Build Multi‑Cloud GitOps 2.0 with ArgoCD and Crossplane

This guide walks through implementing a GitOps 2.0 workflow that combines ArgoCD and Crossplane to manage both application deployments and multi‑cloud infrastructure as declarative YAML stored in Git, covering architecture, environment setup, step‑by‑step installation, example use cases, best‑practice recommendations, troubleshooting, monitoring, and backup strategies.

Ops Community
Ops Community
Ops Community
How to Build Multi‑Cloud GitOps 2.0 with ArgoCD and Crossplane

Overview

In the cloud‑native era, managing infrastructure across multiple clouds and clusters with rapid iteration requires a declarative, version‑controlled approach. GitOps uses a Git repository as the single source of truth, providing versioning, auditability, and automation for both applications and underlying cloud resources.

Technical Features

Declarative Management : All infrastructure and application configurations are stored as YAML in Git.

Automated Synchronization : ArgoCD continuously watches the repository and applies changes to target clusters.

Multi‑Cloud Abstraction : Crossplane exposes cloud resources (RDS, S3, VPC, etc.) as Kubernetes CRDs, enabling a unified API.

Drift Detection & Self‑Healing : ArgoCD detects state drift and can automatically reconcile or alert.

Audit & Traceability : Every change is a Git commit, providing a complete history.

Applicable Scenarios

Multi‑cloud environment management (AWS, Azure, GCP).

Large‑scale cluster orchestration (dozens to hundreds of clusters).

Self‑service infrastructure requests for development teams.

Disaster‑recovery and environment cloning.

Compliance‑driven audit requirements.

Environment Requirements

Kubernetes 1.24+ (recommended 1.26+ for CRD v1 support).

ArgoCD 2.8+ (2.9+ for full ApplicationSet support).

Crossplane 1.14+.

Helm 3.10+ for installing ArgoCD and Crossplane.

kubectl 1.24+ and Git client.

Step‑by‑Step Implementation

Preparation

Verify cluster health and version.

Install Helm, add required repos, and update.

# Check cluster status
kubectl cluster-info
kubectl get nodes
kubectl version --short
kubectl top nodes
kubectl get pods -A
# Install Helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
helm repo add argo https://argoproj.github.io/argo-helm
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm krew install crossplane

Install ArgoCD

# Create namespace
kubectl create namespace argocd
# Install via Helm
helm install argocd argo/argo-cd \
  --namespace argocd \
  --version 5.51.0 \
  --set server.service.type=LoadBalancer \
  --set configs.params."application.namespaces"="*" \
  --set server.extraArgs[0]="--insecure"
# Wait for pods
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
# Get initial admin password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d

Install Crossplane

# Install Crossplane core
helm install crossplane crossplane-stable/crossplane \
  --namespace crossplane-system \
  --create-namespace \
  --version 1.14.5 \
  --set args[0]="--enable-composition-revisions"
# Verify installation
kubectl get pods -n crossplane-system
kubectl api-resources | grep crossplane

Configure Cloud Provider (AWS example)

# Provider CRD
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/upbound/provider-aws:v0.41.0
  controllerConfigRef:
    name: aws-provider-config
---
# ControllerConfig with resource limits
apiVersion: pkg.crossplane.io/v1alpha1
kind: ControllerConfig
metadata:
  name: aws-provider-config
spec:
  resources:
    limits:
      cpu: "1000m"
      memory: "2Gi"
    requests:
      cpu: "500m"
      memory: "1Gi"
  args:
    - --poll=5m
    - --max-reconcile-rate=50

Create ArgoCD Application for Infrastructure

# infrastructure-app.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: infrastructure
  namespace: argocd
spec:
  project: default
  source:
    repoURL: https://github.com/your-org/gitops-infra.git
    targetRevision: main
    path: infrastructure
  destination:
    server: https://kubernetes.default.svc
    namespace: default
  syncPolicy:
    automated:
      prune: true
      selfHeal: true
    syncOptions:
      - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m

Validate Deployment

Check Application status: argocd app get infrastructure --refresh Verify Crossplane resources: kubectl get managed Confirm cloud resources (e.g., S3 bucket) exist.

Example Use Cases

Self‑Service Database Request

# PostgreSQL claim (developers create this file)
apiVersion: database.example.com/v1alpha1
kind: PostgreSQLInstance
metadata:
  name: my-app-db
  namespace: my-app
spec:
  parameters:
    storageGB: 50
    instanceClass: db.t3.medium
    engine: "14.5"
  writeConnectionSecretToRef:
    name: my-app-db-conn

Developers commit the claim, ArgoCD syncs it, Crossplane provisions an RDS instance, and the application reads connection details from the generated secret.

Multi‑Cluster Application Distribution

# ApplicationSet for deploying nginx to many clusters
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: nginx-multi-cluster
  namespace: argocd
spec:
  generators:
    - list:
        elements:
          - cluster: prod-us-east-1
            url: https://prod-us-east-1.k8s.local
          - cluster: prod-us-west-2
            url: https://prod-us-west-2.k8s.local
          - cluster: prod-eu-west-1
            url: https://prod-eu-west-1.k8s.local
          - cluster: staging-us-east-1
            url: https://staging-us-east-1.k8s.local
  template:
    metadata:
      name: '{{cluster}}-nginx'
    spec:
      project: default
      source:
        repoURL: https://github.com/your-org/gitops-infra.git
        targetRevision: main
        path: apps/nginx/overlays/{{cluster}}
      destination:
        server: '{{url}}'
        namespace: nginx
      syncPolicy:
        automated:
          prune: true
          selfHeal: true
        syncOptions:
          - CreateNamespace=true

The ApplicationSet generates one Application per cluster, ensuring consistent deployment across all environments.

Best Practices & Security

Performance Tuning : Increase application.sync.concurrency in ArgoCD ConfigMap, allocate sufficient CPU/memory for ArgoCD server and controller.

Provider Resource Limits : Use ControllerConfig to set CPU/memory limits for each cloud provider to avoid OOM.

Repository Structure : Separate apps/, infrastructure/, and clusters/ directories for clear ownership.

Secret Management : Store credentials with Sealed Secrets or External Secrets; never keep plain text in Git.

RBAC Least‑Privilege : Define ArgoCD policy.csv and rbac-cm to restrict teams to only the resources they need.

Network Policies : Isolate Crossplane and ArgoCD pods, allow only required outbound ports (443 for cloud APIs, 53 for DNS).

Known Issues & Compatibility

ArgoCD 2.8+ requires Kubernetes 1.24+.

Crossplane 1.14+ requires Kubernetes 1.21+.

AWS Provider in China regions needs custom endpoints; Azure may hit subscription quota limits.

Long‑running cloud resources (e.g., RDS) need increased reconcile timeout.

Troubleshooting & Monitoring

Log Access

# ArgoCD controller logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller -f
# ArgoCD server logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-server -f
# Crossplane core logs
kubectl logs -n crossplane-system deployment/crossplane -f
# Provider logs (example AWS)
kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=provider-aws -f

Common Problems

Application stuck in Progressing : Check health checks, add argocd.argoproj.io/sync-wave annotations, or adjust custom health scripts.

Crossplane resource stuck in Creating : Verify provider IAM permissions, increase --poll interval, and inspect provider logs.

Git sync failures : Ensure repository credentials are up‑to‑date, enable SSH or HTTPS with proper tokens, and verify network connectivity.

Prometheus Metrics

Key metrics to monitor include argocd_app_sync_total, argocd_app_reconcile_duration_seconds, crossplane_managed_resource_ready, and controller reconcile error counters. Configure ServiceMonitors and PrometheusRule alerts for sync failures, out‑of‑sync applications, and resources that remain unready for more than 10 minutes.

Backup & Recovery

# Full backup script (run daily)
#!/bin/bash
set -e
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
BACKUP_DIR="/backup/gitops/$TIMESTAMP"
mkdir -p "$BACKUP_DIR"
# ArgoCD
kubectl get applications -n argocd -o yaml > "$BACKUP_DIR/argocd-apps.yaml"
kubectl get appprojects -n argocd -o yaml > "$BACKUP_DIR/argocd-projects.yaml"
# Crossplane
kubectl get providers -o yaml > "$BACKUP_DIR/crossplane-providers.yaml"
kubectl get compositeresourcedefinitions -o yaml > "$BACKUP_DIR/xrds.yaml"
kubectl get compositions -o yaml > "$BACKUP_DIR/compositions.yaml"
kubectl get managed -o yaml > "$BACKUP_DIR/managed.yaml"
# Git repository (mirror)
cd /tmp
git clone --mirror https://github.com/your-org/gitops-infra.git "$BACKUP_DIR/git"
# Archive and upload to S3
tar -czf "gitops-backup-$TIMESTAMP.tar.gz" -C "$BACKUP_DIR" .
aws s3 cp "gitops-backup-$TIMESTAMP.tar.gz" s3://my-backup-bucket/gitops/
# Cleanup old backups (keep 7 days)
find /backup/gitops -name "gitops-backup-*.tar.gz" -mtime +7 -delete

Restoration consists of stopping ArgoCD sync, applying the saved YAML files, restoring the Git mirror, and re‑enabling automated sync.

Conclusion

The GitOps 2.0 stack built with ArgoCD and Crossplane provides a unified, declarative workflow for managing both applications and cloud resources across multiple providers. By following the outlined installation steps, security hardening measures, monitoring setup, and backup strategy, teams can achieve reliable, auditable, and scalable infrastructure automation while empowering developers with self‑service capabilities.

Kubernetesmulti-cloudGitOpsInfrastructure as CodeArgoCDCrossplane
Ops Community
Written by

Ops Community

A leading IT operations community where professionals share and grow together.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.