k3s-io/k3s · warning

etcd datastore disabled

Error message

etcd datastore disabled

What it means

When the server runs a non-etcd datastore (sqlite/postgres/mysql via --datastore-endpoint, or etcd disabled), the /db/ mux is wrapped by handlerNoEtcd: every request under /db/ answers 400 with 'etcd datastore disabled'. The etcd-specific database API simply does not exist for this datastore backend.

Source

Thrown at pkg/cluster/managed.go:151

// deleteNodePasswdSecret wipes out the node password secret after restoration
func (c *Cluster) deleteNodePasswdSecret(ctx context.Context) {
	nodeName := os.Getenv("NODE_NAME")
	if err := nodepassword.Delete(nodeName); err != nil {
		if apierrors.IsNotFound(err) {
			logrus.Debugf("Node password secret is not found for node %s", nodeName)
			return
		}
		logrus.Warnf("failed to delete old node password secret: %v", err)
	}
}

// handlerNoEtcd wraps a handler with an error message indicating that etcd is not deployed.
func handlerNoEtcd(handler http.Handler) http.Handler {
	r := mux.NewRouter()

	// Wildcard route for anything after /db/
	r.HandleFunc("/db/", func(resp http.ResponseWriter, r *http.Request) {
		util.SendError(errors.New("etcd datastore disabled"), resp, r, http.StatusBadRequest)
	})

	// Needs to come at the end, otherwise wildcard routes won't work
	r.NotFoundHandler = handler

	return r
}

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Use datastore-appropriate tooling: SQL snapshots are handled by the local/k3s etcd-snapshot commands or database-native backups, not /db/snapshot.
  2. Join additional servers by pointing them at the same --datastore-endpoint, not via the etcd /db join path.
  3. Update scripts/agents to detect datastore type before calling /db endpoints and skip on non-etcd clusters.
Defensive patterns

Strategy: validation

Validate before calling

// Only call /db endpoints when the datastore is etcd:
func isEtcdDatastore(cfg Endpoint) bool {
	return strings.HasPrefix(cfg.DatastoreEndpoint, "etcd") || cfg.DatastoreEndpoint == "" && !cfg.DisableEmbeddedEtcd
}
if isEtcdDatastore(cfg) { callDBInfo() } else { useSQLBackupTooling() }

Try / catch

resp, err := http.Get(base + "/db/snapshot")
if err == nil && resp.StatusCode == http.StatusBadRequest {
	body, _ := io.ReadAll(resp.Body)
	if strings.Contains(string(body), "etcd datastore disabled") {
		// switch to datastore-native backup path instead of /db API
	}
}

Prevention

When it happens

Trigger: Calling etcd-only endpoints (/db/info, /db/snapshot, /db/check, /db/connect) on a server started with a SQL datastore or with the managed etcd disabled; an agent or script hardcoding the etcd join flow against such a server.

Common situations: Migrating a cluster to sqlite/PostgreSQL and reusing old automation that hits /db routes; mixed servers where a joining node assumes embedded etcd; monitoring scraping etcd metrics endpoints.

Related errors


AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15). Data as JSON: /api/errors/424c73002f721381. Report an issue: GitHub.