adamant-kite-43734
04/21/2026, 10:38 PMbland-article-62755
04/21/2026, 10:49 PMbland-article-62755
04/21/2026, 10:53 PM$ kc get <http://machines.cluster.x-k8s.io|machines.cluster.x-k8s.io> -n fleet-local
NAME CLUSTER NODENAME PROVIDERID PHASE AGE VERSION
custom-48909be85546 local gpu1 <rke2://gpu1> Running 195d
custom-561bc75fdd0f local gpu3 <rke2://gpu3> Running 195d
custom-6f8b0ffd9f65 local gpu2 <rke2://gpu2> Running 195dbland-article-62755
04/21/2026, 11:54 PMbland-article-62755
04/22/2026, 12:59 AMbland-article-62755
04/22/2026, 12:59 AMNAME STATUS ROLES AGE VERSION
gpu1 Ready <none> 41m v1.34.3+rke2r3
gpu2 Ready control-plane,etcd,master 195d v1.34.3+rke2r3
gpu3 Ready control-plane,etcd,master 196d v1.34.3+rke2r3bland-article-62755
04/22/2026, 2:19 AMbland-article-62755
04/22/2026, 4:02 AMbland-article-62755
04/22/2026, 3:26 PM1.7.1 installer but it didn't get promoted.
Here's a new support bundle.bland-article-62755
04/22/2026, 7:42 PMbland-article-62755
04/24/2026, 3:44 PMbland-article-62755
04/24/2026, 3:48 PMpromote.sh that's used for node promotion.
It was in a configmap that I think got created for something else: kubectl get configmap harvester-helpers -n harvester-system -oyaml
promote.sh: |-
LONGHORN_NAMESPACE="longhorn-system"
KUBECTL="/host/$(readlink /host/var/lib/rancher/rke2/bin)/kubectl"
YQ="/host/usr/bin/yq"
ROLE_LABELS="<http://rke.cattle.io/control-plane-role=true|rke.cattle.io/control-plane-role=true> <http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>"
ETCD_ONLY=false
if [[ -n "$1" && $1 == "<http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>" ]]; then
ETCD_ONLY=true
ROLE_LABELS=$1
fi
case $ROLE_LABELS in
"<http://rke.cattle.io/control-plane-role=true|rke.cattle.io/control-plane-role=true> <http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>" | "<http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>")
;;
*)
echo "ROLE $ROLE_LABELS is not supported."
exit 1
;;
esac
get_machine_from_node() {
$KUBECTL get node $HARVESTER_PROMOTE_NODE_NAME -o jsonpath='{.metadata.annotations.cluster\.x-k8s\.io/machine}'
}
# Wait for rancher-webhook ready. It's default to one replica.
# Otherwise lebeling capi resources later might fail.
$KUBECTL rollout status --watch=true deployment rancher-webhook -n cattle-system
# <https://github.com/rancher/webhook/blob/436e359b136b633cb1a6fa7cdedbed4d74821bdb/pkg/server/server.go#L114>
sleep 20
CUSTOM_MACHINE=$(get_machine_from_node)
until [ -n "$CUSTOM_MACHINE" ]
do
echo Waiting for custom machine label of $HARVESTER_PROMOTE_NODE_NAME ...
sleep 2
CUSTOM_MACHINE=$(get_machine_from_node)
done
until $KUBECTL get <http://machines.cluster.x-k8s.io|machines.cluster.x-k8s.io> $CUSTOM_MACHINE -n fleet-local &> /dev/null
do
echo Waiting for custom machine $CUSTOM_MACHINE...
sleep 2
done
PLAN_SECRET="${CUSTOM_MACHINE}-machine-plan"
until $KUBECTL get secret $PLAN_SECRET -n fleet-local &> /dev/null
do
echo Waiting for machine plan of $CUSTOM_MACHINE...
sleep 2
done
until $KUBECTL get <http://rkebootstraps.rke.cattle.io|rkebootstraps.rke.cattle.io> "${CUSTOM_MACHINE}" -n fleet-local &> /dev/null
do
echo Waiting for bootstrap object of $CUSTOM_MACHINE...
sleep 2
done
VIP=$($KUBECTL get configmap vip -n harvester-system -o=jsonpath='{.data.ip}')
cat > /host/etc/rancher/rke2/config.yaml.d/90-harvester-server.yaml <<EOF
cni: multus,canal
cluster-cidr: 10.52.0.0/16
service-cidr: 10.53.0.0/16
cluster-dns: 10.53.0.10
tls-san:
- $VIP
audit-policy-file: /etc/rancher/rke2/config.yaml.d/92-harvester-kube-audit-policy.yaml
EOF
# Disable snapshot-controller related charts because we manage them in Harvester.
# RKE2 enables these charts by default after v1.25.7 (<https://github.com/rancher/rke2/releases/tag/v1.25.7%2Brke2r1>)
cat > /host/etc/rancher/rke2/config.yaml.d/40-disable-charts.yaml <<EOF
disable:
- rke2-snapshot-controller
- rke2-snapshot-controller-crd
- rke2-snapshot-validation-webhook
EOF
# make sure we should not have any related label/taint on the node
if [[ $ETCD_ONLY == false ]]; then
found=$($KUBECTL get node $HOSTNAME -o yaml | $YQ '.spec.taints[] | select (.effect == "NoSchedule" and .key == "<http://node-role.kubernetes.io/etcd=true|node-role.kubernetes.io/etcd=true>") | .effect')
if [[ -n $found ]]
then
$KUBECTL taint nodes $HOSTNAME <http://node-role.kubernetes.io/etcd=true:NoExecute-|node-role.kubernetes.io/etcd=true:NoExecute->
fi
$KUBECTL label --overwrite nodes $HOSTNAME <http://node-role.harvesterhci.io/witness-|node-role.harvesterhci.io/witness->
fi
# For how to promote nodes, see: <https://github.com/rancher/rancher/issues/36480#issuecomment-1039253499>
$KUBECTL label --overwrite -n fleet-local <http://machines.cluster.x-k8s.io|machines.cluster.x-k8s.io> $CUSTOM_MACHINE $ROLE_LABELS
$KUBECTL label --overwrite -n fleet-local <http://machines.cluster.x-k8s.io|machines.cluster.x-k8s.io> $CUSTOM_MACHINE <http://cluster.x-k8s.io/control-plane=true|cluster.x-k8s.io/control-plane=true>
$KUBECTL label --overwrite -n fleet-local secret $PLAN_SECRET $ROLE_LABELS
$KUBECTL label --overwrite -n fleet-local <http://rkebootstraps.rke.cattle.io|rkebootstraps.rke.cattle.io> $CUSTOM_MACHINE $ROLE_LABELS
kickout_longhorn_node()
{
target=$1
found=$($KUBECTL get <http://nodes.longhorn.io|nodes.longhorn.io> -n $LONGHORN_NAMESPACE |grep -q $target && echo true || echo false)
if [[ $found == true ]]; then
echo "Found longhorn node $target, kicking it out..."
$KUBECTL delete <http://nodes.longhorn.io|nodes.longhorn.io> $target -n $LONGHORN_NAMESPACE
fi
}
while true
do
if [[ $ETCD_ONLY == true ]]; then
ETCD_STATE=$($KUBECTL get node $HOSTNAME -o go-template=$'{{index .metadata.labels "<http://node-role.kubernetes.io/etcd|node-role.kubernetes.io/etcd>"}}\n' || true)
if [ "$ETCD_STATE" = "true" ]; then
$KUBECTL taint nodes $HOSTNAME <http://node-role.kubernetes.io/etcd=true:NoExecute|node-role.kubernetes.io/etcd=true:NoExecute> --overwrite
$KUBECTL patch managedchart harvester -n fleet-local --type=json -p='[{"op":"replace", "path":"/spec/values/replicas", "value": 2}]'
$KUBECTL patch managedchart harvester -n fleet-local --type=json -p='[{"op":"replace", "path":"/spec/values/webhook/replicas", "value": 2}]'
$KUBECTL annotate --overwrite deployment rancher -n cattle-system <http://management.cattle.io/scale-available=%222%22|management.cattle.io/scale-available="2">
kickout_longhorn_node $HOSTNAME
break
fi
else
CONTROL_PLANE=$($KUBECTL get node $HOSTNAME -o go-template=$'{{index .metadata.labels "<http://node-role.kubernetes.io/control-plane|node-role.kubernetes.io/control-plane>"}}\n' || true)
if [ "$CONTROL_PLANE" = "true" ]; then
break
fi
fi
echo Waiting for promotion...
sleep 2
donebland-article-62755
04/24/2026, 3:56 PMMACHINE_ID=$(kubectl get node gpu1 -o jsonpath='{.metadata.annotations.cluster\.x-k8s\.io/machine}') ; echo $MACHINE_ID
• There's some files you need to create on that node. Copy over them from an existing good node: /etc/rancher/rke2/config.yaml.d/90-harvester-server.yaml and /etc/rancher/rke2/config.yaml.d/40-disable-charts.yaml
• Label the Machine object kubectl label --overwrite -n fleet-local <http://machines.cluster.x-k8s.io|machines.cluster.x-k8s.io> $MACHINE_ID <http://rke.cattle.io/control-plane-role=true|rke.cattle.io/control-plane-role=true> <http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true> <http://cluster.x-k8s.io/control-plane=true|cluster.x-k8s.io/control-plane=true>
• Label the Machine Plan Secret kubectl label --overwrite -n fleet-local secret ${MACHINE_ID}-machine-plan <http://rke.cattle.io/control-plane-role=true|rke.cattle.io/control-plane-role=true> <http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>
• Label the RKE Bootstrap object kubectl label --overwrite -n fleet-local <http://rkebootstraps.rke.cattle.io|rkebootstraps.rke.cattle.io> $MACHINE_ID <http://rke.cattle.io/control-plane-role=true|rke.cattle.io/control-plane-role=true> <http://rke.cattle.io/etcd-role=true|rke.cattle.io/etcd-role=true>
• Wait a few minutes and then add the master role to the node and verify they all look the same: kubectl label node gpu1 <http://node-role.kubernetes.io/master=true|node-role.kubernetes.io/master=true> ; kubectl get nodes
• profit.