This guide applies to an existing, operational Kubernetes cluster based on Talos Linux: the control plane is running, worker nodes have joined the cluster, a CNI is installed, and the Kubernetes API is accessible with d8 k.

Deckhouse Platform (DP) is installed on top of the existing Kubernetes cluster in existing-cluster mode. This example uses Deckhouse Platform Open, the EarlyAccess release channel, and the Managed bundle.

In this setup:

  • Talos continues to manage the operating system, MachineConfig, kubelet, containerd, etcd, the control plane, Kubernetes PKI, and Kubernetes updates.
  • The existing CNI continues to provide Pod networking.
  • The external provisioner or the user continues to create and delete machines.
  • DP installs and updates platform modules but does not manage Talos or the node lifecycle.

The bundle value is selected during installation and cannot be changed afterwards. You cannot install Managed and then switch it to Minimal or Default with a regular patch.

Prerequisites

The following tools and access are required on the computer from which the installation will be performed:

  • Docker
  • Deckhouse CLI
  • yq for validating YAML
  • An administrative kubeconfig for the Talos cluster
  • Access to the Kubernetes API
  • HTTPS access to registry.deckhouse.io from both the computer and the cluster nodes
  • talosctl and talosconfig if an administrative kubeconfig has not yet been obtained

SSH access to Talos nodes is not required: the installer communicates with the cluster through the Kubernetes API.

Before installation, it is recommended to create an etcd snapshot using Talos and save the original talosconfig and kubeconfig.

Setting the working paths

Create a separate directory for the installation files and change to it:

mkdir -p "$PWD/talos-deckhouse-install"
cd "$PWD/talos-deckhouse-install"

All subsequent commands assume that this remains the current directory. Set the paths once:

TALOSCONFIG="$PWD/talosconfig"
ADMIN_KUBECONFIG="$PWD/kubeconfig-admin"
INSTALLER_KUBECONFIG="$PWD/kubeconfig-installer"
CONFIG_FILE="$PWD/config.yml"

The files are used as follows:

Variable Purpose
TALOSCONFIG talosctl configuration for accessing the Talos API
ADMIN_KUBECONFIG Administrative kubeconfig for running d8 k on the computer
INSTALLER_KUBECONFIG Portable copy of the administrative kubeconfig for the Docker container
CONFIG_FILE Deckhouse Platform installation configuration

If you open a new terminal, return to the working directory and define the four variables from the block above.

Preparing an administrative kubeconfig

The next steps depend on whether you already have an administrative kubeconfig.

If you already have a kubeconfig

Copy it to the working directory:

cp <ADMIN_KUBECONFIG_PATH> "$ADMIN_KUBECONFIG"
chmod 600 "$ADMIN_KUBECONFIG"

This refers to a kubeconfig for d8 k, not a talosconfig file for talosctl.

If you need to obtain a kubeconfig through Talos

First, copy the existing talosconfig to the working directory:

cp <TALOSCONFIG_PATH> "$TALOSCONFIG"
chmod 600 "$TALOSCONFIG"

Specify the address of a control-plane node:

CONTROL_PLANE_ADDRESS=<CONTROL_PLANE_IP_OR_DNS>

Obtain an administrative kubeconfig:

talosctl kubeconfig "$ADMIN_KUBECONFIG" --talosconfig="$TALOSCONFIG" --nodes="$CONTROL_PLANE_ADDRESS" --merge=false
chmod 600 "$ADMIN_KUBECONFIG"

By default, talosctl uses the Talos API endpoints from the current talosconfig context. If a different endpoint is required, add:

--endpoints=<TALOS_API_ENDPOINT>

Use --force only when you intentionally want to overwrite an existing file.

Verifying permissions

Check how Kubernetes identifies the user:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" auth whoami

Installation requires stable administrative access. A Talos administrative kubeconfig usually uses the system:masters group. Using an OIDC user kubeconfig for the installation is not recommended: after the DP user-authz module is enabled, that user’s access to system namespaces may change.

