Back to Blog
Software EngineeringTechnologyJune 19, 20263 min read

Building a GitOps Pipeline with Argo CD and Crossplane

Master GitOps with Argo CD and Crossplane to manage infrastructure as code. Learn how to unify your Kubernetes deployment strategy for apps and cloud resources.

GitOpsArgo CDCrossplaneKubernetesInfrastructure as CodeAWSDevOpsLinuxServer

Building a GitOps Pipeline with Argo CD and Crossplane

I’ve spent years fighting the disconnect between "application code" and "infrastructure code." You know the drill: your app is deployed via Helm, but your RDS instance or S3 bucket is sitting in a separate Terraform state file that nobody touched since 2022.

If you're tired of drift and manual interventions, it’s time to unify your stack. By combining GitOps principles with Argo CD and Crossplane, you can manage your entire infrastructure as Kubernetes custom resources.

Why Crossplane and Argo CD?

Most teams use Argo CD to keep their Kubernetes manifests in sync with Git. But what about the cloud resources those apps depend on?

Crossplane extends the Kubernetes API. It allows you to define cloud resources (like AWS RDS, Google Cloud SQL, or Azure Blob Storage) using standard YAML. When you pair this with Argo CD, your entire lifecycle—from VPC creation to pod deployment—lives in your Git repository. If it’s not in Git, it doesn't exist.

The Architecture

We’re looking at a single control plane approach:

  1. Git Repository: The source of truth for all manifests.
  2. Argo CD: The GitOps controller that watches the repo and reconciles state.
  3. Crossplane: The provider that talks to your cloud APIs (AWS/GCP/Azure) to provision infrastructure.

Step 1: Install Crossplane

First, get Crossplane running on your cluster. I recommend using Helm for this.

Bash
helm repo add crossplane-stable https://charts.crossplane.io/stable
helm repo update
helm install crossplane crossplane-stable/crossplane --namespace crossplane-system --create-namespace

Once it's up, you need a provider. If you're on AWS, install the provider-aws:

YAML
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
  name: provider-aws
spec:
  package: xpkg.upbound.io/upbound/provider-aws:v0.40.0

Step 2: Configure the Provider Secret

Crossplane needs credentials to talk to your cloud provider. Create a Kubernetes secret containing your AWS access keys and map it to a ProviderConfig.

YAML
apiVersion: aws.upbound.io/v1beta1
kind: ProviderConfig
metadata:
  name: default
spec:
  credentials:
    source: Secret
    secretRef:
      namespace: crossplane-system
      name: aws-creds
      key: creds

Step 3: Define Infrastructure as Code

Now, instead of Terraform, you define your infrastructure in YAML. Here’s an example of an S3 bucket:

YAML
apiVersion: s3.aws.upbound.io/v1beta1
kind: Bucket
metadata:
  name: my-app-data-bucket
spec:
  forProvider:
    region: us-east-1
  writeConnectionSecretToRef:
    name: s3-bucket-conn
    namespace: default

Step 4: The GitOps Loop with Argo CD

This is where the magic happens. Point Argo CD at your repository containing both your application Helm charts and your Crossplane infrastructure manifests.

Create an Application in Argo CD to manage the infrastructure:

YAML
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: infra-stack
spec:
  destination:
    server: https://kubernetes.default.svc
    namespace: crossplane-system
  project: default
  source:
    path: infra/aws
    repoURL: https://github.com/my-org/gitops-repo.git
    targetRevision: HEAD
  syncPolicy:
    automated:
      prune: true
      selfHeal: true

When you push a change to infra/aws/bucket.yaml, Argo CD detects the diff, pushes the new state to the Kubernetes API, and Crossplane immediately begins reconciling that change with AWS.

Lessons from Production

I’ve seen teams try to mix Terraform and Crossplane. Don't do it. If you choose Crossplane, commit to it. The biggest friction point isn't the tooling—it's the mindset shift. You have to stop thinking about "provisioning" as a one-time command and start thinking about "reconciliation" as a constant background process.

Key things to watch out for:

  1. Secret Management: Since Crossplane writes connection secrets back to the cluster, ensure you have something like External Secrets Operator or Sealed Secrets to handle sensitive data in Git.
  2. Controller Latency: Crossplane controllers can be chatty. Monitor your crossplane-system namespace for resource exhaustion.
  3. Drift Detection: Argo CD is excellent at detecting drift, but remember that Crossplane also has its own reconciliation loop. You get a double layer of protection, which is great until a manual change in the AWS console causes a "fight" between the two controllers. Stick to the GitOps flow and keep the console off-limits.

Conclusion

Building a Kubernetes CD pipeline that includes Infrastructure as Code is the final step in maturing your DevOps practice. By using Argo CD to drive Crossplane, you eliminate the "it works on my machine" infrastructure nightmare.

You’ve got one cluster, one tool for deployment, and one source of truth. That’s how you scale.

Similar Posts