This guide describes the steps required to switch the Deckhouse Platform edition in a running cluster. Follow the sections in order.

The switching process differs depending on how you work with the image registry. Choose the method that applies to your cluster and follow the instructions.

A valid license key is required when switching to DP BE/SE/SE+/EE. It is not required when switching to DP CE.

This guide assumes the use of a public container image registry (registry.deckhouse.io). If you use a different registry address, adjust the commands or refer to the guide for switching Deckhouse to a third-party container image registry.

Execute all commands on the master node of the existing cluster as the root user.

Pre-switch preparation

Before switching between revisions, follow these steps:

  1. Make sure that the DP queues are empty.
  2. Determine the current DP revision and version.
  3. Verify that you can switch from the current version to the desired one.

Queue check

Make sure the DP queues are empty and there are no running tasks that could interfere with the switch:

d8 system queue list

Example output (queues are empty)...

Summary:
- 'main' queue: empty.
- 88 other queues (0 active, 88 empty): 0 tasks.
- no tasks to handle.

Determining the current edition and version

To ensure the correctness of further steps, determine the current DP edition used in the cluster. This helps avoid errors during the switch and confirms that the required modules and features are supported in the new edition.

You can find the edition and version currently used in the cluster on the main page of the DP web interface, or by using CLI commands:

  • edition:

    d8 k -n d8-system exec -it svc/deckhouse-leader -c deckhouse -- deckhouse-controller global values -o yaml | yq '.deckhouseEdition'
    
  • version:

    d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}'
    

Checking whether switching to the desired edition is possible

Different DP editions support different sets of modules, Kubernetes versions, and features. It is important to understand what functional changes will occur during the switch and which capabilities will become unavailable. This will help you prepare for the switching process.

A comparison of DP editions by module set can be found in the documentation on the Edition comparison page.