Verify the required permissions:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" auth can-i '*' '*' --all-namespaces
d8 k --kubeconfig="$ADMIN_KUBECONFIG" auth can-i create customresourcedefinitions.apiextensions.k8s.io
d8 k --kubeconfig="$ADMIN_KUBECONFIG" auth can-i create clusterroles.rbac.authorization.k8s.io

All three commands must return yes.

Verifying the existing cluster

Check the nodes:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get nodes -o wide

All nodes must be in the Ready state.

Check the Kubernetes API:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get --raw='/readyz?verbose'

The response must end with readyz check passed.

Check the system Pods:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n kube-system get pods -o wide

Before installing DP, the following components must already be running:

  • CNI
  • CoreDNS
  • kube-proxy, if it is used by the selected network setup
  • Control-plane components

Also make sure that the Kubernetes version is supported by the selected DP version.

Verifying component ownership

DP must not manage the same components as Talos or an external provisioner.

The table below lists the components and their owners after installation:

Component Owner after installation
Talos OS and MachineConfig Talos
etcd and the Kubernetes control plane Talos
Kubernetes PKI Talos
kubelet and containerd Talos
CNI The existing external CNI
CoreDNS and kube-proxy, if used The existing cluster
Machine creation and deletion The external provisioner or the user
Platform modules DP

The following DP modules must remain disabled:

  • control-plane-manager
  • node-manager
  • terraform-manager
  • cni-cilium
  • kube-dns
  • kube-proxy
  • cloud-provider-* modules
  • registry-packages-proxy

If Cilium is already installed in the Talos cluster, do not enable the DP cni-cilium module: two operators must not manage the same CNI at the same time.

The Managed bundle includes ingress-nginx, cert-manager, local-path-provisioner, VPA, monitoring, and the user-authz module. Before installation, check whether external equivalents are already present in the cluster:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get storageclass
d8 k --kubeconfig="$ADMIN_KUBECONFIG" get ingressclass
d8 k --kubeconfig="$ADMIN_KUBECONFIG" get deployments -A
d8 k --kubeconfig="$ADMIN_KUBECONFIG" get crd

If a component is already installed, choose a single owner before proceeding. Do not run two ingress controllers, two cert-manager installations, or two VPA installations at the same time. If an external solution remains responsible for the component, explicitly disable the corresponding DP module using a ModuleConfig with spec.enabled: false. If DP is to manage the component, disable or remove the external counterpart before installation.

Preparing a kubeconfig for the installer container

The installer runs inside Docker. It requires a portable kubeconfig that does not reference certificate and key files available only on the user’s computer.

Create a portable copy:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" config view --raw --flatten --minify > "$INSTALLER_KUBECONFIG"
chmod 600 "$INSTALLER_KUBECONFIG"

This command does not create new certificates. The --flatten option reads the CA, client certificate, and key from the paths specified in the source kubeconfig and embeds them in the new file.

Verify the copy:

d8 k --kubeconfig="$INSTALLER_KUBECONFIG" auth whoami
d8 k --kubeconfig="$INSTALLER_KUBECONFIG" auth can-i '*' '*' --all-namespaces

The second command must return yes.

Check the Kubernetes API address:

d8 k --kubeconfig="$INSTALLER_KUBECONFIG" config view --minify -o jsonpath='{.clusters[0].cluster.server}{"\n"}'

This address must be reachable from the Docker container. A reachable Kubernetes API address, a VPN address, or a load balancer address is preferred.

If the address is https://127.0.0.1:6443, the installer cannot use it directly: inside the container, 127.0.0.1 refers to the container itself. Make the Kubernetes API accessible from the container before continuing with the installation.

Creating the configuration file

Create the $CONFIG_FILE file with the following content and replace example.com with your domain:

apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
  name: deckhouse
spec:
  version: 1
  enabled: true
  settings:
    bundle: Managed
    releaseChannel: EarlyAccess
    logLevel: Info
---
apiVersion: deckhouse.io/v1alpha1
kind: ModuleConfig
metadata:
  name: global
spec:
  version: 2
  settings:
    modules:
      publicDomainTemplate: "%s.example.com"

The domain in publicDomainTemplate must not match or be a subdomain of the domain specified in clusterDomain. Before using the template, configure DNS services in both the networks where cluster nodes are located and the networks from which clients access the platform service web interfaces.

