Provider setup pattern
Every Provider in Crossplane follows the same three-step shape: install the package, give it credentials through a ProviderConfig, then reference the config from your MRs. The 101 path walked you through it once with provider-helm. This page collects the variations — credential sources, cluster vs namespaced ProviderConfig, version pinning, where to find providers — so you don't have to re-derive them every time.
5.1 The shape
Three objects, in order:
Provider(pkg.crossplane.io/v1). A package whose install spawns a long-running Pod incrossplane-system. The Pod registers CRDs and reconciles MRs of those kinds.ProviderConfig(orClusterProviderConfig— see §5.3). Tells the Provider Pod how to authenticate. Most commonly references a Secret; cloud-native auth (IRSA, Workload Identity, Managed Identity) is the no-Secret alternative.- MRs. Cluster- or namespace-scoped Custom Resources the Provider reconciles. Each carries a
providerConfigRefpointing at aProviderConfig(orClusterProviderConfig) by name.
If any of those three is missing or unhealthy, MRs sit unreconciled — typically with a Cannot connect to the API reason on .status.conditions.
5.2 Pin the version
Always pin the package version. :latest works but bites you the next time the provider has a breaking schema change.
kubectl apply -f - <<'EOF'
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v2.5.3
EOF
Find current versions on the Crossplane Marketplace. Each provider page lists every published version, the CRDs it ships, and whether the family auto-pulls subordinate packages.
5.3 ClusterProviderConfig vs ProviderConfig
Crossplane v2 ships two ProviderConfig kinds for each provider:
| Kind | Scope | Pick when |
|---|---|---|
ClusterProviderConfig | Cluster-scoped | One credential serves the whole cluster — typical for cloud accounts where every team writes through the same IAM identity. Namespaced MRs in any namespace can reference it. |
ProviderConfig | Namespace-scoped | Each tenant namespace carries its own credential — typical for SaaS providers (provider-github, provider-vault) when different teams need different identities. Only MRs in the same namespace can reference it. |
Both kinds live under the v2-namespaced API group *.m.<provider>.io (e.g. aws.m.upbound.io, helm.m.crossplane.io). The .m. infix marks the namespaced flavour. Older cluster-only kinds at *.<provider>.io (e.g. aws.upbound.io, the github.upbound.io group provider-github uses) predate v2 and are still around for back-compat.
Namespaced MRs in v2 require an explicit providerConfigRef.kind so the controller knows which kind to look up:
spec:
providerConfigRef:
kind: ClusterProviderConfig # or: ProviderConfig
name: default
Forget this on a v2 namespaced MR and the provider will refuse to reconcile it with a kind is required error.
5.4 Credentials
Three patterns cover most providers.
Secret-based (default)
Every Provider supports it; some only support it. The Secret's value is whatever the Provider's docs specify — often a JSON blob, sometimes a CLI-format file (the AWS shared-credentials file), sometimes a single token.
apiVersion: aws.m.upbound.io/v1beta1
kind: ClusterProviderConfig
metadata:
name: default
spec:
credentials:
source: Secret
secretRef:
name: aws-creds
namespace: crossplane-system
key: credentials
Keep the Secret in crossplane-system — that's the namespace the Provider Pod runs in, and most providers default-search there. The Secret can sit anywhere reachable to the Pod's ServiceAccount, but breaking the convention costs you a chase later when the Provider can't read the key.
Cloud-native (no Secret)
When the Provider Pod itself has a workload identity, the credential is implicit and there's no Secret to manage. AWS IRSA, GCP Workload Identity, and Azure Managed Identity all set source: InjectedIdentity (the exact key varies — check the Provider's docs):
spec:
credentials:
source: InjectedIdentity
This is the production-grade move on a real cloud cluster: no static keys to rotate, blast radius scoped by the Pod's IAM role.
Operator-staged (managed clusters)
When the cluster operator pre-provisions a credential outside GitOps (because the secret can't live in git), the ProviderConfig points at a Secret the operator maintains. provider-github on your workshop cluster uses this pattern — a single GitHub App credential the operator stages into the crossplane-system namespace for you.
5.5 Provider families
Modern Upbound providers are published as families — one small package per cloud service, sharing a single provider-family-<cloud> package for auth. Installing provider-aws-s3 automatically pulls provider-family-aws, where the ClusterProviderConfig CRD lives.
Pick the smallest family member you need. The old monolithic provider-aws shipped ~1500 CRDs; provider-aws-s3 ships ~30. Smaller install, fewer schema upgrades to track, and you can compose multiple family members on the same ClusterProviderConfig:
kubectl apply -f - <<'EOF'
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-s3
spec:
package: xpkg.upbound.io/upbound/provider-aws-s3:v2.5.3
---
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-aws-rds
spec:
package: xpkg.upbound.io/upbound/provider-aws-rds:v2.5.3
EOF
Both share the aws.m.upbound.io ClusterProviderConfig you already applied. No second credential needed.
5.6 Where to from here
The 3xx category puts this pattern to work against real backends:
- Create resources in GitHub
- Create resources in AWS
- Create resources in GCP
- Create resources in Azure
- (Guided) Create resources in Aruba
Each one follows the shape on this page: install, ClusterProviderConfig pointing at a Secret, one MR. Read this page once and you can predict every code block in those modules before clicking.