K3s uses tokens to authenticate nodes joining the cluster, but there’s a surprisingly simple way to make them much more secure than just a random string.

Let’s see how a node joins a K3s cluster. Imagine we have a running K3s server, and we want to add a new agent node.

First, on the server node, we need to get the join token. This is usually found in /var/lib/rancher/k3s/server/node-token on the server. If you don’t see it there, the server might not have started correctly, or you might have configured a custom token file.

# On the K3s server node
sudo cat /var/lib/rancher/k3s/server/node-token

This will output a token like K10abcdefg12345::server:xyz987654321.

Now, on the agent node, we need to tell K3s how to join the server using this token. We do this by setting the K3S_URL and K3S_TOKEN environment variables before starting the K3s agent service.

# On the K3s agent node
export K3S_URL="https://<server-ip>:6443"
export K3S_TOKEN="K10abcdefg12345::server:xyz987654321"
curl -sfL https://get.k3s.io | sh -

After running this, the agent node will attempt to connect to the server using the provided URL and token, and if successful, it will join the cluster. You can verify this by checking the nodes on the server:

# On the K3s server node
kubectl get nodes

You should see your new agent node listed with a Ready status.

The problem K3s solves here is straightforward: how do you ensure that only legitimate nodes can join your cluster? Without a secure authentication mechanism, anyone could potentially spin up a malicious node and try to join your cluster, leading to all sorts of security risks. The token acts as a shared secret, proving the node’s identity to the server.

Internally, when an agent node starts, it contacts the K3s server at the specified K3S_URL. It presents the K3S_TOKEN as part of its TLS handshake and subsequent API requests. The server verifies this token against its own stored secret. If they match, the server allows the agent to register and become part of the cluster. This process ensures that only nodes possessing the correct token can participate.

Now, here’s the part most people miss: the K3s join token isn’t just a random string; it’s actually a JWT (JSON Web Token) signed by the K3s server itself. This means the server can cryptographically verify that the token was indeed issued by it and hasn’t been tampered with. The format K10abcdefg12345::server:xyz987654321 is a clue to this. The K10 prefix is specific to K3s and indicates it’s a K3s token. The ::server: part signifies it’s a token for a server to join a cluster (though typically used for agents joining a server).

If you need to rotate these tokens, you can do so by stopping the K3s agent, deleting the /var/lib/rancher/k3s/agent/db/token file on the agent, and then restarting the agent with a new token obtained from the server. The server itself can have its token rotated by stopping it, deleting /var/lib/rancher/k3s/server/node-token, and restarting the server.

The next concept you’ll likely encounter is managing more complex cluster configurations, such as using external datastores or setting up high availability for your K3s server.

Want structured learning?

Take the full K3s course →