Upjet
A code generator and runtime that turns a Terraform provider into a Crossplane provider, so Terraform’s resource code sits behind Kubernetes-style managed resources.
CNCF · Graduated- What it is
- Infrastructure as code
- Part of
- Built on Crossplane
- Type
- Open source
- Foundation
- Cloud Native Computing Foundation, graduated. Part of Crossplane. Part of the Linux Foundation
- License
- Apache 2.0
- Facts checked
- 13 October 2026
What it is
At build time Upjet reads a Terraform provider’s schema and generates Go types, CRDs and reconciling controllers that follow Crossplane’s resource model, plus example manifests and API docs scraped from the Terraform documentation. The provider author configures each resource in Go: its external name (how it is identified and imported), references to other resources, sensitive fields, and whether calls are sync or async. Only resources with an external name configuration get generated. Upjet builds the official AWS, Azure and GCP providers and more than 50 community ones, such as Grafana and GitHub.
At runtime the generated controllers run Terraform’s resource logic while users see only Kubernetes objects. The first versions wrote JSON and ran the Terraform CLI for every operation. Since 2024 the controllers call the provider’s Go code directly (Terraform Plugin SDK v2) or speak its plugin protocol (Plugin Framework), with the CLI kept as one of three modes. Upjet v2 (October 2025) generates both the legacy cluster-scoped resources and namespaced ones for Crossplane v2, in API groups with an .m. such as s3.aws.m.upbound.io.
Why teams pick it
- Broad coverage fast: anything with a Terraform provider can become a set of managed resources, reusing resource logic the Terraform community already maintains
- Skipping the Terraform CLI made providers much cheaper: creating 1,000 AWS RolePolicyAttachments, average time to ready fell from 19.55 minutes to about 1 second and peak memory from 1.79 GiB to 421 MiB
- Generates the repetitive parts at scale (CRDs, controllers, references, examples, docs) and adds frameworks for API version conversion and migrating between providers
- Apache 2.0 under the Crossplane project; without the Terraform CLI, providers avoid HashiCorp’s BSL license, and Terraform providers themselves stay MPL 2.0
Watch out for
- Resources behave the way the underlying Terraform provider does, and each one needs hand-written external name configuration before it is generated
- Its own README says to write a native provider instead when no Terraform provider exists, when you need custom reconcile logic or exact control of the API, or when performance needs exceed what Terraform execution allows
- Generated providers are large: a whole cloud means hundreds of CRDs, and Terraform schema changes have to be managed as CRD version changes
- It is a framework for provider authors in Go; most Crossplane users only meet it through the providers it generates
How it fits with other technologies
Configuring two GitHub resources in an Upjet provider, then `make generate` · go
// config/external_name.go: which Terraform resources to generate, and how each is identified
var ExternalNameConfigs = map[string]config.ExternalName{
"github_repository": config.NameAsIdentifier,
"github_branch": config.TemplatedStringAsIdentifier("branch", "{{ .parameters.repository }}:{{ .external_name }}:{{ .parameters.source_branch }}"),
}
// config/namespaced/branch/config.go
func Configure(p *config.Provider) {
p.AddResourceConfigurator("github_branch", func(r *config.Resource) {
r.ShortGroup = "branch"
// a Branch refers to a Repository object instead of a raw name
r.References["repository"] = config.Reference{
Type: "github.com/myorg/provider-github/apis/namespaced/repository/v1alpha1.Repository",
}
})
}See it in a real stack
No architecture uses Upjet yet. Drop it into the playground and wire it up yourself.Open playground →Where it came from2021–2025
Crossplane is only as useful as its providers, and writing a controller by hand for every cloud resource was slow; the sheer number of resources was “way too many for one to develop one by one”. Earlier code generators, such as crossplane-tools and the AWS Controllers for Kubernetes pipeline, still needed resource-specific logic written by hand, because cloud APIs are not consistent enough for one generic pipeline. Terraform providers had already solved the same problem of talking to each cloud’s API.
Muvaffak Onus’s Terrajet design, approved by the Crossplane maintainers, chose to drive the Terraform CLI with generated JSON. It called the CLI “not the cleanest option”, but the fastest route to broad coverage, reusing Terraform’s tested diff and import logic. Terrajet appeared in 2021 with AWS, Azure and GCP providers; Upbound then created Upjet as its successor in 2022 to build its own official providers, and Terrajet was archived.
In September 2023 Upbound donated Upjet and its AWS, Azure and GCP providers to the Crossplane project. The Crossplane blog called it a governance change that stops Upbound from ever relicensing them under something more restrictive. In March 2024 the providers stopped running the Terraform CLI, which Upbound said was never built for continuous reconciliation, and called the provider code directly, for up to four times lower running cost. Upjet 1.0 shipped in November 2023, and 2.0, for Crossplane v2’s namespaced resources, in October 2025.
Why it’s built this way
- Generate from Terraform providers, not from cloud APIs
- Cloud APIs are too inconsistent for one generic generator, and Terraform providers already handle each API’s quirks, so the community could “build on top of the shoulders of the giants”.
- Drive the Terraform CLI at first
- Providers are written to be consumed by the CLI and hide much of their logic; calling the CLI gave Terraform’s own diff and import behavior and the quickest path to coverage, behind an interface that could change later.
- Later bypass the CLI
- The CLI caused heavy overhead in a control plane that reconciles continuously, and fixing it would have been too large an effort; calling providers directly also removed the BSL question.
- Donate it to the CNCF project
- Moving from a vendor project under Upbound’s CLA to Crossplane governance means Upjet and its providers must stay Apache 2.0.
Timeline
- 2021
Terrajet, Upjet’s predecessor, starts generating Crossplane providers through the Terraform CLI
- 2022
Upbound releases Upjet as Terrajet’s successor
- 2023
Donated to the Crossplane project in September; Upjet 1.0 in November
- 2024
Generated providers drop the Terraform CLI and call provider code directly
- 2025
Upjet 2.0 in October generates namespaced resources for Crossplane v2
- Crossplane design doc: Terrajet, code generation for Terraform-based providers
- crossplane/terrajet on GitHub (archived; points to Upjet)
- Crossplane blog: Crossplane expands charter and accepts Upbound’s donation of Upjet providers (Sep 2023)
- Crossplane blog: New providers donated by Upbound bring up to 4x cost savings (Mar 2024)
- Upjet releases on GitHub (v1.0.0 Nov 2023, v2.0.0 Oct 2025)
Sources7 · facts checked 13 October 2026
- Upjet README (architecture, execution modes, providers built with it, when to use it)
- Upjet docs: Generating a Crossplane provider
- Upjet docs: Upgrading the provider to Upjet v2 (namespaced MRs)
- Crossplane blog: New providers donated by Upbound bring up to 4x cost savings (Mar 2024)
- Crossplane docs: Disabling unused managed resources (provider CRD counts)
- Crossplane docs: Get started with managed resources (s3.aws.m.upbound.io)
- Upjet releases on GitHub