If the nodes have custom taints and DP components must run on them, add the corresponding values to global.spec.settings.modules.placement.customTolerationKeys. Do not add an example taint unless it exists in the cluster.

Validate the file:

yq eval-all '.' "$CONFIG_FILE" >/dev/null && echo "YAML OK"
grep -n $'\t' "$CONFIG_FILE"

The first command must print YAML OK; the second command must not print anything.

Running the Deckhouse Platform Open installer

The installer tag must match the releaseChannel in the configuration. The early-access tag is used for EarlyAccess.

Check that the files exist:

ls -l "$CONFIG_FILE" "$INSTALLER_KUBECONFIG"

Run the installer:

docker run --pull=always -it -v "$CONFIG_FILE:/config.yml:ro" -v "$INSTALLER_KUBECONFIG:/kubeconfig:ro" registry.deckhouse.io/deckhouse/ce/install:early-access bash

The installer image path still uses the ce designation for Deckhouse Platform Open.

Inside the installer container, run:

dhctl bootstrap-phase install-deckhouse --kubeconfig=/kubeconfig --config=/config.yml

Do not close the terminal until the bootstrap process completes. Installation can take anywhere from 5 to 30 minutes.

Monitoring the installation

In a separate terminal, change to the same working directory and define the variables from the “Setting the working paths” section again. Run the command:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n d8-system get deployment,replicaset,pods -w

If the deckhouse Pod is not created, check the events:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n d8-system get events --sort-by=.metadata.creationTimestamp

Errors such as ImagePullBackOff, ErrImagePull, 401 Unauthorized, or 403 Forbidden usually indicate a problem with the registry address, registry access, DNS, or routing.

To diagnose a specific Pod, run:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n <NAMESPACE> describe pod <POD_NAME>

Verifying the installation

Wait for the main Deployment to become ready:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n d8-system rollout status deployment/deckhouse --timeout=10m
d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n d8-system get deployment,pods -o wide

Check the modules:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get modules -o wide

Enabled modules are expected to have PHASE: Ready, ENABLED: True, and READY: True.

The Module status alone is not sufficient: a module may be Ready even if one of its Deployments, StatefulSets, or DaemonSets was not created or is restarting. Check the actual resources:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get deployment,statefulset,daemonset -A

For each DaemonSet, the DESIRED, CURRENT, and READY values must match. For Deployments and StatefulSets, the expected number of replicas must be ready.

Find Pods that are not in the Running or Succeeded phase:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get pods -A --field-selector='status.phase!=Running,status.phase!=Succeeded'

Check recent warnings:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get events -A --field-selector=type=Warning --sort-by=.metadata.creationTimestamp

An old warning does not necessarily indicate a current problem. Consider the event timestamp, repetition count, and the current state of the related resource.

Verifying that DP does not manage Talos components

Verify that DP modules that can manage Talos components remain disabled.

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get modules control-plane-manager node-manager terraform-manager cni-cilium kube-dns kube-proxy registry-packages-proxy -o wide

All listed modules must have ENABLED: False.

Check the cloud provider modules:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" get modules -o wide | grep -E '(^NAME|^cloud-provider-)'

All cloud-provider-* modules found by the command must have ENABLED: False.

Check the original cluster components again:

d8 k --kubeconfig="$ADMIN_KUBECONFIG" -n kube-system get pods -o wide
d8 k --kubeconfig="$ADMIN_KUBECONFIG" get nodes -o wide

All Talos nodes must remain Ready. The original CNI, CoreDNS, and control-plane components must continue to run; kube-proxy must also continue to run if it was used before the DP installation.

Successful installation criteria

The installation is considered successful when all of the following conditions are met:

  • All Talos nodes remain Ready.
  • The original CNI and CoreDNS continue to run; kube-proxy also continues to run if it was used before the DP installation.
  • Deployment deckhouse is ready.
  • Enabled modules have READY: True.
  • The actual module Deployments, StatefulSets, and DaemonSets are ready.
  • DP modules that can manage Talos components remain disabled.
  • Administrative access through the Talos administrative kubeconfig is preserved.

Additional resources