Skip to main content

Quickstart

Get OpenControlPlane running on your local machine in under 10 minutes. By the end, you'll have a platform that hands out managed ControlPlanes with the capability for teams to request Flux.

note

ocpctl is the CLI for managing OpenControlPlane environments locally and in production. It is under active development. Some commands and flags may change.

What You'll Build

OpenControlPlane creates three clusters that work together:

ClusterWho uses itPurpose
🟠 PlatformPlatform OwnersRuns platform services, cluster providers, and service providers
🟢 OnboardingEnd users (teams)API surface where teams create ControlPlanes
🟣 ControlPlaneEnd users (teams)One per team, isolated workspace with requested services

The separation ensures end users never touch infrastructure. They interact only with the Onboarding cluster to request resources, and their services appear on their own ControlPlane cluster.


Prerequisites

  • Docker running (8 GB RAM allocated to it)
  • Go installed
  • kubectl CLI installed
  • ~10 minutes
Linux: inotify limits

The default Linux inotify limit (fs.inotify.max_user_instances=128) is too low when running multiple Kind clusters. If it is exhausted, containerd inside the ControlPlane cluster fails to initialize, which significantly delays bootstrap and can cause the AccessRequest controller to build up a long exponential backoff before the cluster becomes reachable.

Raise the limit before you start:

sudo sysctl -w fs.inotify.max_user_instances=512
sudo sysctl -w fs.inotify.max_user_watches=524288

To persist across reboots, add both lines to /etc/sysctl.d/99-kind.conf.

Install ocpctl

go install github.com/openmcp-project/ocpctl@v0.4.1

Or download a pre-built binary from the releases page.


Step 1: Start the platform

ocpctl env apply local

This takes a few minutes. It creates a local Kind-based environment with the full OpenControlPlane stack: openmcp-operator, cluster-provider-kind, plus an onboarding cluster and pre-installed service providers that you can consume.

Verify the platform is running:

Apply to Platform Cluster
kubectl config use-context kind-local-platform
kubectl get pods -n openmcp-system

You should see these pods in Running state:

NAME READY STATUS RESTARTS AGE
cp-kind-5dbd475459-6zgfr 1/1 Running 0 88s
cp-kind-init-ps7f7 0/1 Completed 0 96s
openmcp-operator-654568c654-4fhfk 1/1 Running 0 2m10s
ps-gateway-57db9fdb9-68hzz 1/1 Running 2 (73s ago) 74s
ps-gateway-init-wxcjw 0/1 Completed 0 91s
ps-helmdeployer-689b98cd99-vfvs5 1/1 Running 0 93s
ps-helmdeployer-init-6nmxk 0/1 Completed 0 96s
ps-managedcontrolplane-796ff64877-l2mhx 1/1 Running 0 72s
ps-managedcontrolplane-init-mr7q9 0/1 Completed 0 96s
sp-crossplane-67659f97f5-n5v9m 1/1 Running 0 64s
sp-crossplane-init-hqccq 0/1 Completed 0 91s
sp-flux-77db5c6cbb-7nnmc 1/1 Running 0 60s
sp-flux-init-k4knn 0/1 Completed 0 91s
sp-kro-78b4cbf89b-djrbs 1/1 Running 0 51s
sp-kro-init-btzww 0/1 Completed 0 90s
sp-ocm-7f97797fc7-dxq4r 1/1 Running 0 57s
sp-ocm-init-r9kmx 0/1 Completed 0 91s

In this output we can see that openmcp-operator and multiple other services like cluster-provider-kind (cp-kind) and service providers such as Crossplane, Flux, Kro and OCM are running.

Configure allowed Flux versions

To enable end users to request Flux for a ControlPlane, as Platform Owner, we need to make sure to configure allowed versions of Flux. We can configure them via a ProviderConfig:

Apply to Platform Cluster
kubectl config use-context kind-local-platform
kubectl apply -f - <<EOF
apiVersion: flux.services.open-control-plane.io/v1alpha1
kind: ProviderConfig
metadata:
name: flux
spec:
versions:
- version: "2.8.3"
chartVersion: "2.18.2"
chartUrl: "oci://ghcr.io/fluxcd-community/charts/flux2"
EOF

This controls exactly which versions teams can request in Step 3. Add more entries to the versions list to offer additional versions.


Step 2: Create a ControlPlane

Now switch to the end-user perspective. A team wants their own ControlPlane.

First, export the onboarding cluster's kubeconfig so kubectl can reach it:

ocpctl clusters kubeconfig export --environment local --name onboarding

See the ControlPlane reference for the full API.

Apply to Onboarding API
kubectl config use-context kind-local-onboarding
kubectl apply -f - <<EOF
apiVersion: core.open-control-plane.io/v2alpha1
kind: ControlPlane
metadata:
name: my-controlplane
namespace: default
spec:
iam: {}
EOF

Wait for it to become ready:

kubectl config use-context kind-local-onboarding
kubectl get controlplane my-controlplane -w

Once provisioning completes, you will see:

NAME PHASE
my-controlplane Ready

The platform has provisioned an isolated ControlPlane cluster. Behind the scenes, OpenControlPlane asked cluster-provider-kind to create a new Kind cluster for this ControlPlane. The cluster is assigned a generated name of the form mcp-<hash>.<random> — for example mcp-ad2klitc.f52190f9. The hash is derived from the environment name; the suffix is random per provisioning run. You will need this name in Step 3.


Step 3: Request Flux as a service

The team wants Flux installed on their ControlPlane:

