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.
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.
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.
We’re looking at a single control plane approach:
First, get Crossplane running on your cluster. I recommend using Helm for this.
Bashhelm 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:
YAMLapiVersion: pkg.crossplane.io/v1 kind: Provider metadata: name: provider-aws spec: package: xpkg.upbound.io/upbound/provider-aws:v0.40.0
Crossplane needs credentials to talk to your cloud provider. Create a Kubernetes secret containing your AWS access keys and map it to a ProviderConfig.
YAMLapiVersion: aws.upbound.io/v1beta1 kind: ProviderConfig metadata: name: default spec: credentials: source: Secret secretRef: namespace: crossplane-system name: aws-creds key: creds
Now, instead of Terraform, you define your infrastructure in YAML. Here’s an example of an S3 bucket:
YAMLapiVersion: 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
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:
YAMLapiVersion: 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.
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:
crossplane-system namespace for resource exhaustion.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.
Learn to implement Kubernetes Policy as Code using Crossplane and OPA Gatekeeper. Secure your infrastructure with automated guardrails in a GitOps workflow.
Read moreMaster GitOps with Argo CD for robust Kubernetes CD. Learn how to implement declarative infrastructure and automate your deployments with this step-by-step guide.