Connect a Space
Connecting a self-hosted Spaces cluster to the Upbound Hub allows you to observe its resources in the Console or Hub API and automatically connects the control planes it manages.
Resources in a connected Space appear in the default realm under a control
plane named space-{space-name}. Connected Space control planes appear as
{space-name}.{control-plane-name} in the realm corresponding to their
namespace on the Space cluster.
To connect a control plane that's not managed by Upbound Spaces, see how to connect a control plane.
Prerequisites
- A Space to connect: the Kubernetes cluster running Upbound Spaces whose resources and control planes you want to observe, and a kubeconfig context for it.
- Upbound Hub, reachable from the Space.
- Access to the Console, or
kubectlconfigured with ahubcontext that targets the Hub. - Helm 3.
Step 1: Declare the Space
Declare the Space in Upbound Hub to assign its name and get a connector registration token.
- Console
- kubectl
- In the Console, open the navigation menu and choose Spaces under Infrastructure.
- Select Register Space.
- Enter a name for the Space.
- Select Register. The Console creates the Space and immediately shows its registration token (see Step 2).
Write the Space manifest. A Space is cluster-scoped and has an empty spec.
The name can't start with space- or contain a .:
apiVersion: fleet.hub.upbound.io/v1beta1
kind: Space
metadata:
name: <space-name>
Create it. The registration token is only returned in the create response, so capture it now (see Step 2):
REGISTRATION_TOKEN=$(kubectl --context=hub create -f space.yaml \
-o jsonpath='{.status.registrationToken}')
Step 2: Get the registration token
Capture the registration token from Step 1. Use it to deploy the connector in Step 3.
The Console and kubectl return the registration token only once, and you can't retrieve it after creation. The token is valid for 24 hours. You can issue a new token at any time, but issuing one doesn't revoke earlier tokens.
After a connector registers the Space, no other token can register it. To hand
the Space to a new connector, for example after you move the connector to
another cluster, deregister the Space
first. The Space keeps its space-<space-name> control plane, so the next
registration reuses it.
- Console
- kubectl
Copy the registration token displayed after you created the Space. The Console will not show it again. Set it as an environment variable for Step 3:
REGISTRATION_TOKEN=<paste-registration-token>
$REGISTRATION_TOKEN from Step 1 holds the token. Confirm it is populated:
echo "$REGISTRATION_TOKEN"
The token from the create response cannot be read back later, but you can issue a new token for the same Space at any time. Issuing a new token doesn't revoke earlier ones:
REGISTRATION_TOKEN=$(echo '{"apiVersion":"fleet.hub.upbound.io/v1beta1","kind":"Space"}' \
| kubectl --context=hub create --raw \
"/apis/fleet.hub.upbound.io/v1beta1/spaces/<space-name>/registrationtoken" \
-f - | jq -r '.status.registrationToken')
Step 3: Deploy the connector
Deploy the connector to the Space. The connector completes the Space's registration, syncs its resources, and connects its control planes.
Set the kubeconfig context for the Space cluster:
SPACE_CONTEXT=<space-context>
Create a namespace and a secret holding the registration token on the Space cluster:
kubectl --context="$SPACE_CONTEXT" create namespace upbound-system
kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \
create secret generic hub-connector-credentials \
--from-literal=registrationToken="$REGISTRATION_TOKEN"
Install the connector chart with Spaces integration enabled:
helm install hub-connector oci://xpkg.upbound.io/upbound/hub-connector \
--version 1.2.0 \
--kube-context "$SPACE_CONTEXT" \
--namespace upbound-system \
--set connector.spaces.enabled=true \
--set connector.hub.url=<hub-url>
Notes:
connector.credentials.existingSecretRef.namedefaults tohub-connector-credentials. Override it only if you named the secret differently.- Override the provisioned connectors' settings under
connector.spaces.controlPlaneConnector.*. Thedebug,rediscoveryInterval,jwksPushInterval, andexcludeResourcesfields default to the Space connector's own settings. Other fields, such asresources,nodeSelector, andtolerations, apply only when you set them. - If the Spaces API server runs outside the cluster where you install the
connector, store a kubeconfig for it in a secret in the
upbound-systemnamespace and setconnector.spaces.kubeconfig.existingSecretRef.nameto the secret's name. The connector reads thekubeconfigkey by default; override it withconnector.spaces.kubeconfig.existingSecretRef.key. Leave the name empty to use the cluster the connector runs in.
Step 4: Verify the connector started
Confirm the connector pods reach Ready:
kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \
wait --for=condition=Ready pod \
--selector app.kubernetes.io/name=hub-connector --timeout=120s
The Space connector deploys a per-control-plane connector into each control
plane's host namespace. Confirm those pods are Ready as well:
kubectl --context="$SPACE_CONTEXT" --all-namespaces \
get pods --selector app.kubernetes.io/name=hub-connector
Step 5: Observe from Insights
View the Space, its resources, and control planes in Insights.
- Console
- kubectl
-
Open the Spaces view. The Space you created shows a status of Ready.