Apply to Onboarding API
kubectl config use-context kind-local-onboarding
kubectl apply -f - <<EOF
apiVersion: flux.services.open-control-plane.io/v1alpha1
kind: Flux
metadata:
name: my-controlplane
namespace: default
spec:
version: 2.8.3
EOF

ServiceProvider Flux on the platform cluster detects this request and installs Flux into the ControlPlane cluster called "my-controlplane" automatically.

You can check the installation status of Flux via the status sub-resource of the Flux object:

Apply to Onboarding API
kubectl config use-context kind-local-onboarding
kubectl get flux my-controlplane -n default -o yaml

The output looks like this:

apiVersion: flux.services.open-control-plane.io/v1alpha1
kind: Flux
metadata:
...
finalizers:
- flux.services.open-control-plane.io/finalizer
name: my-controlplane
namespace: default
spec:
version: 2.8.3
status:
conditions:
- lastTransitionTime: "2026-07-31T14:40:59Z"
message: Reconcile in progress
observedGeneration: 1
reason: Reconciling
status: "False"
type: Ready
observedGeneration: 1
phase: Progressing # The installation is currently ongoing, "Ready" indicates successful installation
resources:
- kind: OCIRepository
location: PlatformCluster
message: Resource is not ready
name: flux
namespace: mcp--76d5b02a-48a3-8952-8fc9-20577e724f47
phase: Pending
- kind: HelmRelease
location: PlatformCluster
message: Resource is not ready
name: flux
namespace: mcp--76d5b02a-48a3-8952-8fc9-20577e724f47
phase: Pending

Connect to the ControlPlane cluster

The ControlPlane cluster runs as its own Kind cluster with a generated name. Find it:

ocpctl clusters list
local
├── platform
├── onboarding
└── mcp-ad2klitc <- your ControlPlane cluster

Export its kubeconfig and switch context:

ocpctl clusters kubeconfig export --environment local --name mcp-ad2klitc
CONTROLPLANE_CLUSTER=$(kubectl config get-contexts -o name | grep '^kind-mcp-')
kubectl config use-context "$CONTROLPLANE_CLUSTER"
Why cluster names differ across tools

ocpctl shows logical cluster names as registered in the platform, not the actual kind names derived by cluster-provider-kind. Two transformations happen between what you see in ocpctl clusters list and the kubectl context you need to use:

  1. Environment prefix & uniqueness suffix — Platform and Onboarding cluster names include the environment (e.g. local-) as a prefix and ControlPlane clusters get a random suffix to guarantee uniqueness across environments.
  2. kind- prefix — when kind exports a kubeconfig, it prepends kind- to every context name.

This is why the command above uses grep '^kind-mcp-' to find the right context because the name from ocpctl clusters list will not match directly. An example of all three name forms for the same clusters is shown below:

ocpctl namekind cluster namekubectl context
platformlocal-platformkind-local-platform
onboardinglocal-onboardingkind-local-onboarding
mcp-ad2klitcmcp-ad2klitc.f52190f9kind-mcp-ad2klitc.f52190f9

Verify Flux is running

Flux installation can take a few minutes while the ControlPlane cluster finishes bootstrapping. Wait for all pods to reach Running:

Apply to ControlPlane Cluster
kubectl get pods -n flux-system
NAME READY STATUS RESTARTS AGE
helm-controller-8564d95f86-6kxlg 1/1 Running 0 2m8s
image-automation-controller-5c484478c6-jj29p 1/1 Running 0 2m8s
image-reflector-controller-5875745f59-b9cp4 1/1 Running 0 2m8s
kustomize-controller-7587bc49f9-m47nv 1/1 Running 0 2m8s
notification-controller-d7d89cdb9-sht7p 1/1 Running 0 2m8s
source-controller-7f6f4dd77d-vmxvv 1/1 Running 0 2m8s

The team now has a fully functional control plane with Flux, provisioned through a simple API request.


Next Steps

Add more services

Beyond Flux, we can offer Crossplane, External Secrets Operator, Velero, and more to end users. Each service is a ServiceProvider deployed on the platform cluster.

Our CLI tool ocpctl already pre-installs a lot of these Service Providers. We can look them up via:

Apply to Platform Cluster
kubectl config use-context kind-local-platform
kubectl get serviceproviders

The output looks like this:

NAME PHASE
crossplane Ready
flux Ready
kro Ready
ocm Ready

But we can also apply a new ServiceProvider to our platform to offer e.g. External Secrets Operator to end users:

Apply to Platform Cluster
kubectl config use-context kind-local-platform
kubectl apply -f - <<EOF
apiVersion: openmcp.cloud/v1alpha1
kind: ServiceProvider
metadata:
name: externalsecretsoperator
namespace: openmcp-system
spec:
image: ghcr.io/openmcp-project/images/service-provider-external-secrets:v1.0.0
EOF

We can look up the status of the installation by executing:

kubectl config use-context kind-local-platform
kubectl get serviceproviders

The output looks like this:

NAME PHASE
crossplane Ready
externalsecretsoperator Progressing
flux Ready
kro Ready
ocm Ready

Next, we need to configure these ServiceProviders via their ProviderConfig API to rule which versions end users can install.

note

More coming soon

Managed team access

Learn how Projects and Workspaces let you organize teams and ControlPlanes.

note

More coming soon

Configure an Identity Provider

Learn how to set up an IdP to authenticate users against a local OpenControlPlane environment.

note

More coming soon

Deploy on real infrastructure

Follow the Production Setup guide to run OpenControlPlane on Gardener.


Clean up

ocpctl env delete local

Removes all Kind clusters and resources created by ocpctl env apply local.