What to consider before switching:

  • To DP CE
  • To DP BE
  • To DP SE
  • To DP SE+
  • To DP EE
  1. Determine the list of internal modules used in the cluster that are not supported in DP new edition. To do this, follow these steps:

    1. Get the list of internal modules not supported in DP ce:

      (set -e
      trap 'echo "Execution error"' ERR
      
      echo
      echo "Running the deckhouse pod of the new edition with the command 'sleep -- infinity'"
      
      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
      d8 k run ce-image --image=registry.deckhouse.io/deckhouse/ce/install:$DECKHOUSE_VERSION \
      --command sleep -- infinity
      d8 k wait --for=condition=ready pod/ce-image --timeout=300s
      
      echo
      echo "Getting information about internal modules from the current and new edition"
      echo "Comparing internal modules of the current and new edition"
      NEW_MODULES=$(d8 k exec ce-image -- ls -l deckhouse/modules/ |   grep -oE "\d.*-\w*" | awk {'print $9'} | cut -c5-)
      USED_MODULES=$(d8 k get modules -o custom-columns=NAME:.metadata.name,SOURCE:.properties.source,STATE:.properties.state,ENABLED:.status.phase | grep Embedded | grep -E 'Enabled|Ready' | awk {'print $1'})
      MODULES_TO_DISABLE=$(echo "$USED_MODULES" | tr ' ' '\n' | grep -Fxv -f <(echo "$NEW_MODULES" | tr ' ' '\n') 2>&1) || { status=$?; [ $status -eq 1 ] && MODULES_TO_DISABLE="" || exit $status; }
      
      echo
      echo "Modules not supported in the desired edition (edition code - ce, version - $DECKHOUSE_VERSION):"
      echo MODULES_TO_DISABLE=\"$MODULES_TO_DISABLE\"
      )
      
      echo
      echo "Deleting the deckhouse pod of the new edition"
      d8 k delete pod/ce-image secret/ce-image-pull-secret --wait=true --ignore-not-found=true
      
    2. Disable the modules from the list if acceptable (the module functionality is not used, or you are ready to give it up). Otherwise, abort the switching process.

      You can disable the modules from the list in the DP web interface under System → System Management → Deckhouse → Modules, or by running the following command:

      echo $MODULES_TO_DISABLE | tr ' ' '\n' | awk {'print "d8 platform module disable",$1'} | bash
      
  2. Determine the list of external modules launched via moduleSource/deckhouse that are not supported in DP new edition. To do this, follow these steps:

    1. Get the list of external modules launched via moduleSource/deckhouse with the result of checking their availability in DP new edition:

      (set -e
      trap 'echo "Execution error"' ERR
      d8-crane() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr "$@"
         else
            crane "$@"
         fi
      }
      
      
      check_external_modules() {
         local repo="$1"
      
         local modules=$(d8 k get modulerelease -o json | jq -r '.items[] | select(.metadata.ownerReferences[] | select(.kind=="ModuleSource" and .name=="deckhouse")) | "\(.spec.moduleName) \(.spec.version)"')
         [[ -z "$modules" ]] && return 0
         local registry_modules=$(d8-crane ls "$repo" 2>/dev/null)
      
         echo "Module | In registry"
         echo "-------|------------"
      
         echo "$modules" | while read module version; do
            grep -qx "$module" <<< "$registry_modules" && list="✅" || list="❌"
            echo "$module | $list"
         done
      }
      
      d8-crane --help > /dev/null 2>&1 || { echo "ERROR: crane not found"; exit 1; }
      check_external_modules "registry.deckhouse.io/deckhouse/ce/modules"
      )
      
    2. Disable the modules that were not found in the new edition if acceptable. Otherwise, abort the switching process.

  3. Make sure all tasks in the DP queue are complete before continuing the switching process:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    
  1. Determine the list of internal modules used in the cluster that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of internal modules not supported in DP be:

      (set -e
      trap 'echo "Execution error"' ERR
      
      echo
      echo "Running the deckhouse pod of the new edition with the command 'sleep -- infinity'"
      
      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
      d8 k create secret docker-registry be-image-pull-secret --docker-server=registry.deckhouse.io --docker-username=license-token --docker-password=${LICENSE_TOKEN}
      d8 k run be-image --image=registry.deckhouse.io/deckhouse/be/install:$DECKHOUSE_VERSION \
      --overrides="{\"spec\": {\"imagePullSecrets\":[{\"name\": \"be-image-pull-secret\"}]}}" \
      --command sleep -- infinity
      d8 k wait --for=condition=ready pod/be-image --timeout=300s
      
      echo
      echo "Getting information about internal modules from the current and new edition"
      echo "Comparing internal modules of the current and new edition"
      NEW_MODULES=$(d8 k exec be-image -- ls -l deckhouse/modules/ |   grep -oE "\d.*-\w*" | awk {'print $9'} | cut -c5-)
      USED_MODULES=$(d8 k get modules -o custom-columns=NAME:.metadata.name,SOURCE:.properties.source,STATE:.properties.state,ENABLED:.status.phase | grep Embedded | grep -E 'Enabled|Ready' | awk {'print $1'})
      MODULES_TO_DISABLE=$(echo "$USED_MODULES" | tr ' ' '\n' | grep -Fxv -f <(echo "$NEW_MODULES" | tr ' ' '\n') 2>&1) || { status=$?; [ $status -eq 1 ] && MODULES_TO_DISABLE="" || exit $status; }
      
      echo
      echo "Modules not supported in the desired edition (edition code - be, version - $DECKHOUSE_VERSION):"
      echo MODULES_TO_DISABLE=\"$MODULES_TO_DISABLE\"
      )
      
      echo
      echo "Deleting the deckhouse pod of the new edition"
      d8 k delete pod/be-image secret/be-image-pull-secret --wait=true --ignore-not-found=true
      
    3. Disable the modules from the list if acceptable (the module functionality is not used, or you are ready to give it up). Otherwise, abort the switching process.

      You can disable the modules from the list in the DP web interface under System → System Management → Deckhouse → Modules, or by running the following command:

      echo $MODULES_TO_DISABLE | tr ' ' '\n' | awk {'print "d8 platform module disable",$1'} | bash
      
  2. Determine the list of external modules launched via moduleSource/deckhouse that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of external modules launched via moduleSource/deckhouse with the result of checking their availability in DP new edition:

      (set -e
      trap 'echo "Execution error"' ERR
      d8-crane() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr "$@"
         else
            crane "$@"
         fi
      }
      
      d8-crane-login() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr login "$1" -u "license-token" -p "$2"
         else
            crane auth login "$1" -u "license-token" -p "$2"
         fi
      }
      
      check_external_modules() {
         local repo="$1"
      
         local modules=$(d8 k get modulerelease -o json | jq -r '.items[] | select(.metadata.ownerReferences[] | select(.kind=="ModuleSource" and .name=="deckhouse")) | "\(.spec.moduleName) \(.spec.version)"')
         [[ -z "$modules" ]] && return 0
         local registry_modules=$(d8-crane ls "$repo" 2>/dev/null)
      
         echo "Module | In registry"
         echo "-------|------------"
      
         echo "$modules" | while read module version; do
            grep -qx "$module" <<< "$registry_modules" && list="✅" || list="❌"
            echo "$module | $list"
         done
      }
      
      d8-crane --help > /dev/null 2>&1 || { echo "ERROR: crane not found"; exit 1; }
      d8-crane-login "registry.deckhouse.io" "$LICENSE_TOKEN"
      check_external_modules "registry.deckhouse.io/deckhouse/be/modules"
      )
      
    3. Disable the modules that were not found in the new edition if acceptable. Otherwise, abort the switching process.

  3. Make sure all tasks in the DP queue are complete before continuing the switching process:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    
  1. Determine the list of internal modules used in the cluster that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of internal modules not supported in DP se:

      (set -e
      trap 'echo "Execution error"' ERR
      
      echo
      echo "Running the deckhouse pod of the new edition with the command 'sleep -- infinity'"
      
      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
      d8 k create secret docker-registry se-image-pull-secret --docker-server=registry.deckhouse.io --docker-username=license-token --docker-password=${LICENSE_TOKEN}
      d8 k run se-image --image=registry.deckhouse.io/deckhouse/se/install:$DECKHOUSE_VERSION \
      --overrides="{\"spec\": {\"imagePullSecrets\":[{\"name\": \"se-image-pull-secret\"}]}}" \
      --command sleep -- infinity
      d8 k wait --for=condition=ready pod/se-image --timeout=300s
      
      echo
      echo "Getting information about internal modules from the current and new edition"
      echo "Comparing internal modules of the current and new edition"
      NEW_MODULES=$(d8 k exec se-image -- ls -l deckhouse/modules/ |   grep -oE "\d.*-\w*" | awk {'print $9'} | cut -c5-)
      USED_MODULES=$(d8 k get modules -o custom-columns=NAME:.metadata.name,SOURCE:.properties.source,STATE:.properties.state,ENABLED:.status.phase | grep Embedded | grep -E 'Enabled|Ready' | awk {'print $1'})
      MODULES_TO_DISABLE=$(echo "$USED_MODULES" | tr ' ' '\n' | grep -Fxv -f <(echo "$NEW_MODULES" | tr ' ' '\n') 2>&1) || { status=$?; [ $status -eq 1 ] && MODULES_TO_DISABLE="" || exit $status; }
      
      echo
      echo "Modules not supported in the desired edition (edition code - se, version - $DECKHOUSE_VERSION):"
      echo MODULES_TO_DISABLE=\"$MODULES_TO_DISABLE\"
      )
      
      echo
      echo "Deleting the deckhouse pod of the new edition"
      d8 k delete pod/se-image secret/se-image-pull-secret --wait=true --ignore-not-found=true
      
    3. Disable the modules from the list if acceptable (the module functionality is not used, or you are ready to give it up). Otherwise, abort the switching process.

      You can disable the modules from the list in the DP web interface under System → System Management → Deckhouse → Modules, or by running the following command:

      echo $MODULES_TO_DISABLE | tr ' ' '\n' | awk {'print "d8 platform module disable",$1'} | bash
      
  2. Determine the list of external modules launched via moduleSource/deckhouse that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of external modules launched via moduleSource/deckhouse with the result of checking their availability in DP new edition:

      (set -e
      trap 'echo "Execution error"' ERR
      d8-crane() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr "$@"
         else
            crane "$@"
         fi
      }
      
      d8-crane-login() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr login "$1" -u "license-token" -p "$2"
         else
            crane auth login "$1" -u "license-token" -p "$2"
         fi
      }
      
      check_external_modules() {
         local repo="$1"
      
         local modules=$(d8 k get modulerelease -o json | jq -r '.items[] | select(.metadata.ownerReferences[] | select(.kind=="ModuleSource" and .name=="deckhouse")) | "\(.spec.moduleName) \(.spec.version)"')
         [[ -z "$modules" ]] && return 0
         local registry_modules=$(d8-crane ls "$repo" 2>/dev/null)
      
         echo "Module | In registry"
         echo "-------|------------"
      
         echo "$modules" | while read module version; do
            grep -qx "$module" <<< "$registry_modules" && list="✅" || list="❌"
            echo "$module | $list"
         done
      }
      
      d8-crane --help > /dev/null 2>&1 || { echo "ERROR: crane not found"; exit 1; }
      d8-crane-login "registry.deckhouse.io" "$LICENSE_TOKEN"
      check_external_modules "registry.deckhouse.io/deckhouse/se/modules"
      )
      
    3. Disable the modules that were not found in the new edition if acceptable. Otherwise, abort the switching process.

  3. Make sure all tasks in the DP queue are complete before continuing the switching process:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    
  1. Determine the list of internal modules used in the cluster that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of internal modules not supported in DP se-plus:

      (set -e
      trap 'echo "Execution error"' ERR
      
      echo
      echo "Running the deckhouse pod of the new edition with the command 'sleep -- infinity'"
      
      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
      d8 k create secret docker-registry se-plus-image-pull-secret --docker-server=registry.deckhouse.io --docker-username=license-token --docker-password=${LICENSE_TOKEN}
      d8 k run se-plus-image --image=registry.deckhouse.io/deckhouse/se-plus/install:$DECKHOUSE_VERSION \
      --overrides="{\"spec\": {\"imagePullSecrets\":[{\"name\": \"se-plus-image-pull-secret\"}]}}" \
      --command sleep -- infinity
      d8 k wait --for=condition=ready pod/se-plus-image --timeout=300s
      
      echo
      echo "Getting information about internal modules from the current and new edition"
      echo "Comparing internal modules of the current and new edition"
      NEW_MODULES=$(d8 k exec se-plus-image -- ls -l deckhouse/modules/ |   grep -oE "\d.*-\w*" | awk {'print $9'} | cut -c5-)
      USED_MODULES=$(d8 k get modules -o custom-columns=NAME:.metadata.name,SOURCE:.properties.source,STATE:.properties.state,ENABLED:.status.phase | grep Embedded | grep -E 'Enabled|Ready' | awk {'print $1'})
      MODULES_TO_DISABLE=$(echo "$USED_MODULES" | tr ' ' '\n' | grep -Fxv -f <(echo "$NEW_MODULES" | tr ' ' '\n') 2>&1) || { status=$?; [ $status -eq 1 ] && MODULES_TO_DISABLE="" || exit $status; }
      
      echo
      echo "Modules not supported in the desired edition (edition code - se-plus, version - $DECKHOUSE_VERSION):"
      echo MODULES_TO_DISABLE=\"$MODULES_TO_DISABLE\"
      )
      
      echo
      echo "Deleting the deckhouse pod of the new edition"
      d8 k delete pod/se-plus-image secret/se-plus-image-pull-secret --wait=true --ignore-not-found=true
      
    3. Disable the modules from the list if acceptable (the module functionality is not used, or you are ready to give it up). Otherwise, abort the switching process.

      You can disable the modules from the list in the DP web interface under System → System Management → Deckhouse → Modules, or by running the following command:

      echo $MODULES_TO_DISABLE | tr ' ' '\n' | awk {'print "d8 platform module disable",$1'} | bash
      
  2. Determine the list of external modules launched via moduleSource/deckhouse that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of external modules launched via moduleSource/deckhouse with the result of checking their availability in DP new edition:

      (set -e
      trap 'echo "Execution error"' ERR
      d8-crane() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr "$@"
         else
            crane "$@"
         fi
      }
      
      d8-crane-login() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr login "$1" -u "license-token" -p "$2"
         else
            crane auth login "$1" -u "license-token" -p "$2"
         fi
      }
      
      check_external_modules() {
         local repo="$1"
      
         local modules=$(d8 k get modulerelease -o json | jq -r '.items[] | select(.metadata.ownerReferences[] | select(.kind=="ModuleSource" and .name=="deckhouse")) | "\(.spec.moduleName) \(.spec.version)"')
         [[ -z "$modules" ]] && return 0
         local registry_modules=$(d8-crane ls "$repo" 2>/dev/null)
      
         echo "Module | In registry"
         echo "-------|------------"
      
         echo "$modules" | while read module version; do
            grep -qx "$module" <<< "$registry_modules" && list="✅" || list="❌"
            echo "$module | $list"
         done
      }
      
      d8-crane --help > /dev/null 2>&1 || { echo "ERROR: crane not found"; exit 1; }
      d8-crane-login "registry.deckhouse.io" "$LICENSE_TOKEN"
      check_external_modules "registry.deckhouse.io/deckhouse/se-plus/modules"
      )
      
    3. Disable the modules that were not found in the new edition if acceptable. Otherwise, abort the switching process.

  3. Make sure all tasks in the DP queue are complete before continuing the switching process:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    
  1. Determine the list of internal modules used in the cluster that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of internal modules not supported in DP ee:

      (set -e
      trap 'echo "Execution error"' ERR
      
      echo
      echo "Running the deckhouse pod of the new edition with the command 'sleep -- infinity'"
      
      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
      d8 k create secret docker-registry ee-image-pull-secret --docker-server=registry.deckhouse.io --docker-username=license-token --docker-password=${LICENSE_TOKEN}
      d8 k run ee-image --image=registry.deckhouse.io/deckhouse/ee/install:$DECKHOUSE_VERSION \
      --overrides="{\"spec\": {\"imagePullSecrets\":[{\"name\": \"ee-image-pull-secret\"}]}}" \
      --command sleep -- infinity
      d8 k wait --for=condition=ready pod/ee-image --timeout=300s
      
      echo
      echo "Getting information about internal modules from the current and new edition"
      echo "Comparing internal modules of the current and new edition"
      NEW_MODULES=$(d8 k exec ee-image -- ls -l deckhouse/modules/ |   grep -oE "\d.*-\w*" | awk {'print $9'} | cut -c5-)
      USED_MODULES=$(d8 k get modules -o custom-columns=NAME:.metadata.name,SOURCE:.properties.source,STATE:.properties.state,ENABLED:.status.phase | grep Embedded | grep -E 'Enabled|Ready' | awk {'print $1'})
      MODULES_TO_DISABLE=$(echo "$USED_MODULES" | tr ' ' '\n' | grep -Fxv -f <(echo "$NEW_MODULES" | tr ' ' '\n') 2>&1) || { status=$?; [ $status -eq 1 ] && MODULES_TO_DISABLE="" || exit $status; }
      
      echo
      echo "Modules not supported in the desired edition (edition code - ee, version - $DECKHOUSE_VERSION):"
      echo MODULES_TO_DISABLE=\"$MODULES_TO_DISABLE\"
      )
      
      echo
      echo "Deleting the deckhouse pod of the new edition"
      d8 k delete pod/ee-image secret/ee-image-pull-secret --wait=true --ignore-not-found=true
      
    3. Disable the modules from the list if acceptable (the module functionality is not used, or you are ready to give it up). Otherwise, abort the switching process.

      You can disable the modules from the list in the DP web interface under System → System Management → Deckhouse → Modules, or by running the following command:

      echo $MODULES_TO_DISABLE | tr ' ' '\n' | awk {'print "d8 platform module disable",$1'} | bash
      
  2. Determine the list of external modules launched via moduleSource/deckhouse that are not supported in DP new edition. To do this, follow these steps:

    1. Set the environment variable with the license key for the edition you plan to switch to:

      LICENSE_TOKEN=<LICENSE_KEY>
      
    2. Get the list of external modules launched via moduleSource/deckhouse with the result of checking their availability in DP new edition:

      (set -e
      trap 'echo "Execution error"' ERR
      d8-crane() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr "$@"
         else
            crane "$@"
         fi
      }
      
      d8-crane-login() {
         if command -v d8 &>/dev/null && d8 cr &>/dev/null 2>&1; then
            d8 cr login "$1" -u "license-token" -p "$2"
         else
            crane auth login "$1" -u "license-token" -p "$2"
         fi
      }
      
      check_external_modules() {
         local repo="$1"
      
         local modules=$(d8 k get modulerelease -o json | jq -r '.items[] | select(.metadata.ownerReferences[] | select(.kind=="ModuleSource" and .name=="deckhouse")) | "\(.spec.moduleName) \(.spec.version)"')
         [[ -z "$modules" ]] && return 0
         local registry_modules=$(d8-crane ls "$repo" 2>/dev/null)
      
         echo "Module | In registry"
         echo "-------|------------"
      
         echo "$modules" | while read module version; do
            grep -qx "$module" <<< "$registry_modules" && list="✅" || list="❌"
            echo "$module | $list"
         done
      }
      
      d8-crane --help > /dev/null 2>&1 || { echo "ERROR: crane not found"; exit 1; }
      d8-crane-login "registry.deckhouse.io" "$LICENSE_TOKEN"
      check_external_modules "registry.deckhouse.io/deckhouse/ee/modules"
      )
      
    3. Disable the modules that were not found in the new edition if acceptable. Otherwise, abort the switching process.

  3. Make sure all tasks in the DP queue are complete before continuing the switching process:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    

