Skip to content

Adopt a Kubernetes cluster via the ESO pattern ​

The customer-side External Secrets Operator (ESO) pattern lets a Kubernetes cluster you already operate join the mesh as an Adopted Resource without exposing your secret store to plexsphere. You mint a single-use BootstrapToken in plexsphere, push it into your own secret store (Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault), define an ExternalSecret that materialises it as a Kubernetes Secret, and the plexd DaemonSet consumes it on first start. plexsphere never reads from your secret store and never writes into your cluster.

Prerequisites ​

  • An authenticated plexsphere session — see Log in with plexctl.
  • The owning Project UUID — see Manage Projects.
  • A target Kubernetes cluster with External Secrets Operator installed and a working ClusterSecretStore (or SecretStore) bound to the customer secret store the token will live in.
  • Write access to that secret store (Vault path, AWS Secrets Manager ARN, etc.) — plexsphere never touches it.
  • kubectl and either helm or a GitOps pipeline (Argo CD, Flux, Kustomize) reaching the target cluster.

Steps ​

1. Issue a BootstrapToken ​

shell
plexctl bootstrap-token issue \
  --project <project-uuid> \
  --kind node \
  --env-prefix prod \
  --ttl 1h

--kind is node; a bridge token is rejected by the Node endpoint. Capture the plaintext from the response immediately — the API returns it exactly once. The same token works for every channel, including the systemd channel documented under Register a Node.

2. Push the token into your secret store ​

Land the plaintext under a path your existing tooling already manages. The exact command depends on the store:

shell
# HashiCorp Vault (KV-v2)
vault kv put secret/plexsphere/<env-prefix>/bootstrap-token \
  token='<plaintext-from-step-1>'

# AWS Secrets Manager
aws secretsmanager create-secret \
  --name plexsphere/<env-prefix>/bootstrap-token \
  --secret-string '{"token":"<plaintext-from-step-1>"}'

Rotate or revoke the upstream secret once registration completes — the token is single-use, so the materialised value becomes inert after the first POST /v1/register.

3. Define the ExternalSecret ​

Apply an ExternalSecret that pulls the value into a Kubernetes Secret named plexd-bootstrap-token in the plexsphere-system namespace. The plexd DaemonSet mounts the Secret by that exact name; renaming it requires editing the chart values.

yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
  name: plexd-bootstrap-token
  namespace: plexsphere-system
spec:
  refreshInterval: 0s
  secretStoreRef:
    name: <your-cluster-secret-store>
    kind: ClusterSecretStore
  target:
    name: plexd-bootstrap-token
    creationPolicy: Owner
  data:
    - secretKey: token
      remoteRef:
        key: secret/plexsphere/<env-prefix>/bootstrap-token
        property: token

refreshInterval: 0s disables polling — the token is one-shot, so re-fetching it after consumption serves no purpose and risks re-materialising a value the upstream store has since rotated.

4. Install or apply the plexd chart ​

Install the plexd Helm chart (or apply the equivalent DaemonSet manifest the Enrollment Workshop emits) into the same namespace. The chart wires plexd to read /etc/plexd/bootstrap-token from the Secret mount and call POST /v1/register on first start.

shell
helm upgrade --install plexd plexsphere/plexd \
  --namespace plexsphere-system --create-namespace \
  --set controlPlaneUrl=https://control.example.com \
  --set bootstrapTokenSecret=plexd-bootstrap-token

When the DaemonSet rolls out, each node redeems the token and reports itself as an Adopted Resource. Because the token is single-use, only the first replica that wins the race consumes it; the rest receive 403 token_consumed and crash-loop until the operator either issues fresh tokens for them or removes the DaemonSet from those nodes.

Verification ​

shell
# 1. Confirm the ExternalSecret materialised the value.
kubectl -n plexsphere-system get externalsecret plexd-bootstrap-token \
  -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}'

# 2. Watch plexd come up and consume the token.
kubectl -n plexsphere-system logs daemonset/plexd -f \
  | grep --line-buffered 'register'

# 3. Confirm the Resource is visible in plexsphere with origin=Adopted.
#    Operator-facing verification today: read the row directly from the
#    plexsphere primary. A typed `GET /v1/projects/{id}/resources?origin=Adopted`
#    surface and a `plexctl resources` subcommand are tracked as a
#    follow-up; until they land, the canonical projection is the
#    plexsphere.resources row that the registration transaction
#    committed.
psql "${PLEXSPHERE_DSN}" \
  -c "SELECT id, kind, external_ref, origin
        FROM plexsphere.resources
       WHERE project_id = '<project-uuid>'
         AND origin = 'Adopted';"

The plexctl bootstrap-token get output now carries a populated consumed_at field; the Workshop Resources list shows the new row with an Adopted badge.

Out of scope ​

This how-to covers only the ESO pattern from token issuance through cluster materialisation. Customer Vault provisioning is not covered — installing or configuring HashiCorp Vault, AWS Secrets Manager, or any other secret backend is your own platform concern. plexsphere never reads from these stores and imposes no plexsphere-specific schema on them.

See also ​

  • ./cloud-init-daemonset.md — the bounded-context reference for the broker's bootstrap-injection renderers. Its DaemonSet bundle's External-Secrets-Operator mode emits the same plexd-bootstrap-token Secret name, plexsphere-system namespace, ClusterSecretStore ExternalSecret, and /etc/plexd/bootstrap-token mount this how-to assembles by hand — so a broker-rendered ESO bundle and the manifests below are interchangeable.
  • Issue a bootstrap token — mint the credential consumed here.
  • Register a Node — the underlying POST /v1/register contract every channel terminates at.
  • ./credentials.md — bounded-context reference for the OpenBao Credential Broker that owns plexsphere-side secrets.
  • External Secrets Operator documentation — vendor reference for the customer-side controller.