k3s-io/k3s · critical
token is required to join a cluster
Error message
token is required to join a cluster
What it means
During bootstrap, when a managed database (sqlite or embedded etcd) exists but is not initialized and a JoinURL is configured, k3s requires a token to pull bootstrap data from the remote server. Control-plane nodes take this path on every startup, so an empty token is fatal: the node cannot anonymously join a cluster.
Source
Thrown at pkg/cluster/bootstrap.go:124
// cluster they need to delete the database.
logrus.Infof("Managed %s cluster bootstrap already complete and initialized", c.managedDB.EndpointName())
// This is a workaround for an issue that can be caused by terminating the cluster bootstrap before
// etcd is promoted from learner. Odds are we won't need this info, and we don't want to fail startup
// due to failure to retrieve it as this will break cold cluster restart, so we ignore any errors.
if c.config.JoinURL != "" && c.config.Token != "" {
c.clientAccessInfo, _ = clientaccess.ParseAndValidateToken(c.config.JoinURL, c.config.Token, opts...)
}
return false, true, nil
} else if c.config.JoinURL == "" {
// Not initialized, not joining - must be initializing (cluster-init)
logrus.Infof("Managed %s cluster initializing", c.managedDB.EndpointName())
return false, false, nil
}
// Not initialized, but have a Join URL - fail if there's no token; if there is then validate it.
// Note that this is the path taken by control-plane-only nodes every startup, as they have a non-nil managedDB that is never initialized.
if c.config.Token == "" {
return false, false, errors.New("token is required to join a cluster")
}
// Fail if the token isn't syntactically valid, or if the CA hash on the remote server doesn't match
// the hash in the token. The password isn't actually checked until later when actually bootstrapping.
info, err := clientaccess.ParseAndValidateToken(c.config.JoinURL, c.config.Token, opts...)
if err != nil {
return false, false, errors.WithMessage(err, "failed to validate token")
}
c.clientAccessInfo = info
if c.config.DisableETCD {
logrus.Infof("Managed %s disabled on this node", c.managedDB.EndpointName())
} else {
logrus.Infof("Managed %s cluster not yet initialized", c.managedDB.EndpointName())
}
}
// No errors and no bootstrap stamp, need to bootstrap.View on GitHub (pinned to 6ba341e396)
Solutions
- Pass the shared cluster token via --token or K3S_TOKEN (same value as the seed server's /var/lib/rancher/k3s/server/token or a `k3s token create` output)
- Verify the token actually reaches the process: `systemctl show k3s -p Environment` and the unit drop-ins
- If this node was meant to seed a new cluster, remove --server so it initializes instead of joins
Example fix
# before k3s server --server https://10.0.0.10:6443 # after K3S_TOKEN=<cluster-token> k3s server --server https://10.0.0.10:6443
Defensive patterns
Strategy: validation
Validate before calling
// fail fast before cluster bootstrap
if c.config.JoinURL != "" && strings.TrimSpace(c.config.Token) == "" && !c.storageReady() {
return errors.New("token is required to join a cluster: set K3S_TOKEN/--token")
} Type guard
func hasJoinCredentials(joinURL, token string) bool {
return joinURL == "" || strings.TrimSpace(token) != ""
} Try / catch
if err != nil {
if strings.Contains(err.Error(), "token is required to join a cluster") {
// surface an actionable message and stop retrying until the secret is populated
return fmt.Errorf("K3S_TOKEN missing; populate the token secret and restart")
}
return err
} Prevention
- Provisioning for HA control-plane nodes must include the shared cluster token, not just agent tokens
- Add init-container/readiness checks that block start until the token secret is non-empty
- Centralize token distribution (secret store) so joins never depend on manual copy
When it happens
Trigger: `k3s server --server https://10.0.0.10:6443` with no --token/K3S_TOKEN and no token in local storage; adding a second (HA) server to a cluster without passing the shared cluster token.
Common situations: New control-plane nodes in embedded-etcd HA clusters where the operator assumes only agents need the token; K3S_TOKEN empty in the systemd unit; token secret not yet populated when the server pod starts.
Related errors
- --token is required
- no bootstrap data found in datastore - check server token va
- found multiple bootstrap keys in storage
- bootstrap data already found and encrypted with different to
- invalid flag use; --server is required with --disable-etcd
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/e2459befe9294b92.
Report an issue: GitHub.