Radius: Microsoft's Open-Source Application Platform for Cloud-Native Teams
Platform engineering has a recurring problem: the gap between what developers want (self-service infrastructure, consistent environments, no cloud-specific knowledge required) and what platform teams can deliver (compliant, secure, cost-controlled infrastructure that follows organizational policy). Kubernetes made deploying containers easier, but it widened that gap — every developer now needs to understand Deployments, Services, Ingresses, ConfigMaps, secrets, namespace hygiene, and cloud-specific integrations just to run an application.
Radius is Microsoft’s attempt to close that gap with a different abstraction layer. Rather than making Kubernetes easier to use, Radius adds an application-centric model on top of it: developers describe what their application needs (a container, a Redis cache, a message queue), platform teams describe how those needs are implemented in each environment (Terraform modules, Bicep templates, cloud-specific configurations), and Radius wires them together at deploy time.
This is the core idea. Developers write cloud-agnostic application definitions. Platform teams write infrastructure recipes. Radius connects the two without either team needing to know the details of the other’s work.
What Problem Radius Solves
Consider a typical cloud-native application: a frontend container, a backend API, a Redis cache for sessions, a PostgreSQL database, and a message queue. In a vanilla Kubernetes environment, deploying this requires:
- Kubernetes manifests for each container (Deployment, Service, ConfigMap)
- A Helm chart or Kustomize overlay for each environment
- Cloud provider-specific resources (Azure Cache for Redis in prod, a local Redis container in dev)
- Manually injected connection strings and secrets, varying by environment
- Documentation or tribal knowledge about which environment variable means what
When a developer moves this from dev to staging to production, the environment variables change, the backing services change, the cloud providers might change, and the Kubernetes namespaces definitely change. The developer has to know all of this. Or the platform team has to maintain bespoke Helm values files for every environment.
Radius proposes a different model: the application definition doesn’t change between environments. What changes is the environment configuration — specifically, which Recipes implement each resource type and which cloud credentials are available. A developer’s app.bicep is identical whether deploying to a local kind cluster or production AKS. The environment, defined by the platform team, determines what actually gets provisioned.
This is application-centric infrastructure: the application is the unit of deployment, not individual Kubernetes resources.
Architecture
Radius runs as a control plane on top of Kubernetes. It does not replace Kubernetes — it extends it.
┌─────────────────────────────────────────────────────────────────┐
│ Kubernetes Cluster │
│ │
│ ┌───────────────────────────────────────────────────────────┐ │
│ │ radius-system namespace │ │
│ │ │ │
│ │ ┌─────────────────────────┐ ┌────────────────────────┐ │ │
│ │ │ Universal Control Plane │ │ Applications RP │ │ │
│ │ │ (UCP) │ │ (Resource Provider) │ │ │
│ │ │ │ │ │ │ │
│ │ │ - REST API │ │ - App graph tracking │ │ │
│ │ │ - Kubernetes API agg. │ │ - Recipe execution │ │ │
│ │ │ - Multi-plane routing │ │ - Connection inject. │ │ │
│ │ └─────────────────────────┘ └────────────────────────┘ │ │
│ │ │ │
│ │ ┌─────────────────────────┐ ┌────────────────────────┐ │ │
│ │ │ Deployment Engine │ │ Radius Dashboard │ │ │
│ │ │ │ │ (Backstage-based) │ │ │
│ │ │ - Dependency ordering │ │ │ │ │
│ │ │ - Bicep/TF execution │ │ - App graph UI │ │ │
│ │ │ - State tracking │ │ - Resource explorer │ │ │
│ │ └─────────────────────────┘ └────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────────┘ │
│ │
│ CRDs: resources.ucp.dev │
│ DeploymentTemplate / DeploymentResource │
│ Contour (for gateways) │
└─────────────────────────────────────────────────────────────────┘
│
▼
Azure / AWS / Kubernetes (in-cluster)
Universal Control Plane (UCP)
The UCP is the core of Radius. It is a REST API written in Go, exposed through the Kubernetes API Aggregation Layer — meaning you interact with it through the standard Kubernetes API server, and Kubernetes RBAC controls access to all Radius resources. There is no separate API endpoint to secure.
The UCP handles routing between different planes:
- The Radius plane for application resources (containers, connections, environments)
- The Azure plane for Azure resources provisioned through Radius
- The AWS plane for AWS resources
This multi-plane design means the UCP can act as a unified control plane across cloud providers while still delegating actual provisioning to the appropriate backend.
Applications Resource Provider (Applications RP)
The Applications RP is the component that understands Radius-specific resource types: Applications.Core/containers, Applications.Datastores/redisCaches, Applications.Core/environments, and so on. It:
- Maintains the application graph (tracking which resources belong to which application and how they connect)
- Triggers Recipe execution when portable resources are deployed
- Handles Connection injection — computing what environment variables to inject into containers based on declared connections
Deployment Engine
When you rad deploy app.bicep, the CLI sends the Bicep template to the Deployment Engine. This component:
- Parses the ARM JSON produced by Bicep compilation
- Builds a dependency graph of all resources in the template
- Deploys resources in dependency order (secrets before databases, databases before containers)
- Tracks in-progress operations and reports status
The Deployment Engine also supports a Kubernetes-native mode via the DeploymentTemplate CRD. Using rad bicep generate-kubernetes-manifest, you can convert a Bicep template into a DeploymentTemplate Kubernetes resource, enabling GitOps workflows where ArgoCD or Flux manage Radius deployments like any other Kubernetes resource.
How Resources Map to Kubernetes
When Radius deploys a container to Kubernetes, it creates standard Kubernetes objects:
| Radius Resource | Kubernetes Objects Created |
|---|---|
Applications.Core/containers |
apps/Deployment@v1 + core/Service@v1 (if ports defined) |
Applications.Core/gateways |
projectcontour.io/HTTPProxy@v1 via Contour |
| Connections | core/Secret@v1 (environment variables injected into pods) |
| Dapr components | dapr.io/Component@v1alpha1 |
Application-scoped resources deploy by default into a new namespace named <envNamespace>-<appname>. This is configurable via the kubernetesNamespace application extension.
Radius uses Contour as its ingress controller for gateway resources. If you already have an ingress controller, this is worth noting — Contour gets installed alongside your existing setup.
The Application Model
Radius uses Bicep as its primary configuration language. Bicep is Microsoft’s ARM template DSL, but Radius extends it with a custom extension providing Radius-specific resource types. YAML is supported for some configuration, but Bicep is the primary authoring experience.
The central model has four main concepts: Applications, Environments, Connections, and Recipes. They compose cleanly and that composition is what makes the platform work.
Applications
An Application in Radius is a logical grouping of all the resources that make up your service. It is a first-class resource — not a label, not a namespace, but an actual object that Radius tracks.
|
|
Notice what is not in this file: no cloud-specific configuration, no AWS ARNs, no Azure resource IDs, no Kubernetes namespace references, no connection strings. This file is completely portable.
The application and environment parameters are automatically injected by the rad CLI at deploy time — you never need to hardcode them.
Portable Resource Types
Radius ships with a set of built-in portable resource types — abstract resource definitions that platform teams can implement with any backing service:
| Resource Type | Description |
|---|---|
Applications.Datastores/redisCaches |
Redis-compatible cache |
Applications.Datastores/mongoDatabases |
MongoDB-compatible database |
Applications.Datastores/sqlDatabases |
SQL database |
Applications.Messaging/rabbitMQQueues |
RabbitMQ message queue |
Applications.Dapr/stateStores |
Dapr state store |
Applications.Dapr/secretStores |
Dapr secret store |
Applications.Dapr/pubSubBrokers |
Dapr pub/sub broker |
A developer who declares Applications.Datastores/redisCaches doesn’t know or care whether the implementation is a local Redis container, Azure Cache for Redis, or ElastiCache. That decision belongs to the environment and its Recipes.
Custom Resource Types
Beyond the built-in types, platform teams can define their own custom Resource Types using a YAML definition file:
|
|
Register it:
|
|
This is how platform teams extend Radius with organization-specific resource types — a Myorg.Data/postgreSqlDatabases instead of a generic SQL type, with org-specific properties like size tiers.
Environments
An Environment in Radius is a deployment landing zone. It specifies:
- Where workloads run (Kubernetes namespace, cloud region)
- Which cloud provider credentials are available
- Which Recipes implement each Resource Type
The same application definition deploys unchanged to any environment. What differs between dev, staging, and production is the environment’s configuration.
Environment Bicep Definition
|
|
The developer’s app.bicep is identical in both environments. The environment definition controls what gets provisioned. This is the key separation of concerns.
Multiple Named Recipes Per Type
An environment can register multiple named Recipes for the same resource type — not just a default. Developers can then choose which implementation to use:
|
|
A developer who needs premium Redis explicitly requests it:
|
|
Initializing an Environment
|
|
Recipes
Recipes are the platform team’s contribution to the Radius model. They are IaC templates — Bicep or Terraform — that implement a Resource Type. A Recipe for Applications.Datastores/redisCaches knows how to provision a Redis instance; the application definition just asks for one.
The key design principle: Recipes are not tightly coupled to Radius. An existing Terraform module for Redis can be adapted into a Recipe with minimal changes. The only requirement is accepting a context variable that Radius injects.
The context Object
When Radius invokes a Recipe, it injects a context object containing:
context.resource.id— the unique ID of the resource requesting the Recipecontext.resource.name— the resource namecontext.runtime.kubernetes.namespace— the Kubernetes namespace for the deploymentcontext.environment.id— the environment IDcontext.application.id— the application ID
This context is how Recipes generate unique, deterministic resource names and target the correct namespace.
Writing a Bicep Recipe
|
|
The output result block is critical. The values and secrets in this output are what Radius uses to populate connection environment variables in consuming containers.
Writing a Terraform Recipe
|
|
Publishing and Registering Recipes
|
|
Manual Provisioning
When you need to connect to pre-existing infrastructure (a database managed outside of Radius), use resourceProvisioning: 'manual':
|
|
This is the escape hatch for brownfield adoption — Radius manages the connections and application graph even when it doesn’t provision the infrastructure.
Connections
Connections are the mechanism that wires together application components. When a container declares a connection to a resource, Radius:
- Resolves the target resource (including running its Recipe if needed)
- Retrieves the connection values and secrets from the recipe’s
resultoutput - Injects environment variables into the container
Environment Variable Injection
The naming convention for injected variables is:
CONNECTION_<CONNECTIONNAME>_<PROPERTY>
For a connection named cache to a Redis resource with host and port in its values output:
CONNECTION_CACHE_HOST=redis-abc123.dev-workloads.svc.cluster.local
CONNECTION_CACHE_PORT=6379
CONNECTION_CACHE_USERNAME=
CONNECTION_CACHE_PASSWORD=<from secrets>
The full Bicep connection definition:
|
|
Azure Managed Identity via Connections
When connecting to Azure resources, Radius can automatically configure workload identity and assign RBAC roles. The iam block in the connection tells Radius to:
- Create an Azure AD role assignment on the target resource
- Annotate the pod for the Azure Workload Identity mutating webhook
- Inject the resource URI and identity endpoint variables into the container
No connection string is needed — the container uses its managed identity to authenticate. The developer declares iam.roles: ['Key Vault Secrets User']; Radius handles the role assignment.
AWS IAM via Connections
For AWS resources, Radius uses IAM Roles for Service Accounts (IRSA). Radius registers the cluster’s OIDC provider with AWS STS and annotates service accounts with the appropriate IAM role ARN, enabling pods to assume AWS roles without storing credentials.
The Application Graph
Connections are not just for environment variable injection — they build the application graph, a queryable data structure representing how all resources in an application relate to each other.
|
|
The Radius Dashboard (a Backstage-based UI) visualizes this graph graphically, showing the full topology of services and infrastructure with their connections and underlying Kubernetes resources.
Real-World Workflow: Developer + Platform Team
This is how the pieces combine in practice.
Platform Team Responsibilities
The platform team owns environments and recipes:
|
|
Developer Responsibilities
The developer writes one app.bicep that works everywhere:
|
|
Deploy to dev (local Redis container spun up automatically):
|
|
Deploy to production (Azure Cache for Redis provisioned via Terraform):
|
|
The developer ran two commands. The environment determines the difference.
GitOps Integration
For production deployments through GitOps:
|
|
The DeploymentTemplate CRD is reconciled by a Radius controller, which triggers the same Deployment Engine flow as rad deploy but through a Kubernetes control loop.
The rad CLI Reference
Key commands you will use regularly:
|
|
Supported Infrastructure
Kubernetes (in-cluster)
All container workloads run in the Kubernetes cluster hosting Radius. Radius currently only deploys containers to the cluster where it is installed — multi-cluster deployment of workloads is not yet supported, though this is on the roadmap.
Azure
Radius has native Azure integration. Resources available through direct Bicep definitions or the Azure plane:
- Azure Cache for Redis
- Azure Cosmos DB
- Azure Service Bus
- Azure Key Vault
- Azure Blob Storage
- Azure SQL Database
- Any ARM resource (via
Microsoft.*resource types in Bicep)
Azure workload identity is natively supported via the iam connection block — no static credentials needed.
AWS
AWS resources are accessible through the Radius AWS plane:
- Amazon ElastiCache
- Amazon DynamoDB
- Amazon S3
- Amazon SQS / SNS
- Amazon RDS
- Any CloudFormation-supported resource
AWS credential management supports both IAM access keys and IRSA (IAM Roles for Service Accounts) for secretless operation.
Google Cloud
GCP support is not yet available. It is on the roadmap but has no committed timeline as of early 2026.
Comparison: Radius vs. the Ecosystem
Platform engineering has several competing approaches. Understanding where Radius sits requires comparing it honestly.
Radius vs. Helm
Helm templates Kubernetes YAML. It solves packaging and environment-specific configuration (values.yaml), but:
- There is no application model — a “Helm application” is just a collection of Kubernetes resources
- Developers must understand Kubernetes resource types
- Connection strings are managed as secrets or config maps, manually
- No recipe system — platform teams write complex, conditional templates
Radius reduces the Kubernetes knowledge required and adds the recipe/connection abstraction, but it adds new tooling on top of Helm. The two can coexist: Radius can deploy Helm charts through Bicep’s Kubernetes extension.
Radius vs. Crossplane
Both are Kubernetes-native control planes, but they target different problems at different layers:
| Radius | Crossplane | |
|---|---|---|
| Primary abstraction | Application (containers + dependencies together) | Infrastructure resource (single cloud resource) |
| Developer interface | app.bicep with portable resource types |
Claim objects in Kubernetes |
| Platform team interface | Recipes (Bicep/Terraform modules) | Compositions (XRDs + Compositions) |
| Connection injection | Automatic via connections: block |
Manual (typically ExternalSecrets or custom) |
| Application graph | Built-in | Not built-in |
| Cloud portability | Yes — same app.bicep for any environment | Provider-specific resources |
| Composability | Crossplane can be used inside a Radius Recipe | N/A |
The critical difference: Radius keeps developers writing application code, not infrastructure code. Crossplane still requires developers (or their GitOps configs) to know what Crossplane resource types exist and how to use claims. Radius adds an additional abstraction layer that Crossplane does not have.
They can coexist: a Radius Recipe can invoke a Crossplane-managed resource, using Crossplane for infrastructure lifecycle and Radius for application-level connections and graph tracking.
Radius vs. Score
Score is a spec for describing workload configuration that can be rendered to different targets (Docker Compose, Kubernetes, Humanitec). Score is:
- A workload-level spec (single service), not an application-level model
- Focused on portability of the workload definition, not the infrastructure recipe system
- Target-agnostic by design — a Score file renders to Kubernetes via
score-k8sor to Humanitec via the Humanitec platform
Radius and Score solve adjacent problems. Score handles “how do I run this workload anywhere without Kubernetes knowledge.” Radius handles “how do I describe an entire application with all its dependencies and have infrastructure provisioned consistently.” Score does not have a recipe system or an application graph. A platform engineering team could realistically use both.
Radius vs. Humanitec
Humanitec is a commercial internal developer platform product. Its Workload Specification format is similar in intent to Radius’s application model, and its Resource Graph plays a similar role to Radius’s application graph. Key differences:
- Humanitec is commercial; Radius is open source (CNCF)
- Humanitec’s orchestration layer is proprietary; Radius is fully open
- Humanitec has more mature enterprise features (approval flows, GitOps-first design, RBAC at deploy time, richer self-service portal)
- Radius is earlier stage; Humanitec has production deployments at scale
- Radius does not yet have built-in approval workflows or deploy gates
If you need production-grade enterprise IDP features today, Humanitec is more mature. If you prefer open source, are willing to build around rough edges, and want to avoid vendor lock-in, Radius is the path.
Radius vs. KubeVela
KubeVela (CNCF incubating) is another application-centric platform with a similar philosophy. KubeVela uses Application CRDs with traits and components. The primary differences:
- KubeVela uses YAML-first (OAM model); Radius uses Bicep-first
- KubeVela has more mature multi-cluster deployment support
- Radius has a stronger recipe/marketplace story with the OCI registry integration
- Radius has richer cloud-provider integration for Azure and AWS
CNCF Status and Maturity
Radius was accepted as a CNCF Sandbox project on April 16, 2024. The CNCF Sandbox is the entry point for early-stage projects — it indicates vendor-neutral governance and open-source commitment, not production readiness.
As of early 2026, Radius is at v0.56.x. The semantic versioning signals early maturity. The project itself explicitly states it is not yet ready for production workloads. This is not spin — it is accurate. Expect:
- API breaking changes between minor versions
- Missing features that matter for production (no GCP support, single-cluster container deployment only, no built-in approval flows)
- Limited ecosystem tooling compared to Helm/Crossplane
Current Limitations
Infrastructure deployment scope: Radius deploys containers only to the Kubernetes cluster it runs on. You cannot use Radius to deploy to a remote cluster. Cloud resources (Azure, AWS) are provisioned in the configured scope, but workloads are local.
Google Cloud: No GCP support. Azure and AWS only.
RBAC and deploy gates: Radius inherits Kubernetes RBAC for control-plane access, but there is no built-in concept of “require approval before deploying to prod.” This needs to be layered on via GitOps workflows.
Terraform state management: Radius manages Terraform state internally via the Kubernetes API, but this is not as flexible as running Terraform with remote state backends (S3, Azure Blob). Large-scale Terraform workflows may feel constrained.
GitOps maturity: The DeploymentTemplate CRD path works but is newer and less battle-tested than the rad deploy CLI path. Documentation and examples are thinner.
Dashboard: The Backstage-based dashboard is functional but not yet a full self-service portal. It is more of an application graph viewer than a developer portal.
Community size: ~1,600 GitHub stars and 122 forks as of early 2026 is modest. The ecosystem is small compared to ArgoCD, Crossplane, or Helm.
What Is Solid
Despite the caveats, several things work reliably:
- The core application model (app.bicep + environments + recipes) is well-designed and internally consistent
- The recipe system is genuinely flexible — existing Terraform modules adapt to recipes with minimal changes
- Local development with
rad runis excellent — it is dramatically faster to get a full application stack running than with raw Kubernetes - The application graph is useful for understanding complex applications
- The Bicep extensibility integration (using the official Bicep compiler since v0.37) is a mature dependency
Getting Started
Prerequisites
- Kubernetes cluster (kind, k3d, AKS, EKS, GKE — any will work)
kubectlconfigured with cluster-admin permissions- Kubernetes v1.23.8 or higher
Step 1: Install the rad CLI
|
|
Step 2: Initialize Radius
|
|
rad init does four things:
- Installs Radius into the
radius-systemnamespace on your cluster - Creates a default environment with local-dev recipes pre-registered
- Creates a default resource group
- Scaffolds an example
app.bicep
The scaffolded app.bicep is a simple container app that you can deploy immediately.
Step 3: Configure bicepconfig.json
rad init generates this automatically, but for manual setup:
|
|
This configures the Bicep compiler to load the Radius and AWS type extensions, giving you IDE autocomplete and type checking for Radius resources.
Step 4: Write Your Application
|
|
Step 5: Deploy and Explore
|
|
Step 6: Add a Cloud Environment
|
|
Positioning Radius in Your Platform Stack
Radius fills a specific niche. It is most valuable when:
- Your organization has multiple environments (dev/staging/prod) with different infrastructure backends and you are tired of maintaining per-environment Helm values or Terraform workspaces
- Developers need to consume infrastructure without understanding it — the recipe model with automatic connection injection removes a real category of toil
- You are Azure-heavy — the Azure integration is the most mature, the tooling (Bicep) comes from the Azure ecosystem, and the workload identity story is well-developed
- You are building an IDP and want an open-source application graph as a foundation rather than building one from scratch
It is less valuable (or premature) if:
- You need production stability guarantees now — wait for a 1.0
- You need GCP support
- Your developers are comfortable with Kubernetes and existing tooling works fine
- You need enterprise features like approval flows and audit logging without building them yourself
The project’s lineage (from the team that built KEDA and Dapr) gives reasonable confidence in long-term technical direction. The CNCF sandbox status is a meaningful governance milestone. But the v0.x versioning, the “not production ready” disclaimer, and the relatively small community are real signals — evaluate carefully before adopting for anything critical.
For platform engineers building the next generation of developer tooling, Radius is worth a serious evaluation. The core model is sound, the recipe system solves a real problem, and the separation between developer and platform engineer concerns is exactly right. The question is whether your organization can tolerate early-adopter roughness, or whether you need to wait 12-18 months for the project to mature.
Comments