Switching the edition

Selecting a switching method

When selecting a switching method, consider how the DP container image repository is configured within the cluster.

There are two ways to work with the DP container image registry:

  • Using the registry module — (recommended) — the configuration for working with the DP registry is specified in the registry section of the deckhouse module parameters (ModuleConfig deckhouse). This method provides a smoother transition process and automatic verification that the required images are present. If your cluster uses this method for working with the DP container registry, use the section “Switching Using the registry Module” to switch editions.

  • Without using the registry module — the configuration for working with the DP registry is set during cluster installation in InitConfiguration, and the registry.mode of the deckhouse module (ModuleConfig deckhouse) is set to Unmanaged, and the registry.unmanaged parameter of the deckhouse module is not specified.

    This method is the only one available for managed Kubernetes clusters where the control plane is managed by a cloud provider rather than DP (e.g. Amazon EKS, Azure AKS, Google GKE, etc.).

    If your cluster uses this method for managing the DP container image repository, refer to the section “Switching without using the registry module” to switch versions.

Switching using the registry module

  • Before proceeding, complete the preparatory steps described in the Pre-switch preparation section.
  • This switching method must be performed only in Unmanaged mode. Make sure that the cluster uses the registry module. ModuleConfig deckhouse must contain the registry parameters of the previous DP edition in Unmanaged mode. If this is not the case, perform the migration to using the registry module and switch the registry to Unmanaged mode.
  • This method is not applicable for managed Kubernetes (EKS, AKS, GKE).
  1. In ModuleConfig deckhouse, set imagesRepo to the target edition and checkMode: Relax:

    Run the command to edit ModuleConfig deckhouse:

    d8 k edit moduleconfig deckhouse
    

    Choose the example for your target edition:

    • DP CE
    • DP BE
    • DP SE
    • DP SE+
    • DP EE
    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: deckhouse
    spec:
      version: 1
      enabled: true
      settings:
        registry:
          mode: Unmanaged
          unmanaged:
            checkMode: Relax
            imagesRepo: registry.deckhouse.io/deckhouse/ce
            scheme: HTTPS
    
    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: deckhouse
    spec:
      version: 1
      enabled: true
      settings:
        registry:
          mode: Unmanaged
          unmanaged:
            license: <LICENSE_KEY>
            checkMode: Relax
            imagesRepo: registry.deckhouse.io/deckhouse/be
            scheme: HTTPS
    
    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: deckhouse
    spec:
      version: 1
      enabled: true
      settings:
        registry:
          mode: Unmanaged
          unmanaged:
            license: <LICENSE_KEY>
            checkMode: Relax
            imagesRepo: registry.deckhouse.io/deckhouse/se
            scheme: HTTPS
    
    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: deckhouse
    spec:
      version: 1
      enabled: true
      settings:
        registry:
          mode: Unmanaged
          unmanaged:
            license: <LICENSE_KEY>
            checkMode: Relax
            imagesRepo: registry.deckhouse.io/deckhouse/se-plus
            scheme: HTTPS
    
    apiVersion: deckhouse.io/v1alpha1
    kind: ModuleConfig
    metadata:
      name: deckhouse
    spec:
      version: 1
      enabled: true
      settings:
        registry:
          mode: Unmanaged
          unmanaged:
            license: <LICENSE_KEY>
            checkMode: Relax
            imagesRepo: registry.deckhouse.io/deckhouse/ee
            scheme: HTTPS
    
  2. Make sure that deckhouse pods has started after the registry change:

    1. To do this, run the command:

      d8 k -n d8-system get pods -l "app=deckhouse"
      
    2. If Deckhouse is in ImagePullBackoff with an error downloading sidecar containers, specify the actual images:

      Get the actual digests of the sidecar images:

      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F':' '{print $1}')
      DKP_TAG=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F':' '{print $2}')
      
      d8 k run dkp-image --image=$DECKHOUSE_VERSION/install:$DKP_TAG --command sleep -- infinity
      d8 k wait --for=condition=ready pod/dkp-image --timeout=300s
      
      DECKHOUSE_KUBE_RBAC_PROXY=$(d8 k exec dkp-image -- cat /deckhouse/candi/images_digests.json | jq -r ".common.kubeRbacProxy")
      DECKHOUSE_INIT=$(d8 k exec dkp-image -- cat /deckhouse/candi/images_digests.json | jq -r ".deckhouse.init")
      
      d8 k delete pod dkp-image
      

      Apply images:

      d8 k -n d8-system set image deployment/deckhouse \
      kube-rbac-proxy=$DECKHOUSE_VERSION@$DECKHOUSE_KUBE_RBAC_PROXY \
      init-downloaded-modules=$DECKHOUSE_VERSION@$DECKHOUSE_INIT
      
    3. Make sure there is no image download error:

      d8 k -n d8-system get pods -l "app=deckhouse"
      
  3. Wait for the switch to complete.

    Check the status:

    d8 k -n d8-system -o yaml get secret registry-state | yq -C -P '.data | del .state | map_values(@base64d) | .conditions = (.conditions | from_yaml) | {"conditions": [.conditions[] | select(.type == "Ready" or .type == "RegistryContainsRequiredImages")]}'
    

    Example of a successful output:

    conditions:
      - lastTransitionTime: "2026-05-05T13:53:23Z"
        message: |-
          Mode: Default
          registry.deckhouse.io: all 1 items are checked
        reason: Ready
        status: "True"
        type: RegistryContainsRequiredImages
      - lastTransitionTime: "2026-05-05T13:54:49Z"
        message: ""
        reason: ""
        status: "True"
        type: Ready
    
  4. Set checkMode back to Default:

    d8 k patch moduleconfig deckhouse --type=json -p='[{"op": "replace", "path": "/spec/settings/registry/unmanaged/checkMode", "value": "Default"}]'
    
  5. Check the switch status again.

    Check the status:

    d8 k -n d8-system -o yaml get secret registry-state | yq -C -P '.data | del .state | map_values(@base64d) | .conditions = (.conditions | from_yaml) | {"conditions": [.conditions[] | select(.type == "Ready" or .type == "RegistryContainsRequiredImages")]}'
    

    Example of a successful output:

    conditions:
      - lastTransitionTime: "2026-05-05T13:53:23Z"
        message: |-
          Mode: Default
          registry.deckhouse.io: all 182 items are checked
        reason: Ready
        status: "True"
        type: RegistryContainsRequiredImages
      - lastTransitionTime: "2026-05-05T13:54:49Z"
        message: ""
        reason: ""
        status: "True"
        type: Ready
    
  6. Check for pods with image pull errors:

    d8 k get pods -A | awk 'NR==1 || /^d8-/' | grep -E 'ImagePullBackOff|ErrImagePull'
    

    For each problematic module, run the following commands on all master nodes, specifying the module name:

    rm -rf /var/lib/deckhouse/downloaded/<MODULE_NAME>/
    d8 k rollout restart deploy -n d8-system deckhouse
    
  7. Check for pods using the old registry:

    d8 k get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | select(.image | contains("deckhouse.io/deckhouse/<PREVIOUS_EDITION_CODE>"))) | .metadata.namespace + "\t" + .metadata.name' | sort | uniq
    

