Utility tool for upgrading talos nodes.

Files
README.md

talos-upgrade #

A CLI for managing Talos Linux cluster upgrades with profile-based configuration. It discovers cluster nodes, picks the right installer image (extensions, kernel args, overlays) per hardware profile via the Image Factory, and upgrades nodes one at a time — draining Kubernetes, staging the installer image, running the upgrade, and waiting for the node and its services to come back healthy before moving on.

Talos 1.13+ only. Upgrades use the LifecycleService.Upgrade gRPC API, which requires the node to run Talos >= 1.13.0. Older nodes are rejected.

Install #

Requires Go 1.26+.

make build          # -> bin/talos-upgrade
make install        # -> $GOPATH/bin/talos-upgrade

Both targets stamp the version via git describe --tags --always --dirty. A plain go install ./cmd/talos-upgrade also works — the git commit is embedded automatically through Go's build info.

Check what you have installed:

talos-upgrade --version     # or: talos-upgrade version
talos-upgrade 2429132-dirty
  commit:    2429132fc5dc-dirty
  committed: 2026-05-17T19:43:07Z
  go:        go1.26.4

If the commit: matches your repo HEAD and has no -dirty suffix, the binary is built from current source:

git rev-parse --short=12 HEAD

Prerequisites #

  • A valid talosconfig (default discovery, same as talosctl) — used to talk to nodes and discover cluster members.
  • A kubeconfig for the cluster — used to cordon/drain nodes during upgrade. If unavailable, drain is skipped (with a warning).

Configuration #

The tool looks for a config file in this order (unless -c/--config is given):

  1. ./configs/talos-profiles.yaml
  2. ./talos-profiles.yaml
  3. the same paths relative to the binary's directory

Note: the canonical config for this homelab lives at /home/data/homelab/talos-profiles.yaml. The copy bundled in configs/ can drift; pass -c /home/data/homelab/talos-profiles.yaml if in doubt.

Schema #

settings:
  factory_base_url: "https://factory.talos.dev"
  default_timeout_seconds: 600
  github_releases_url: "https://api.github.com/repos/siderolabs/talos/releases/latest"
  backup_dir: "backups"   # where machine configs are backed up around upgrades (default: "backups")

# Hardware profiles: each becomes an Image Factory schematic.
profiles:
  amd64:
    description: "Intel GPU nodes with secureboot"
    arch: amd64           # required
    platform: metal       # required
    secureboot: true
    kernel_args:
      - pci=realloc
    extensions:
      - siderolabs/i915
      - siderolabs/iscsi-tools
  arm64-rpi:
    arch: arm64
    platform: metal
    overlay:               # SBC overlay (optional)
      name: rpi_generic
      image: siderolabs/sbc-raspberrypi
    extensions:
      - siderolabs/iscsi-tools

# Map discovered hardware -> profile. Nodes are discovered via the Talos
# cluster members API; their profile is detected from hardware info.
detection:
  rules:
    - profile: arm64-rpi
      match:                       # AND: all listed conditions must match
        system_manufacturer: raspberrypi   # case-insensitive substring
        arch: arm64                         # case-insensitive exact
    - profile: amd64
      match:
        arch: amd64

Detection matching:

  • match — AND, every condition must match.
  • match_either — OR, any one match block matches.
  • system_manufacturer / processor_manufacturer are case-insensitive substring matches; arch is a case-insensitive exact match.

Instead of detection, you may list static nodes (ip / profile / role), but detection is preferred.

Usage #

talos-upgrade [command] [flags]

Global flags #

Flag Env Description
-c, --config <path> Config file path
--dry-run DRY_RUN=true Show what would happen; make no changes
--force Re-apply the upgrade even if the node already matches the target version/config
-V, --talos-version <ver> TALOS_VERSION Target Talos version (default: latest from GitHub)
--version Print version/build info and exit

When no version is supplied, the latest stable Talos release is fetched from github_releases_url.

Commands #

Command Description
status Show current cluster status and node versions
urls [version] Print Image Factory browser URLs per profile
images [version] Print installer image references per profile
upgrade [target] [version] Upgrade nodes (workers first, then control planes)
upgrade-node <ip> [version] Upgrade a single node by IP
version Print version and build information

upgrade targets: all (default), workers, controlplanes, a profile name, or a node IP. Arguments are order-insensitive — upgrade workers 1.13.4 and upgrade 1.13.4 workers are equivalent.

Examples #

# See current versions across the cluster
talos-upgrade status

# Preview the installer images that would be used for the latest release
talos-upgrade images

# Dry-run an upgrade of all nodes to the latest stable release
talos-upgrade --dry-run upgrade all

# Upgrade only workers to a specific version
talos-upgrade upgrade workers 1.13.4

# Upgrade a single node
talos-upgrade upgrade-node 192.168.1.50 1.13.4

How an upgrade works #

For each node, in order (workers before control planes):

  1. Skip if already at the target version with matching extensions and kernel args.
  2. Back up the node's machine config to backup_dir (<ip>-<timestamp>-pre-upgrade.yaml).
  3. Cordon and drain the Kubernetes node (DaemonSet and mirror pods are left in place; PDB-throttled evictions are retried until the timeout).
  4. Stage the installer image into the node's system containerd namespace via ImageService.Pull — LifecycleService.Upgrade does not pull it itself.
  5. Run LifecycleService.Upgrade, stream install progress, and reboot.
  6. Wait for the node to return to RUNNING, then for Talos services (etcd/kubelet/apid/trustd on control planes), and for control-plane static pods (kube-apiserver, kube-controller-manager, kube-scheduler).
  7. Back up the machine config again (<ip>-<timestamp>-post-upgrade.yaml).
  8. Uncordon the node (always attempted, even on failure).

A failed control-plane upgrade prompts before continuing to the next node.

Machine config backups #

Before and after each node upgrade the tool snapshots the node's active machine config to backup_dir (default backups/, git-ignored):

  • <ip>-<timestamp>-pre-upgrade.yaml — the node exactly as it is now, captured before draining, unmodified.
  • <ip>-<timestamp>-post-upgrade.yaml — the node's new state, with machine.install.image patched to the target installer image so it doubles as a self-sufficient manifest for a from-scratch reinstall at the intended version (talosctl apply-config --insecure --file <backup>).

Backups contain machine secrets and are written owner-only (0600); keep them somewhere safe.

Development #

make test           # go test ./...
make tidy           # go mod tidy
make build-all      # cross-compile linux/darwin amd64+arm64

The repository is hosted at https://tangled.org/evan.jarrett.net/talos-upgrade (not GitHub).