TL;DR Talos Linux is a Linux that runs Kubernetes and nothing else. No SSH, no shell, no package manager. Every change goes through an API and a YAML file in Git. I’ve run my nodes on it for three years, and this post is how I stopped missing the shell.
The first time I tried to SSH into a Talos node, I got nothing. No shell, no connection, not even a login prompt. I sat there for a minute wondering how I was supposed to debug a machine I couldn’t get into.
That was three years ago. I haven’t run Kubernetes on anything else since.
Before Talos, my nodes looked the way most Kubernetes nodes look. A general-purpose distro, hardened with a CIS benchmark script, an SSH key added, a config management tool to keep drift in check, and then two years of SSH-ing in to fix the things the tool didn’t catch. Every node had its own history, and only some of it was written down.
What Talos leaves out
Talos Linux is a Linux distribution built to run Kubernetes and nothing else. “Distribution” is generous, because Talos throws away most of what makes a Linux box feel like one. There’s no SSH, so you can’t shell into a node. There’s no package manager, so no apt-get, no yum, nothing to install. There’s no shell at all, no bash, no sh. The root filesystem is read-only. Everything you’d do to the machine goes through a gRPC API.
My first reaction was “that’s insane, how do you get anything done”. Yours probably is too. The answer took me a while, and it’s the rest of this post.
The fix that lived on one disk
Years before Talos, production went down in the middle of the night. I SSH’d into the bad node, did some panicked debugging, found the problem, and fixed it by hand-editing a config file and bouncing a service. Back to bed.
That fix lived in exactly one place, on that node’s disk. It wasn’t in Git and it wasn’t documented anywhere. Months later we replaced the node and the problem came straight back, because nobody remembered the hand edit that had been keeping it alive.
That’s the cost of a mutable node, and it compounds. Every SSH session is a chance for drift. Every quick fix is a debt you pay later, usually during the next incident, and without the context you had the first time.
Now picture a node you can’t drift by hand, because there’s no way in. That’s what Talos is. You can’t SSH in and tinker. Every change goes through the API, so every change can be tracked, versioned and reproduced. My hand edit would have been a one-line diff in Git, reviewed, and the replacement node would have had it from day one.
When I explain this, the pushback comes fast. What about debugging? What about emergencies?
Debugging without a shell
Fair. Talos ships talosctl, a CLI that talks to the Talos API, and it covers the things you’d normally open a shell for:
# Get system logs
talosctl logs kubelet
# Stream kernel messages
talosctl dmesg -f
# List running processes
talosctl processes
# Get node health
talosctl health
# Read any file from the node
talosctl read /etc/hosts
# Get full machine configuration
talosctl get machineconfig
Everything you’d normally do over SSH is here, with one difference that matters. The API has access control and audit logging, and it can be scoped to specific operations. SSH is an all-or-nothing root shell, which is exactly what an attacker wants as well.
For the moments where you want to sit and watch a machine, there’s a dashboard:
talosctl dashboard
That’s a read-only TUI with system stats, logs and node status. For real emergencies there’s a debug container, a privileged container with full host access. It’s awkward to use, and that’s deliberate, so you don’t reach for it out of habit.
One YAML file per node
A Talos node is configured with a single YAML machine config. A trimmed-down example:
machine:
type: controlplane
token: <bootstrap-token>
ca:
crt: <ca-certificate>
key: <ca-key>
network:
hostname: node-01
interfaces:
- interface: eth0
dhcp: true
install:
disk: /dev/sda
image: ghcr.io/siderolabs/installer:v1.6.0
cluster:
clusterName: homelab
controlPlane:
endpoint: https://192.168.1.100:6443
That file describes everything about the node. Network settings, disk layout, Kubernetes version, kernel arguments, system extensions, CNI configuration.
You apply it with one command:
talosctl apply-config --nodes 192.168.1.10 --file controlplane.yaml
Talos doesn’t so much configure the node as make the node converge to the file. Change the file, apply again, and the node moves to the new state. It’s the same declarative model Kubernetes uses for pods, one layer further down.
GitOps one layer down
Compare that with where OS configuration usually lives. Ansible playbooks that have drifted from what’s on the box. A config management tool nobody fully trusts. A layer of tribal knowledge built up in SSH sessions that left no trace. The source of truth is “ask the person who set it up”, and that person leaves eventually.
Because a Talos node is one YAML file, it drops straight into the same GitOps workflow as everything else. My repo looks like this:
infrastructure/
├── talos/
│ ├── controlplane-1.yaml
│ ├── controlplane-2.yaml
│ ├── controlplane-3.yaml
│ ├── worker-1.yaml
│ └── worker-2.yaml
├── kubernetes/
│ └── ... (manifests managed by ArgoCD)
└── ...
When I need to change something on a node, say a kernel argument or a network setting, I edit the YAML, commit, and apply. The commit is the documentation. It’s reviewable in a PR and reproducible on the next node I build. That’s the whole reason I do it this way.
What an attacker finds on the box
Take away the shell and the package manager and a whole class of attacks stops working. No package manager, so no curl | bash installing malware. No SSH, so nothing to brute-force. A read-only filesystem, so no swapping out system binaries. A minimal userspace, so there’s less to attack in the first place.
The node runs only what Kubernetes needs: containerd , kubelet, and a few supporting services. No cron, no systemd-resolved, none of the background daemons a general-purpose distro ships because you might want them someday.
So say an attacker pops a container and escapes to the host. Where do they land? A read-only filesystem with no shell, no package manager, and nowhere to persist. It lines up with zero trust principles, in that the node doesn’t even trust itself to stay configured correctly and keeps enforcing the declared state.
Upgrades with a rollback built in
Upgrades are the part I like most:
talosctl upgrade --nodes 192.168.1.10 \
--image ghcr.io/siderolabs/installer:v1.7.0
Talos pulls the new OS image, writes it to a second partition, and reboots into it. If the new version fails to come up, the node rolls back to the previous one on its own.
That’s a transactional upgrade. No half-applied state, no Saturday spent on a kernel that panicked halfway through the upgrade. It completes, or it rolls back whole.
For Kubernetes version bumps you update the machine config with the new version and apply it. Talos does the draining and cordoning if you use the Talos-aware upgrade path.
When not to use Talos
This doesn’t fit everyone. Stick with a traditional distro if:
- you need specific packages that aren’t available as Talos extensions
- you’re still learning Linux and the abstraction hides too much of it
- your team isn’t ready for API-driven management
- you run non-Kubernetes workloads on the same nodes
Talos does Kubernetes and nothing else. If that’s not your main use case, you’ll spend your time fighting the design instead of using it.
Debugging also has a steeper curve up front. When something breaks you can’t vim /etc/whatever your way out. You have to learn the Talos tools and concepts first. Long term that’s a benefit, because it forces better habits, but it’s a real cost during adoption and I’d rather say so.
Trying it in Docker
The fastest way to try Talos is with Docker:
# Create a local cluster
talosctl cluster create --name demo
# Get kubeconfig
talosctl kubeconfig --nodes 127.0.0.1
# You're now running Talos locally
kubectl get nodes
For something closer to real, start with a small homelab cluster. My homelab runs on refurbished mini-PCs, which is a cheap way to experiment with Talos before you bet production on it. The Talos documentation is good, with guides for bare metal, AWS, GCP, Azure, VMware and more.
What the toolchain assumes
So why doesn’t everyone run nodes this way? Because the whole industry assumes the opposite. Traditional infrastructure management is built around the idea that at some point you’ll need to get onto the machine and fix it by hand. SSH keys, bastion hosts, jump boxes, console access. The entire toolchain exists to give you a way in.
Talos removes the way in. The bet is that you never need one, because the node describes itself and converges on its own. If something’s wrong, you fix the file and reapply. That is rigid, and the rigidity is what stops the drift.
In practice it’s less dramatic than it sounds. Every incident I’ve had on Talos went the same way. Read the logs through the API, spot the misconfiguration, fix the file, apply. The fix is a commit, so it documents itself. Nobody has to ask afterwards whether someone SSH’d in and changed something, because nobody can.
Three years in, I don’t miss SSH. The API gives me everything a shell gave me, plus access control, an audit trail, and a node I can rebuild from a file. Talos has a strong opinion about how a node should be run. Mine turned out to match it.
