
Leveraging Crossplane to build single-tenant SaaS control planes on top of Kubernetes
by Natasha Tomattis and Yehuda Cohen @ Sela
Published Apr 30, 2025
Last Modified May 1, 2025
As SaaS companies scale, one of the most critical architectural decisions they face is whether to adopt a single-tenant or multi-tenant model. While multi-tenancy offers cost efficiency and resource optimization, it also introduces challenges such as noisy neighbor effects, security risks, and limited customization. In contrast, a single-tenant model provides each customer with dedicated infrastructure, ensuring stronger isolation, security, and flexibility—features that enterprise customers often demand for compliance and customization reasons.
However, scaling a single-tenant SaaS platform presents significant infrastructure challenges. Now, imagine a SaaS platform deployed on Kubernetes, managing hundreds of isolated clusters—each requiring its own infrastructure, networking, and security configurations. Without automation, this quickly becomes a nightmare. Traditional Infrastructure-as-Code (IaC) tools like Terraform help with initial provisioning, but they lack continuous reconciliation, requiring manual interventions to maintain consistency across environments.
This blog post is dedicated to one such use-case and how we are leveraging the Kubernetes control plane in conjunction with Crossplane to solve these challenges.
Before we get started, we should introduce Crossplane. Crossplane is a control-plane extension for Kubernetes that enables the management of non-Kubernetes resources via Kubernetes’ declarative model and reconciliation loop. Effectively, this enables platform teams to provision and manage cloud resources using Kubernetes-native APIs, and enables us to achieve some typically challenging goals for SaaS companies who wish to offer a single-tenant deployment model to customers, including:
· Automation of tenant provisioning across multiple AWS accounts.
· Continuous reconciliation of infrastructure state to prevent drift.
· The management of cloud resources at scale.
· The ability to support multi-cloud and hybrid deployments without rewriting automation for each deployment type.
In this blog, we’ll show how we worked with the Flywheel team to transform the operation of its single-tenant SaaS infrastructure —eliminating manual effort and making scalability seamless.
In traditional Infrastructure-as-Code (IaC) workflows, provisioning cloud resources is often a one-time operation. Tools like Terraform are effective at defining infrastructure state, but they lack continuous reconciliation—requiring manual interventions to detect and correct drift. Crossplane extends Kubernetes’ declarative model beyond containerized workloads, enabling continuous lifecycle management of non-Kubernetes resources.
To achieve this, we deployed Crossplane in a dedicated management cluster—separating infrastructure control from application workloads. This cluster acts as the central orchestrator, continuously ensuring that cloud resources remain in their intended state.
Crossplane accomplishes this through Provider controllers, which serve as Kubernetes-native resource managers for external cloud services. These controllers watch for changes in desired infrastructure states—defined as Kubernetes Custom Resources (CRs)—and automatically reconcile discrepancies. Whether scaling databases, updating IAM roles, or provisioning networking components, Crossplane ensures that AWS resources remain in sync with Kubernetes-defined policies.
In collaboration with the Flywheel team, Sela designed this architecture to streamline the management of single-tenant SaaS deployments across multiple AWS accounts**.** Each tenant’s infrastructure is provisioned, monitored, and continuously reconciled through Kubernetes’ API, eliminating the need for external orchestration tools. This approach not only reduces operational overhead but also ensures consistent, policy-driven infrastructure management at scale.

To enable Crossplane to provision and manage infrastructure across multiple AWS accounts, we needed to establish a secure and scalable authorization mechanism between the management cluster and target AWS environments.
A key challenge in this setup was that our management cluster runs in Azure, while Crossplane needed permissions to manage AWS resources. To bridge this gap, we leveraged AWS Security Token Service (STS).
Since Kubernetes workloads in Azure authenticate using Entra ID (formerly Azure AD), we designed an authentication flow where:
- Azure pods use workload identity to assume**** continuously**** an Entra ID user.
- The Entra ID user is authorized to assume an AWS IAM role via STS.
- Crossplane’s AWS provider uses this assumed role to provision and reconcile AWS resources.
However, AWS’ provider lacked native support for this authentication method. To solve this, we deployed a sidecar container responsible for retrieving and refreshing the Azure web identity token when it expired. This token was then used to configure Crossplane’s AWS provider via the AWS STS AssumeRoleWithWebIdentity API:

Or, with a configuration similar to the following:
This setup ensures that Crossplane can continuously manage AWS resources from an Azure-hosted control plane without requiring static credentials.
For Flywheel customers requiring deployments in their own AWS accounts, we configured cross-account permissions. This allowed us to use a role in Flywheel’s AWS account to manage a customer’s infrastructure, enabling the creation of Crossplane provider configurations specific to each customer account.