Switching without the registry module

Choose the target edition:

  • DP CE/BE/SE/SE+/EE
  1. Run the command to set the authentication credentials for the image registry:

    • DP CE
    • DP BE
    • DP SE
    • DP SE+
    • DP EE

    Not required for DP CE.

    If you need to add configuration for an additional registry in containerd, refer to the How to add configuration for an additional registry in containerd section.

    For ContainerdV1:

    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv1-be-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        mkdir -p /etc/containerd/conf.d
        bb-sync-file /etc/containerd/conf.d/be-registry.toml - << "EOF"
        [plugins]
          [plugins."io.containerd.grpc.v1.cri"]
            [plugins."io.containerd.grpc.v1.cri".registry.configs]
              [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
                [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.deckhouse.io"]
                  endpoint = ["https://registry.deckhouse.io"]
              [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.deckhouse.io".auth]
                auth = "$AUTH_STRING"
        EOF
    EOF
    

    For ContainerdV2:

    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv2-be-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 31
      content: |
        mkdir -p /etc/containerd/registry.d/registry.deckhouse.io
        bb-sync-file "/etc/containerd/registry.d/registry.deckhouse.io/hosts.toml" - << "EOF"
        [host]
    
          [host."https://registry.deckhouse.io"]
             capabilities = ["pull", "resolve"]
             [host."https://registry.deckhouse.io".auth]
                username = "license-token"
                password = "${LICENSE_TOKEN}"
        EOF
    EOF
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    If you need to add configuration for an additional registry in containerd, refer to the How to add configuration for an additional registry in containerd section.

    For ContainerdV1:

    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv1-se-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        mkdir -p /etc/containerd/conf.d
        bb-sync-file /etc/containerd/conf.d/se-registry.toml - << "EOF"
        [plugins]
          [plugins."io.containerd.grpc.v1.cri"]
            [plugins."io.containerd.grpc.v1.cri".registry.configs]
              [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
                [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.deckhouse.io"]
                  endpoint = ["https://registry.deckhouse.io"]
              [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.deckhouse.io".auth]
                auth = "$AUTH_STRING"
        EOF
    EOF
    

    For ContainerdV2:

    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv2-se-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 31
      content: |
        mkdir -p /etc/containerd/registry.d/registry.deckhouse.io
        bb-sync-file "/etc/containerd/registry.d/registry.deckhouse.io/hosts.toml" - << "EOF"
        [host]
    
          [host."https://registry.deckhouse.io"]
             capabilities = ["pull", "resolve"]
             [host."https://registry.deckhouse.io".auth]
                username = "license-token"
                password = "${LICENSE_TOKEN}"
        EOF
    EOF
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    If you need to add configuration for an additional registry in containerd, refer to the How to add configuration for an additional registry in containerd section.

    For ContainerdV1:

    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv1-se-plus-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        mkdir -p /etc/containerd/conf.d
        bb-sync-file /etc/containerd/conf.d/se-plus-registry.toml - << "EOF"
        [plugins]
          [plugins."io.containerd.grpc.v1.cri"]
            [plugins."io.containerd.grpc.v1.cri".registry.configs]
              [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
                [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.deckhouse.io"]
                  endpoint = ["https://registry.deckhouse.io"]
              [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.deckhouse.io".auth]
                auth = "$AUTH_STRING"
        EOF
    EOF
    

    For ContainerdV2:

    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv2-se-plus-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 31
      content: |
        mkdir -p /etc/containerd/registry.d/registry.deckhouse.io
        bb-sync-file "/etc/containerd/registry.d/registry.deckhouse.io/hosts.toml" - << "EOF"
        [host]
    
          [host."https://registry.deckhouse.io"]
             capabilities = ["pull", "resolve"]
             [host."https://registry.deckhouse.io".auth]
                username = "license-token"
                password = "${LICENSE_TOKEN}"
        EOF
    EOF
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    If you need to add configuration for an additional registry in containerd, refer to the How to add configuration for an additional registry in containerd section.

    For ContainerdV1:

    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv1-ee-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        mkdir -p /etc/containerd/conf.d
        bb-sync-file /etc/containerd/conf.d/ee-registry.toml - << "EOF"
        [plugins]
          [plugins."io.containerd.grpc.v1.cri"]
            [plugins."io.containerd.grpc.v1.cri".registry.configs]
              [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
                [plugins."io.containerd.grpc.v1.cri".registry.mirrors."registry.deckhouse.io"]
                  endpoint = ["https://registry.deckhouse.io"]
              [plugins."io.containerd.grpc.v1.cri".registry.configs."registry.deckhouse.io".auth]
                auth = "$AUTH_STRING"
        EOF
    EOF
    

    For ContainerdV2:

    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: containerdv2-ee-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 31
      content: |
        mkdir -p /etc/containerd/registry.d/registry.deckhouse.io
        bb-sync-file "/etc/containerd/registry.d/registry.deckhouse.io/hosts.toml" - << "EOF"
        [host]
    
          [host."https://registry.deckhouse.io"]
             capabilities = ["pull", "resolve"]
             [host."https://registry.deckhouse.io".auth]
                username = "license-token"
                password = "${LICENSE_TOKEN}"
        EOF
    EOF
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    
  2. Switch the container image registry:

    • DP CE
    • DP BE
    • DP SE
    • DP SE+
    • DP EE
    DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
    
    d8 k -n d8-system exec -ti svc/deckhouse-leader -c deckhouse -- deckhouse-controller helper change-registry \
    --new-deckhouse-tag=$DECKHOUSE_VERSION \
    registry.deckhouse.io/deckhouse/ce
    
    DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
    
    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    DOCKER_CONFIG_JSON=$(echo -n "{\"auths\": {\"registry.deckhouse.io\": {\"username\": \"license-token\", \"password\": \"${LICENSE_TOKEN}\", \"auth\": \"${AUTH_STRING}\"}}}" | base64 -w 0)
    
    d8 k --as system:sudouser -n d8-cloud-instance-manager patch secret deckhouse-registry \
    --type merge \
    --patch="{\"data\":{\".dockerconfigjson\":\"$DOCKER_CONFIG_JSON\"}}"
    
    d8 k -n d8-system exec -ti svc/deckhouse-leader -c deckhouse -- deckhouse-controller helper change-registry \
    --user=license-token \
    --password=$LICENSE_TOKEN \
    --new-deckhouse-tag=$DECKHOUSE_VERSION \
    registry.deckhouse.io/deckhouse/be
    
    DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
    
    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    DOCKER_CONFIG_JSON=$(echo -n "{\"auths\": {\"registry.deckhouse.io\": {\"username\": \"license-token\", \"password\": \"${LICENSE_TOKEN}\", \"auth\": \"${AUTH_STRING}\"}}}" | base64 -w 0)
    
    d8 k --as system:sudouser -n d8-cloud-instance-manager patch secret deckhouse-registry \
    --type merge \
    --patch="{\"data\":{\".dockerconfigjson\":\"$DOCKER_CONFIG_JSON\"}}"
    
    d8 k -n d8-system exec -ti svc/deckhouse-leader -c deckhouse -- deckhouse-controller helper change-registry \
    --user=license-token \
    --password=$LICENSE_TOKEN \
    --new-deckhouse-tag=$DECKHOUSE_VERSION \
    registry.deckhouse.io/deckhouse/se
    
    DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
    
    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    DOCKER_CONFIG_JSON=$(echo -n "{\"auths\": {\"registry.deckhouse.io\": {\"username\": \"license-token\", \"password\": \"${LICENSE_TOKEN}\", \"auth\": \"${AUTH_STRING}\"}}}" | base64 -w 0)
    
    d8 k --as system:sudouser -n d8-cloud-instance-manager patch secret deckhouse-registry \
    --type merge \
    --patch="{\"data\":{\".dockerconfigjson\":\"$DOCKER_CONFIG_JSON\"}}"
    
    d8 k -n d8-system exec -ti svc/deckhouse-leader -c deckhouse -- deckhouse-controller helper change-registry \
    --user=license-token \
    --password=$LICENSE_TOKEN \
    --new-deckhouse-tag=$DECKHOUSE_VERSION \
    registry.deckhouse.io/deckhouse/se-plus
    
    DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F: '{print $NF}')
    
    AUTH_STRING="$(echo -n license-token:${LICENSE_TOKEN} | base64 )"
    DOCKER_CONFIG_JSON=$(echo -n "{\"auths\": {\"registry.deckhouse.io\": {\"username\": \"license-token\", \"password\": \"${LICENSE_TOKEN}\", \"auth\": \"${AUTH_STRING}\"}}}" | base64 -w 0)
    
    d8 k --as system:sudouser -n d8-cloud-instance-manager patch secret deckhouse-registry \
    --type merge \
    --patch="{\"data\":{\".dockerconfigjson\":\"$DOCKER_CONFIG_JSON\"}}"
    
    d8 k -n d8-system exec -ti svc/deckhouse-leader -c deckhouse -- deckhouse-controller helper change-registry \
    --user=license-token \
    --password=$LICENSE_TOKEN \
    --new-deckhouse-tag=$DECKHOUSE_VERSION \
    registry.deckhouse.io/deckhouse/ee
    
  3. Make sure that deckhouse pods has started after the registry change:

    1. To do this, run the command:

      d8 k -n d8-system get pods -l "app=deckhouse"
      
    2. If Deckhouse is in ImagePullBackoff with an error downloading sidecar containers, specify the actual images:

      Get the actual digests of the sidecar images:

      DECKHOUSE_VERSION=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F':' '{print $1}')
      DKP_TAG=$(d8 k -n d8-system get deploy deckhouse -ojson | jq -r '.spec.template.spec.containers[] | select(.name == "deckhouse") | .image' | awk -F':' '{print $2}')
      
      d8 k run dkp-image --image=$DECKHOUSE_VERSION/install:$DKP_TAG --command sleep -- infinity
      d8 k wait --for=condition=ready pod/dkp-image --timeout=300s
      
      DECKHOUSE_KUBE_RBAC_PROXY=$(d8 k exec dkp-image -- cat /deckhouse/candi/images_digests.json | jq -r ".common.kubeRbacProxy")
      DECKHOUSE_INIT=$(d8 k exec dkp-image -- cat /deckhouse/candi/images_digests.json | jq -r ".deckhouse.init")
      
      d8 k delete pod dkp-image
      

      Apply images:

      d8 k -n d8-system set image deployment/deckhouse \
      kube-rbac-proxy=$DECKHOUSE_VERSION@$DECKHOUSE_KUBE_RBAC_PROXY \
      init-downloaded-modules=$DECKHOUSE_VERSION@$DECKHOUSE_INIT
      
    3. Make sure there is no image download error:

      d8 k -n d8-system get pods -l "app=deckhouse"
      
  4. Wait for DP to be ready:

    d8 system queue list
    

    Example output (queues are empty)...

    Summary:
    - 'main' queue: empty.
    - 88 other queues (0 active, 88 empty): 0 tasks.
    - no tasks to handle.
    
  5. Check for pods with image pull errors:

    d8 k get pods -A | awk 'NR==1 || /^d8-/' | grep -E 'ImagePullBackOff|ErrImagePull'
    

    For each problematic module, run the following commands on all master nodes, specifying the module name:

    rm -rf /var/lib/deckhouse/downloaded/<MODULE_NAME>/
    d8 k rollout restart deploy -n d8-system deckhouse
    
  6. Check for pods using the old registry:

    d8 k get pods -A -o json | jq -r '.items[] | select(.spec.containers[] | select(.image | contains("deckhouse.io/deckhouse/<PREVIOUS_EDITION_CODE>"))) | .metadata.namespace + "\t" + .metadata.name' | sort | uniq
    
  7. Perform cleanup:

    • DP CE
    • DP BE
    • DP SE
    • DP SE+
    • DP EE

    Not required for DP CE.

    For ContainerdV1

    d8 k --as=system:sudouser delete ngc containerdv1-be-config.sh
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: del-temp-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        if [ -f /etc/containerd/conf.d/be-registry.toml ]; then
          rm -f /etc/containerd/conf.d/be-registry.toml
        fi
    EOF
    
    d8 k --as=system:sudouser delete ngc del-temp-config.sh
    

    For ContainerdV2

    d8 k --as=system:sudouser delete ngc containerdv2-be-config.sh
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    For ContainerdV1

    d8 k --as=system:sudouser delete ngc containerdv1-se-config.sh
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: del-temp-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        if [ -f /etc/containerd/conf.d/se-registry.toml ]; then
          rm -f /etc/containerd/conf.d/se-registry.toml
        fi
    EOF
    
    d8 k --as=system:sudouser delete ngc del-temp-config.sh
    

    For ContainerdV2

    d8 k --as=system:sudouser delete ngc containerdv2-se-config.sh
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    For ContainerdV1

    d8 k --as=system:sudouser delete ngc containerdv1-se-plus-config.sh
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: del-temp-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        if [ -f /etc/containerd/conf.d/se-plus-registry.toml ]; then
          rm -f /etc/containerd/conf.d/se-plus-registry.toml
        fi
    EOF
    
    d8 k --as=system:sudouser delete ngc del-temp-config.sh
    

    For ContainerdV2

    d8 k --as=system:sudouser delete ngc containerdv2-se-plus-config.sh
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

    For ContainerdV1

    d8 k --as=system:sudouser delete ngc containerdv1-ee-config.sh
    d8 k --as=system:sudouser apply -f - <<EOF
    apiVersion: deckhouse.io/v1alpha1
    kind: NodeGroupConfiguration
    metadata:
      name: del-temp-config.sh
    spec:
      nodeGroups:
      - '*'
      bundles:
      - '*'
      weight: 1
      content: |
        if [ -f /etc/containerd/conf.d/ee-registry.toml ]; then
          rm -f /etc/containerd/conf.d/ee-registry.toml
        fi
    EOF
    
    d8 k --as=system:sudouser delete ngc del-temp-config.sh
    

    For ContainerdV2

    d8 k --as=system:sudouser delete ngc containerdv2-ee-config.sh
    

    Wait for the bashible service to synchronize (the UPTODATE column value for a NodeGroup must match NODES):

    d8 k get ng -o custom-columns=NAME:.metadata.name,NODES:.status.nodes,READY:.status.ready,UPTODATE:.status.upToDate
    

    The bashible log should contain Configuration is in sync, nothing to do:

    journalctl -u bashible -n 5
    

Additional resources