6 Ways to Fix kubectl "You must be logged in to the server (Unauthorized)"
kubectl get pods was working fine yesterday. Today it suddenly spits this out:
error: You must be logged in to the server (Unauthorized)Palms start sweating. Bottom line first: this is not "you don't have permission (403)" — it is "the cluster could not prove who you are (401)". That is an authentication problem. Before you go digging through RBAC settings, check whether your ID (token or certificate) is still valid.
Earlier posts in this series (K8s_Troubleshooting_Guide) covered workload issues like scheduling, images, and networking. This 12th installment moves into authn/authz. If you landed here by searching the error text, the goal is to pinpoint the cause and recover with copy-paste commands in five minutes.
401 vs 403: the distinction you have to get right first
Half of troubleshooting is just figuring out which neighborhood the problem lives in.
| Error message | HTTP code | Meaning | Cause area | First check |
|---|---|---|---|---|
You must be logged in to the server (Unauthorized) | 401 | Failed to prove who you are (authentication) | authn | Token/cert expiry, context, cloud credentials |
Forbidden ... cannot list resource ... | 403 | We know who you are, but you lack permission (authorization) | authz | RBAC Role/RoleBinding |
Core analogy: 401 is the badge itself expired or lost; 403 is you have a badge but no permission to enter that room. This post focuses on 401. For 403 (RBAC) we only mark the boundary under cause 5.
Also tell apart messages that look similar but have different causes.
| Actual output | Likely cause | First action |
|---|---|---|
You must be logged in to the server (Unauthorized) | Expired token/credentials (on kubeadm, including an expired client certificate), wrong user | (1)–(4) below |
You must be logged in to the server (the server has asked for the client to provide credentials) | Credentials are empty or the exec plugin failed | Check the kubeconfig exec block and AWS_PROFILE |
Unable to connect to the server: x509: certificate has expired or is not yet valid | Usually the API server's serving certificate has expired, or client/server clock skew. An expired client certificate usually shows up as Unauthorized, not this message | Server certificate expiry, kubeadm certs check-expiration, timedatectl |
Error from server (Forbidden): ... cannot ... | Authentication succeeded, authorization (RBAC) failed | (5) |
Unable to connect to the server: dial tcp ... | Endpoint or network problem | Check the cluster endpoint |
Six cause-by-cause diagnoses and fixes (copy-paste commands)
(1) kubeconfig token/certificate expired
The most common culprit. Short-lived (STS) tokens are now the norm, so the "it suddenly broke a few hours later" pattern is frequent.
# : User token / certificate check
kubectl config view --raw --minifyIn the output, look under user: for a token: or an exec: block. If a static token is embedded, it has likely expired. Fix: reissue the token from the issuer and refresh it (if this is a cloud cluster, go to cause 3).
⚠️ --raw prints tokens and certificates in plain text. Mask them before screen-sharing or pasting into issues or chat. To see just the structure, kubectl config view --minify without --raw is enough.
(2) Wrong context/cluster selected
In multi-cluster setups, hitting prod with dev credentials happens more often than you'd think.
#
kubectl config current-context #
kubectl config get-contexts # * when context
#
kubectl config use-context <-context->Confirm the CLUSTER/AUTHINFO columns from get-contexts match what you intended. If it was the wrong context, this alone fixes it.
(3) Cloud IAM token expired (EKS / GKE)
EKS issues short-lived STS tokens via aws-iam-authenticator or aws eks get-token (exec credential plugin). When the AWS session expires, the AWS CLI errors out while issuing the token, or you get a straight 401.
# EKS: kubeconfig
aws eks update-kubeconfig --region <> --name <>
# file
aws eks update-kubeconfig --region <> --name <> --profile <file>
# GKE
gcloud container clusters get-credentials <> --region <>The most common EKS trap: first confirm the AWS credentials themselves are valid with aws sts get-caller-identity. If the AWS session is dead, update-kubeconfig still 401s at token issuance.
aws sts get-caller-identity # if this errors, refresh your AWS credentials firstIf the credentials are valid and you still get 401, check whether that IAM identity is mapped into the cluster. EKS accepts only identities registered in the aws-auth ConfigMap or in EKS access entries. Ask a cluster admin to confirm that the ARN from aws sts get-caller-identity (the role ARN, if you assumed a role) is registered.
# Run from a side that has admin access
aws eks list-access-entries --cluster-name <cluster-name>
kubectl -n kube-system get configmap aws-auth -o yaml(4) client-certificate permissions and expiry
On-prem kubeadm clusters usually use client-certificate auth. Certificates expire after one year by default — easy to forget.
# certificate expiry
openssl x509 -in ~/.kube/client.crt -noout -enddate
# ) notAfter=Jun 16 09:00:00 2026 GMT
# kubeconfig base64
kubectl config view --raw -o jsonpath='{.users[0].user.client-certificate-data}' | base64 -d | openssl x509 -noout -enddateIf notAfter is in the past, the cert is expired. Also check the cert file path and permissions (ls -l; 600 is recommended). On kubeadm, renew with kubeadm certs renew.
If you see x509: certificate has expired or is not yet valid, check the API server certificate and the clock before the client certificate. It means kubectl failed while verifying the certificate the server presented.
# Validity of the certificate the API server presents (kubeadm default port 6443)
echo | openssl s_client -connect <apiserver-host>:6443 2>/dev/null | openssl x509 -noout -dates
# On a control plane node, check and renew all certificates
sudo kubeadm certs check-expiration
sudo kubeadm certs renew all
# Renewed certificates take effect only after the control plane static Pods restart:
# move the manifests out of /etc/kubernetes/manifests/, wait for the Pods to stop, then move them back
# Copy the renewed admin.conf to your kubeconfig
sudo cp /etc/kubernetes/admin.conf $HOME/.kube/configKubernetes docs: Certificate Management with kubeadm
(5) Missing RBAC — you get 403 here, not 401
This is the 401 vs 403 boundary. If authentication succeeded but you lack permission, you should get 403, not 401. So if you are seeing 401, RBAC is usually not the culprit.
# check
kubectl auth can-i list pods
kubectl auth can-i get pods --namespace prodIf the output is no, it is a 403 (authorization) problem. If the message is still 401, you were already blocked at authentication — go back to causes 1–4.
kubectl auth whoami # who the cluster thinks you are (kubectl 1.28+)If whoami shows a different user than you expect, it's an authentication/context problem, not a permissions problem.
(6) System clock skew
JWT/STS tokens have NotBefore/Expiry baked in. If the client clock is wrong, a perfectly valid token fails verification as "not yet valid" or "expired". Surprisingly common on VMs and in containers.
#
timedatectl # System clock synchronized: yes check
# (NTP )
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncdIf you see System clock synchronized: no, or the clock is off by more than a few minutes, sync and retry.
5-minute quick diagnosis flow
When you are stuck, work top to bottom.
- Check context →
kubectl config current-contextto see if you are pointed at the wrong place (cause 2) - Check token/cert expiry →
kubectl config view --raw --minify+openssl x509 -enddate(causes 1 and 4) - If cloud, refresh credentials →
aws sts get-caller-identity→aws eks update-kubeconfig ...orgcloud ... get-credentials(cause 3) - Check the clock →
timedatectlsync status (cause 6) - If you got this far, it is the permission layer →
kubectl auth can-i ...to check for 403 (cause 5)
Field tip: when a 401 is reported, make checking the context (cause 2) and
aws sts get-caller-identity(cause 3) your first habit; recovery gets faster.
Prevention is troubleshooting — auto token refresh and context isolation
Recovery matters, but stopping recurrence is the real skill.
- Auto-refresh with an exec credential plugin: do not embed a static token. Use
user.execin kubeconfig so a fresh token is fetched on every request. This is the recommended EKS approach.
users:
- name: my-eks
user:
exec:
apiVersion: client.authentication.k8s.io/v1beta1
command: aws
args:
- eks
- get-token
- --cluster-name
- my-cluster
- --region
- ap-northeast-2-
Separate contexts and kubeconfigs per cluster: do not mix prod and dev in one file. Split with
KUBECONFIG=~/.kube/prod:~/.kube/dev, or show the current context in the shell prompt (kube-ps1) to cut down on misfires. -
Check KUBECONFIG merge precedence: when you merge several files, for a user or context with the same name, the value from the file listed first wins. If you still get 401 after reissuing, check the file order with
echo $KUBECONFIGand compare the user actually picked withkubectl config view --minify. -
Use short-lived tokens on CI/CD runners: since Kubernetes 1.24, ServiceAccount token Secrets are no longer auto-generated, and Pods get bound tokens that expire. Don't rely on a token the runner copied long ago; issue one per run with
kubectl create token <sa-name> --duration=1h(TokenRequest API), or use your cloud's OIDC integration.
Command recap: confirm location with current-context → check expiry with config view --raw / openssl x509 → refresh with aws eks update-kubeconfig → check the clock with timedatectl → separate permissions with auth can-i.
Coming next: K8s_Troubleshooting_Guide Part 13 — Troubleshooting 403 Forbidden and RBAC permission design, which digs into the authorization (authz) area we only touched here.
Reference: official docs
Primary sources for the behavior, settings, and errors in this post are the official docs below. Check version-specific options and exact behavior there.
FAQ
Q. I ran aws eks update-kubeconfig and still get 401.
A. The AWS session itself is expired. First check that credentials are alive with aws sts get-caller-identity. If they are expired, SSO login (aws sso login) or refresh keys, then run update-kubeconfig again.
Q. I have multiple profiles and regions and it is confusing.
A. Pass --profile and --region explicitly, and put the same values in the exec plugin args. If the AWS_PROFILE environment variable and the kubeconfig exec settings disagree, you mint a token with the wrong credentials and get 401.
Q. Could a 401 still be an RBAC problem? A. Almost never. Missing RBAC happens after authentication succeeds, so it usually shows up as 403. If it is 401, token, certificate, context, or clock issues are far more likely — start with steps 1–4 of the 5-minute flow.
Sources and checks vary by article. Only articles with a recorded check date, scope and sources below were checked, and only within that scope. For other articles, verify against the current docs for your version before applying anything, and email us about errors. Editorial policy · email us
This article has no recorded human check (date and scope) yet.
Comments
Be the first to comment.