---
title: Node images and upgrades
description: What a node is made of, where the machine config lives, and how an upgrade reaches four machines without an operator watching.
weight: 10
icons: [talos, kubernetes]
tags: [talos, kubernetes, tuppr]
---

A node here has no SSH daemon, no shell package manager and no configuration files
managed by hand. It boots a squashfs image, mounts the rest read-only, and reads its
entire configuration from one machine config document.

| what          | value                                   |
| ------------- | --------------------------------------- |
| Talos version | v1.14.4                                 |
| Kubernetes    | v1.37.1                                 |
| kernel        | 6.18.5x-talos                           |
| nodes         | 4 (3 control-plane, 1 worker)           |
| upgrade owner | `tuppr` (Talos and Kubernetes upgrades) |

## Where the config lives

The machine config is generated by Terraform from
`terraform/inventory/nodes.yaml` plus the per-node patch files. A patch file is small
and reviewable:

```
machine:
  nodeLabels:
    role: infra
    site: oracle
  kubelet:
    extraArgs:
      max-pods: "110"
```

Node identity, labels and kubelet flags are all patches. There is no second source of
truth: if a label is not in the patch, it does not exist on the node after the next
apply.

## How an upgrade reaches a machine

`tuppr` watches the declared Talos and Kubernetes versions, drains a node, reboots it
into the new image, waits for it to rejoin and moves to the next one. I never run
`talosctl upgrade` by hand, and the same applies in reverse: a node that drifts back
to an older image is upgraded again on the next reconcile.

The reason to pay for this shape is what it removes. There is no package diff to
reason about, no half-upgraded node and no "works on that machine" class of bug,
because two nodes of the same version are the same image with different patches.