-
Open the control planes view. Two kinds of control plane appear for the Space:
- The representative control plane for the Space, named
space-<space-name>, in thedefaultrealm. - The Space's member control planes, named
<space-name>.<control-plane-name>. Each is created in the realm that matches its namespace in the Space.

- The representative control plane for the Space, named
-
Open a control plane's resources view to see the resources synced from that control plane.

The Space reports phase Ready and a populated controlPlaneRef:
kubectl --context=hub get spaces
kubectl --context=hub get space <space-name> -o jsonpath='{.status.phase}'
The member control planes appear as <space-name>.<control-plane-name> in their
realms:
kubectl --context=hub --all-namespaces get controlplanes
List the resources synced across this Space. The resources endpoint takes a
space filter:
kubectl --context=hub get --raw \
'/apis/inventory.hub.upbound.io/v1beta1/resources?pageSize=100&filter=space==%22<space-name>%22' \
| jq -r '.items[].metadata.name'
Troubleshooting
The Space connector pods don't reach Ready
Check the pod status and events:
kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \
describe pod --selector app.kubernetes.io/name=hub-connector
The connector registers the Space in its resolve-identity init container, and
the pod doesn't start the connector until registration succeeds. If
registration fails, the pod stays in Init or Init:CrashLoopBackOff, and the
Space stays Pending. Read the init container's logs to see why:
kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \
logs deployment/hub-connector -c resolve-identity
Common causes:
-
The
hub-connector-credentialssecret is missing, or its token expired before the Space registered. Issue a new token (Step 2), update the secret, and restart the connector. To update the secret, replace it with the new token:kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \create secret generic hub-connector-credentials \--from-literal=registrationToken="$REGISTRATION_TOKEN" \--dry-run=client -o yaml \| kubectl --context="$SPACE_CONTEXT" apply -f - -
The Space is already registered to another connector, for example after you reinstall the connector on a different cluster. Deregister the Space, issue a new token (Step 2), update the secret, and restart the connector.
-
The connector can't reach the Hub. See The connector can't reach the Hub.
To deregister a Space so a new connector can register it:
kubectl --context=hub delete --raw \
"/apis/fleet.hub.upbound.io/v1beta1/spaces/<space-name>/registration"
Deregistering keeps the Space's space-<space-name> control plane, so the next
registration reuses it.
To restart the connector after you update the secret:
kubectl --context="$SPACE_CONTEXT" --namespace upbound-system \
rollout restart deployment hub-connector
The Space stays Pending
A Space stays Pending until its connector completes registration. Check the
phase:
kubectl --context=hub get space <space-name> -o jsonpath='{.status.phase}'
The phase changes to Ready as soon as registration succeeds, which is also
when the Space connector pods can become Ready. If the phase stays Pending,
see
The Space connector pods don't reach Ready.
The connector can't reach the Hub
The connector registers with and pushes to the Hub at connector.hub.url. From
the Space cluster, confirm that URL is correct and reachable, and that any
firewall or network policy allows egress to the Hub. The connector refuses a
plain http:// URL unless the host is cluster-local, so use https:// for a
Hub outside the cluster. Correct the value and upgrade the release:
helm upgrade hub-connector oci://xpkg.upbound.io/upbound/hub-connector \
--version 1.2.0 \
--kube-context "$SPACE_CONTEXT" \
--namespace upbound-system \
--reuse-values \
--set connector.hub.url=<hub-url>
Member control planes don't appear
The connector deploys a per-control-plane connector into each control plane's
system namespace on the Space cluster. If the Space is Ready but its member
control planes don't appear in the Hub, confirm the per-control-plane
connectors came up:
kubectl --context="$SPACE_CONTEXT" --all-namespaces \
get pods --selector app.kubernetes.io/name=hub-connector
If those connectors are missing, confirm the Spaces installation is healthy and that the control planes you expect exist on the Space cluster.
Next steps
- Access management: Grant users and groups access to the Space and its control planes.