With a configuration file similar to the following:
By leveraging multiple provider configurations, we were able to use the same AWS provider family to manage infrastructure across different AWS accounts.
Once the Crossplane AWS provider is connected, we can define managed resources—cloud resources represented as Kubernetes objects within Crossplane. These include S3 buckets, EKS clusters, NodeGroups, and other AWS infrastructure components.
However, managing individual resources at scale becomes cumbersome and error prone. To address this, Crossplane Compositions allow us to bundle multiple managed resources into a single, reusable abstraction. This enables platform engineers to define infrastructure declaratively while embedding Flywheel-specific business logic.
We designed three core Crossplane Compositions to standardize and simplify infrastructure provisioning:
- ClusterNetwork – Manages VPC-related resources,**** including**** VPCs, Subnets, RouteTables, Routes, and Gateways.
- KubeCluster – Handles EKS cluster provisioning, including NodeGroups, LaunchTemplates, and Add-ons.
- PodIdentity – Automates PodIdentityAssociation deployments, ensuring Kubernetes workloads inherit correct AWS IAM roles.
These Compositions allow Flywheel’s infrastructure to remain declarative, modular, and scalable, reducing operational overhead and improving consistency.
To support complex infrastructure patterns, we embedded Go template functions into our Compositions. This allowed us to:
· Encode business logic directly within resource definitions.
· Dynamically generate resource configurations based on input parameters.
· Enable multi-account deployments by introducing a provider configuration variable within all Compositions.
By abstracting these details at the Composition level, we ensured that platform engineers could define infrastructure centrally and deploy it consistently across multiple AWS environments.

To provision an EKS cluster, engineers only need to define ClusterNetwork and KubeCluster resources. The KubeClusterClaim CRD abstracts away the complexity of individual AWS components while providing flexibility in configuration:
This declarative approach ensures:
· Scalability – Infrastructure definitions remain modular and reusable.
· Simplicity – Engineers don’t need to manage low-level AWS details.
· Portability – Future cloud providers can be integrated using the same Composition patterns.
With these Compositions, Crossplane doesn’t just provision infrastructure—it continuously manages its lifecycle, ensuring AWS resources remain in the desired state across their entire lifespan.
With our Crossplane Composite Resources defined, deploying a new Kubernetes cluster is as simple as applying the necessary manifests to the management cluster. However, to ensure continuous, automated deployments, we integrate ArgoCD as a GitOps-based control mechanism.
ArgoCD continuously monitors a Git repository containing all infrastructure definitions and ensures that the management Kubernetes cluster remains in sync with the desired state. This enables a fully declarative infrastructure workflow:
1. Engineers commit a cluster configuration to the Git repository.
2. ArgoCD detects the change and applies the updated manifests.
3. Crossplane provisions the required AWS resources, creating or updating an EKS cluster.
This process eliminates the need for manual cluster provisioning—platform operators simply update a Git repository to create, update, or destroy an EKS cluster.

Once an EKS cluster is provisioned, the next challenge is deploying workloads onto it. To automate this step, we use a Crossplane provider for ArgoCD, which extends GitOps automation beyond infrastructure provisioning.
This provider has three responsibilities:
1. Extracts configuration variables from the cluster definition (e.g., cluster name, endpoint, authentication details) once Crossplane provisions it.
2. Registers the new cluster in ArgoCD using the configuration variables.
3. Ensures application manifests are deployed to the new cluster automatically.
By integrating Crossplane and ArgoCD, we fully automate both cluster provisioning and workload deployment, making it easy to manage a large fleet of single-tenant Kubernetes clusters.

With this approach, deploying a new tenant in a -Tenant model requires modifying only two Git repositories:
1. Cluster Configuration Repository – Defines infrastructure (EKS, networking, storage).
2. Cluster Application Configuration Repository – Manages application deployment settings per tenant.
This GitOps-first approach enables:
· Consistent, repeatable deployments – Infrastructure and application changes are version-controlled.
· Secure, automated SaaS provisioning – No manual intervention required to onboard new tenants.
· Scalability at enterprise scale – Hundreds of clusters can be managed with minimal overhead. SaaS
By combining Crossplane, ArgoCD, and Kubernetes, we built a fully automated, GitOps-driven control plane for managing single-tenant SaaS environments—ensuring security, scalability, and operational efficiency at every stage.
Final Thoughts
Managing a single-tenant SaaS platform at scale presents significant operational complexity. However, Crossplane and ArgoCD transform this challenge into an automated, self-healing system by extending Kubernetes’ reconciliation model to infrastructure.
By adopting a GitOps-driven approach, we ensure that both infrastructure and applications remain continuously in sync with the desired state—eliminating manual provisioning and minimizing operational overhead. Crossplane provisions and manages cloud resources declaratively, while ArgoCD automates deployments and updates, enabling a seamless multi-cloud control plane.
This architecture provides:
· Scalability – Infrastructure expands dynamically without increasing complexity.
· Multi-cloud flexibility – Seamlessly manages workloads across different cloud providers.
· Operational efficiency – Automates both infrastructure and application lifecycles with minimal intervention.
With Crossplane and ArgoCD, the process of managing hundreds or even thousands of tenant environments becomes declarative, automated, and scalable—allowing SaaS providers to focus on innovation rather than infrastructure maintenance.
Contact Us
If you are looking to build a SaaS Control Plane for your SaaS product, want to migrate to AWS, want to modernize your application, or want to discuss a data engineering project, reach out to our team at: contact.us@selacloud.com.