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
- Point clients at a server that runs the apiserver (drop --disable-api-server nodes from client endpoints).
- Use the load-balanced endpoint/VIP that only includes apiserver-enabled servers.
- If the node should serve the API, restart it without --disable-api-server.
- 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
- Never hardcode client kubeconfigs to a single supervisor IP; use the LB/VIP.
- Track which nodes run --disable-api-server and keep them out of client endpoint pools.
- Treat 'apiserver disabled' as routing feedback, not an error to retry.
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
- invalid flag use; cannot use --disable-apiserver with --data
- apiserver not ready
- invalid flag use; --server is required with --disable-etcd
- invalid flag use; cannot use --disable-etcd with --datastore
- critical configuration value mismatch between servers
AI-assisted analysis of k3s-io/k3s@6ba341e396 (2026-08-15).
Data as JSON: /api/errors/1c3c57d3387da725.
Report an issue: GitHub.