Kubernetes Error Guide: 'You must be logged in to the server (Unauthorized)' 401 Authentication Failures
Fix 'Unauthorized' (HTTP 401) errors in Kubernetes: expired client certs, stale tokens, broken EKS/GKE exec credentials, ServiceAccount token issues, and clock skew.
- #kubernetes
- #troubleshooting
- #errors
- #auth
Exact Error Message
When the Kubernetes API server cannot establish who you are, it returns HTTP 401 Unauthorized and kubectl surfaces it like this:
error: You must be logged in to the server (Unauthorized)
Or, when the failure happens before any request body is processed:
Unable to connect to the server: the server has asked for the client to provide credentials (Unauthorized)
You may also see it inline from any verb:
$ kubectl get pods
error: You must be logged in to the server (Unauthorized)
This is a 401 — an authentication failure. It is fundamentally different from a 403, which is an authorization (RBAC) failure. A 401 means the API server could not verify your identity at all; a 403 means it knows who you are but you lack permission. If your error instead reads User "..." cannot list resource ..., that is a 403 — see the RBAC “cannot list resource” guide.
What the Error Means
Every request to the Kubernetes API server passes through an authentication stage before anything else. The server tries each configured authenticator — client certificate, bearer token, OIDC, webhook, or service account token — against the credentials your client presented. If none of them produce a valid authenticated user, the request is rejected with 401 Unauthorized.
Critically, the 401 happens before RBAC. The API server never even looks at what you were trying to do, because it does not know who is asking. This is why a 401 affects every command equally (get, apply, delete all fail identically), whereas a 403 is usually scoped to specific resources or verbs.
Anonymous requests are normally rejected too: most clusters run with --anonymous-auth=true but bind the system:anonymous user to nothing, so an unauthenticated request that could fall through to anonymous still gets denied at the RBAC stage as system:anonymous. When you see a clean 401, it means your presented credential was actively invalid, not merely absent.
Common Causes
- Expired or invalid client certificate. The
client-certificate-datain your kubeconfig has passed itsnotAfterdate, or was signed by a CA the API server no longer trusts. - Expired or wrong bearer token. A static token or OIDC
id_tokenhas expired, been rotated, or belongs to a different cluster. - Broken exec credential plugin. EKS uses
aws eks get-token/aws-iam-authenticator; GKE usesgke-gcloud-auth-plugin. If the plugin is missing, the underlying cloud credentials are expired, or the IAM identity was removed from the cluster, the exec call returns no usable token. - ServiceAccount token not mounted or expired. With
BoundServiceAccountTokenVolume, projected SA tokens are short-lived and audience-bound; a stale token cached in an old client fails. - Clock skew. Tokens (JWT/OIDC) carry
iat/exp/nbfclaims. A node or workstation with a wrong clock makes a valid token look not-yet-valid or already-expired. - Wrong user context. Your
current-contextpoints at auserentry whose credentials are stale or belong to a different cluster.
How to Reproduce the Error
Point a kubeconfig at a valid cluster but with a deliberately expired token:
apiVersion: v1
kind: Config
clusters:
- cluster:
server: https://api.example.k8s.local:6443
certificate-authority-data: LS0tLS1CRUdJ...
name: prod
contexts:
- context:
cluster: prod
user: stale-user
name: prod
current-context: prod
users:
- name: stale-user
user:
token: eyJhbGciOiJSUzI1Ni␣␣␣EXPIRED␣␣␣
kubectl --kubeconfig=/tmp/stale.yaml get pods
error: You must be logged in to the server (Unauthorized)
The cluster and CA are correct, so TLS to the API server succeeds — but the token cannot be validated, producing the 401.
Diagnostic Commands
Confirm which context and user you are actually using:
kubectl config current-context
kubectl config view --minify -o jsonpath='{.contexts[0].context.user}{"\n"}'
See the raw 401 on the wire with verbose logging:
kubectl get pods --v=8 2>&1 | grep -iE 'Response Status|Unauthorized'
I0625 10:14:22.118 round_trippers.go:553] GET https://api.example.k8s.local:6443/api/v1/namespaces/default/pods 401 Unauthorized in 38 milliseconds
Check a client certificate’s expiry:
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' \
| base64 -d | openssl x509 -noout -enddate -subject
notAfter=Apr 12 09:30:00 2026 GMT
subject=CN=admin, O=system:masters
Inspect a bearer/OIDC token’s expiry (decode the JWT payload):
TOKEN=$(kubectl config view --raw -o jsonpath='{.users[0].user.token}')
echo "$TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | jq '{exp, iat, aud, sub}'
{ "exp": 1750000000, "iat": 1749996400, "aud": ["https://kubernetes.default.svc"], "sub": "system:serviceaccount:prod:deployer" }
Test an exec credential plugin directly:
# EKS
aws eks get-token --cluster-name prod --region us-east-1 | jq '.status.token' | head -c 20
# GKE
gke-gcloud-auth-plugin --version
Step-by-Step Resolution
Client certificate expired
Issue a new client cert via a CertificateSigningRequest, or for managed clusters re-fetch admin credentials:
# Self-managed: confirm expiry, then rotate the kubeadm-issued cert
kubeadm certs check-expiration
kubeadm certs renew admin.conf
cp /etc/kubernetes/admin.conf ~/.kube/config
EKS exec credential broken
Refresh the underlying AWS credentials and regenerate the kubeconfig entry:
aws sts get-caller-identity # confirm AWS creds are valid first
aws eks update-kubeconfig --name prod --region us-east-1
kubectl get nodes
If get-caller-identity itself fails, the AWS credential (SSO session, role) is expired — re-run aws sso login. If AWS works but the cluster still returns 401, your IAM principal may have been removed from the aws-auth ConfigMap / EKS access entries.
GKE exec credential broken
gcloud auth login
gcloud container clusters get-credentials prod --region us-central1
# ensure the plugin is installed
gcloud components install gke-gcloud-auth-plugin
ServiceAccount token (in-cluster)
For workloads, do not use long-lived secrets. Rely on the projected token volume and let the kubelet rotate it:
volumes:
- name: kube-api-access
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600
audience: https://kubernetes.default.svc
If you need an out-of-cluster token, mint a fresh bound one:
kubectl create token deployer -n prod --duration=1h
Wrong context or clock skew
kubectl config use-context prod # switch to the correct context
sudo chronyc makestep # force NTP resync if clock is off
timedatectl # verify "System clock synchronized: yes"
Prevention and Best Practices
- Prefer short-lived, automatically-refreshed credentials (exec plugins, projected SA tokens) over long-lived static tokens pasted into kubeconfig.
- Alert on certificate expiry: run
kubeadm certs check-expiration(or monitor the API server’s serving/client certs) and rotate well beforenotAfter. - Keep NTP/chrony healthy on every node and workstation so token
exp/nbfclaims are evaluated against the correct time. - In EKS/GKE, treat cloud-identity expiry as a first-class failure mode — wire
aws sso login/gcloud auth logininto onboarding docs and CI runners. - Never bake
system:masterscerts into shared automation; scope credentials and rotate them. See more in Kubernetes & Helm guides.
Related Errors
User "..." cannot list resource "..." in API group ""— an RBAC403, not a401. Your identity is valid but lacks permission. See the RBAC cannot-list-resource guide.Unable to connect to the server: x509: certificate signed by unknown authority— a TLS/CA trust problem (wrongcertificate-authority-data), which fails before authentication.error: You must be logged in to the server (the server has asked for the client to provide credentials)— the same401family, typically a missing rather than invalid credential.
Frequently Asked Questions
What is the difference between a 401 and a 403 in Kubernetes?
A 401 Unauthorized is an authentication failure — the API server cannot determine who you are, so the request is rejected before RBAC. A 403 Forbidden is an authorization failure — you are authenticated, but your user or ServiceAccount lacks an RBAC rule for the action. Fix 401 by repairing your credential; fix 403 by adding a Role/RoleBinding.
Why do all my kubectl commands fail with Unauthorized, not just one?
Because authentication happens before the API server inspects the verb or resource. If your credential is invalid, every request fails identically. A 403, by contrast, is usually scoped to specific resources or verbs, so some commands work and others do not.
My AWS credentials are valid but EKS still returns 401 — why?
Valid AWS credentials only prove your IAM identity to aws eks get-token. If that IAM principal is not mapped in the cluster’s aws-auth ConfigMap or EKS access entries, the resulting token authenticates to no Kubernetes user and you get a 401. Add your principal to the access configuration.
How do I see the actual 401 response?
Run any command with --v=8 and grep for the status line: kubectl get pods --v=8 2>&1 | grep -i Unauthorized. The verbose output shows the exact API path and the 401 Unauthorized HTTP response, confirming it is authentication and not a network or TLS issue.
Can clock skew really cause Unauthorized errors?
Yes. OIDC and bound ServiceAccount tokens are JWTs with nbf (not-before) and exp (expiry) claims. If your machine’s clock is wrong by more than the allowed leeway, a perfectly good token is judged not-yet-valid or already-expired and the API server returns 401. Sync time with NTP/chrony to resolve it.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.