
August 20, 2026
8 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Crossplane: Kubernetes-Native Infrastructure extends your cluster’s API server to provision and manage external cloud resources—databases, buckets, VPCs—using the same kubectl workflows you already use for application deployments. Instead of maintaining separate Terraform state files or clicking through cloud consoles, you define infrastructure as Kubernetes Custom Resources (CRDs) that reconcile continuously against real-world provider APIs. For teams already operating GitOps pipelines for applications, this unifies infrastructure and application lifecycle management into a single control plane.
If you are evaluating whether to adopt this approach over traditional tooling, understanding the architectural differences is critical before committing production workloads. While my primary expertise lies in Laravel and PHP backend development, I have increasingly integrated Kubernetes-native infrastructure patterns for clients requiring scalable, self-healing platform layers beneath their applications. The shift from static provisioning to continuous reconciliation fundamentally changes how you design deployment pipelines and handle operational failures.
How does Crossplane: Kubernetes-Native Infrastructure differ from Terraform?
The most common question engineers ask is why they should replace working Terraform modules. The distinction is not about syntax preference; it is about control loop architecture. Terraform operates on a plan-apply cycle: you run a command, it makes API calls, records state, and stops. If someone manually changes a security group five minutes later, Terraform will not know until the next manual apply. Crossplane runs as a set of controllers inside your cluster, constantly watching both the desired state (your YAML) and the actual state (cloud provider API). When drift occurs, it reconciles automatically.
This continuous reconciliation model aligns infrastructure management with the same patterns used for CI/CD pipeline automation. You store your resource definitions in Git, use ArgoCD or Flux to sync them, and let the controllers handle the rest. There is no state file to corrupt, no lock to contend with across team members, and no separate CLI to install on every developer machine. The trade-off is complexity: debugging a failing reconciliation requires understanding Kubernetes events, controller logs, and provider-specific status conditions rather than reading a linear Terraform plan output.
| Feature | Terraform | Crossplane |
|---|---|---|
| Execution Model | Imperative plan/apply CLI | Declarative controller loop |
| State Management | External .tfstate file | Kubernetes etcd (native objects) |
| Drift Detection | Manual refresh required | Automatic continuous correction |
| Abstraction | Modules (static) | Compositions (dynamic, typed) |
| Integration Surface | CLI / CI job | Kubernetes API / RBAC / GitOps |
| Multi-tenancy | Workspace / directory isolation | Native namespace + RBAC boundaries |
How do you install providers and configure credentials securely?
Crossplane itself is just an orchestrator; it cannot provision anything without Providers. A Provider is a Kubernetes controller that translates Crossplane resource definitions into specific cloud API calls. As of 2026, the official AWS, GCP, Azure, and Alibaba Cloud providers are stable, alongside community providers for DigitalOcean, Cloudflare, and others.
Installing the AWS Provider
Create a provider.yaml manifest. Pin to a specific version to avoid unexpected breaking changes during upgrades:
<!-- provider.yaml -->
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v1.2.1 Apply it and wait for the provider to become healthy:
kubectl apply -f provider.yaml
kubectl get providers
# NAME INSTALLED HEALTHY PACKAGE AGE
# provider-aws-s3 True True xpkg.upbound.io/upbound/provider-aws-s3:v1.2.1 45s Configuring Credentials Without Secrets in Git
Never commit cloud credentials to your repository. Use the ProviderConfig resource to reference a Kubernetes Secret that is injected via your GitOps tool’s secret management (Sealed Secrets, External Secrets Operator, or Vault):
<!-- 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 On production systems, prefer IRSA (AWS), Workload Identity (GCP), or Managed Identity (Azure) over static access keys. These mechanisms eliminate long-lived credentials entirely and are supported natively by Crossplane providers via credentials.source: InjectedIdentity. This is especially important for Nepal-based teams managing international cloud accounts where credential rotation policies may be difficult to enforce across distributed developers.
What are Composite Resources and how do compositions work?
Raw Managed Resources (MRs) map 1:1 to cloud APIs. Exposing these directly to application teams defeats the purpose of platform engineering. Compositions let you create higher-level abstractions called Composite Resource Definitions (XRDs) that bundle multiple MRs into a single, opinionated interface.
Define an XRD that exposes only the parameters your developers need:
<!-- xrd.yaml -->
apiVersion: apiextensions.crossplane.io/v1
kind: CompositeResourceDefinition
metadata:
name: xpostgresqlinstances.platform.example.com
spec:
group: platform.example.com
names:
kind: XPostgreSQLInstance
plural: xpostgresqlinstances
claimNames:
kind: PostgreSQLInstance
plural: postgresqlinstances
versions:
- name: v1alpha1
served: true
referenceable: true
schema:
openAPIV3Schema:
type: object
properties:
spec:
type: object
properties:
storageGB:
type: integer
description: Requested storage in gigabytes
tier:
type: string
enum: [standard, high-memory]
required: [storageGB] Then bind it to real infrastructure via a Composition. This is where you encode organizational policy—encryption standards, networking rules, backup windows—so application teams cannot accidentally violate compliance requirements. When building custom admin panels or SaaS backends, this abstraction prevents junior developers from provisioning oversized databases or misconfigured public endpoints.
How do you handle drift correction and operational debugging?
Drift correction is Crossplane’s killer feature, but it can also cause confusion if you do not understand the reconciliation semantics. When a managed resource is modified outside Kubernetes (e.g., via AWS Console), the controller detects the discrepancy on its next reconcile interval (default: 10 minutes) and reverts the change. This is desirable for enforcing policy but dangerous during incident response when an engineer needs to make an emergency manual fix.
To pause reconciliation temporarily, annotate the resource:
kubectl annotate rdsinstance.database.aws.upbound.io/my-db \
crossplane.io/paused=true Remove the annotation when the incident is resolved. Always document this escape hatch in your runbooks.
Debugging Failed Reconciliations
When a resource stays in Synced: False, follow this diagnostic sequence:
- Check the resource events:
kubectl describe <resource> <name>— the Events section usually contains the exact API error message from the cloud provider. - Inspect controller logs:
kubectl logs -n crossplane-system deploy/provider-aws-s3-xxx— look for rate limiting, authentication failures, or dependency errors. - Verify ProviderConfig health: Ensure credentials have not expired and IAM permissions match the resource type being provisioned.
- Check management policies: If using
managementPolicies: Observe, the controller will never create or update; confirm you intended this mode.
A common mistake in 2026 is assuming that deleting a Claim deletes all underlying resources immediately. Deletion follows the deletionPolicy specified in the Composition or Managed Resource. If set to Retain, the cloud resource persists after the Kubernetes object is removed. Always verify this setting before decommissioning environments to avoid orphaned billing resources—a painful lesson I have seen teams learn the hard way on client projects.
When should you choose Crossplane over traditional IaC tools?
Crossplane is not a universal replacement. It excels when your organization already operates Kubernetes as a platform and wants to extend GitOps practices to infrastructure. It struggles when you need simple one-off provisioning, lack Kubernetes operational maturity, or manage resources across dozens of unrelated accounts without a central control plane.
For Nepal-based organizations adopting cloud infrastructure, consider the operational overhead honestly. If your team is still mastering basic Kubernetes deployments, adding Crossplane introduces significant cognitive load. Start with Terraform or Pulumi, build K8s competency first, then migrate to Crossplane when you genuinely need unified GitOps for both apps and infrastructure. The cloud hosting landscape in Nepal often involves hybrid setups where local VMs coexist with international cloud resources; Crossplane can manage both, but only if your team can operate the control plane reliably.
Practical Next Steps for Adopting Crossplane
If you decide to proceed, start small. Provision a single non-critical resource type (S3 bucket, DNS record) in a staging cluster. Validate your GitOps pipeline end-to-end before composing complex multi-resource abstractions. Invest time in writing clear XRD schemas with validation and documentation—these are your internal product contracts. Monitor controller resource consumption; Crossplane providers can be memory-intensive at scale, and right-sizing requests prevents node pressure issues.
Crossplane: Kubernetes-Native Infrastructure represents a mature paradigm shift for platform teams ready to treat infrastructure as truly declarative, self-healing, and API-driven. It is not simpler than Terraform, but it is more aligned with modern Kubernetes-native operations. Evaluate it against your actual operational capabilities, not hype cycles. If you need guidance on integrating this pattern with existing Laravel or PHP application deployments, or want to discuss whether your infrastructure warrants this level of abstraction, reach out to discuss your specific architecture.

