k3s-io/k3s · info

apiserver disabled

Error message

apiserver disabled

What it means

ErrAPIDisabled (pkg/util/apierrors.go) is returned as HTTP 503 by handlers.APIServer when the server was started with --disable-api-server (cfg.DisableAPIServer). Such supervisors run agent/dispatcher functions but intentionally host no apiserver, so any request to the proxied /k8s routes is refused permanently on that node.

Source

Thrown at pkg/util/apierrors.go:19

package util

import (
	"crypto/rand"
	"errors"
	"fmt"
	"math/big"
	"net/http"

	"github.com/k3s-io/api/pkg/generated/clientset/versioned/scheme"
	"github.com/sirupsen/logrus"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"k8s.io/apiserver/pkg/endpoints/handlers/responsewriters"
)

var ErrAPINotReady = errors.New("apiserver not ready")
var ErrAPIDisabled = errors.New("apiserver disabled")
var ErrCoreNotReady = errors.New("runtime core not ready")

// SendErrorWithID sends and logs a random error ID so that logs can be correlated
// between the REST API (which does not provide any detailed error output, to avoid
// information disclosure) and the server logs.
func SendErrorWithID(err error, component string, resp http.ResponseWriter, req *http.Request, status ...int) {
	errID, _ := rand.Int(rand.Reader, big.NewInt(99999))
	logrus.Errorf("%s error ID %05d: %v", component, errID, err)
	SendError(fmt.Errorf("%s error ID %05d", component, errID), resp, req, status...)
}

// SendError sends a properly formatted error response
func SendError(err error, resp http.ResponseWriter, req *http.Request, status ...int) {
	var code int
	if len(status) == 1 {
		code = status[0]
	}
	if code == 0 || code == http.StatusOK {

View on GitHub (pinned to 6ba341e396)

Solutions

  1. Point clients at a server that runs the apiserver (drop --disable-api-server nodes from client endpoints).
  2. Use the load-balanced endpoint/VIP that only includes apiserver-enabled servers.
  3. If the node should serve the API, restart it without --disable-api-server.
  4. Treat 503 'apiserver disabled' as a routing signal: remove the node from the pool, do not retry against it.

Example fix

# before: client pinned to a non-apiserver supervisor
curl --cacert ... https://supervisor-noapi:6443/k8s/version   # 503

# after: target apiserver-enabled endpoint (LB)
curl --cacert ... https://k3s-api.lb.internal:6443/k8s/version
Defensive patterns

Strategy: fallback

Validate before calling

# Before pointing a client at a supervisor, confirm it hosts the apiserver
systemctl show k3s -p ExecStart | grep -q -- --disable-api-server && echo 'no apiserver here; pick another endpoint'

Try / catch

if errors.Is(err, util.ErrAPIDisabled) {
    // permanent for this node: fail over to an apiserver-enabled server; do not retry in place
}

Prevention

When it happens

Trigger: A kubeconfig or --server URL pointing at a k3s node started with --disable-api-server; probes aimed at a dedicated non-apiserver supervisor in a split control/worker layout.

Common situations: HA clusters where api-server duties are split across nodes; clients hardcoded to a node IP instead of the LB/VIP; health checks not updated after a node's role change.

Related errors


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