1. Introduction
When kubectl returns connection refused, Unable to connect to the server, or no such host, the control plane is either unreachable or your client isn't configured correctly to reach it. For engineers managing production clusters, this can be a critical situation: you've lost the ability to diagnose or remediate anything in the cluster until the connection is restored.
The problem is either in your client (kubeconfig, VPN, credentials) or in the API server itself (crashed, OOMKilled, network partition). Separating client issues from server issues is the first step.
2. What the Error Actually Means
The Kubernetes API server is a REST API that kubectl (and all internal components) communicate with over HTTPS. "Connection refused" means the TCP connection was actively rejected — nothing is listening at that address and port. "Connection timed out" means the packet is being dropped — a firewall or routing issue. "TLS handshake failed" means connectivity works but the certificate is wrong or expired.
3. Common Causes
- kubeconfig points to wrong endpoint (wrong cluster, IP changed after restart)
- VPN not connected — API server is in a private network
- API server process crashed or was OOMKilled (static pod restart failure)
- etcd is down — API server can start but becomes unavailable without etcd
- TLS certificates expired (common after 1 year on self-managed clusters)
- Security group / firewall blocking port 6443 from your IP
- Control plane nodes have networking issues
- For EKS: cluster endpoint access is set to Private only, but you're connecting from outside the VPC
4. Step-by-Step Diagnosis and Fix
Step 1: Read the exact error message
# Run kubectl with verbose output to see full connection details
kubectl cluster-info --v=8 2>&1 | head -30
# Common patterns and what they mean:
# "connection refused" = nothing listening at that IP:port
# "no such host" = DNS can't resolve the API server hostname
# "certificate signed by unknown authority" = cert trust issue
# "TLS handshake timeout" = connectivity but TLS failing
# "context deadline exceeded" = timeout (firewall dropping packets)
# "You must be logged in to the server (Unauthorized)" = auth issue, not connectivity
Step 2: Check and fix kubeconfig
# View current context
kubectl config current-context
kubectl config view
# Check the server endpoint
kubectl config view -o jsonpath='{.clusters[].cluster.server}'
# Should return: https://<api-server-ip-or-hostname>:6443
# List all contexts
kubectl config get-contexts
# Switch to correct context
kubectl config use-context <correct-context>
# For EKS: regenerate kubeconfig
aws eks update-kubeconfig --region <region> --name <cluster-name>
Step 3: Test raw connectivity
# Extract the API server URL from kubeconfig
API_SERVER=$(kubectl config view -o jsonpath='{.clusters[].cluster.server}')
# Test TCP connectivity
curl -k $API_SERVER/healthz
# Expected: "ok"
# If curl can't connect, it's a network issue
# Test DNS resolution
nslookup <api-server-hostname>
# Test with specific port
nc -zv <api-server-ip> 6443
Step 4: Check API server pod health (self-managed clusters)
# On the control plane node via SSH:
# Check API server static pod
ls /etc/kubernetes/manifests/kube-apiserver.yaml
# Check if the container is running
crictl ps | grep kube-apiserver
# or: docker ps | grep kube-apiserver
# Check logs
crictl logs <apiserver-container-id>
# or: journalctl -u kubelet | grep apiserver
# Restart by touching the static pod manifest (kubelet will recreate)
touch /etc/kubernetes/manifests/kube-apiserver.yaml
Step 5: Check TLS certificates
# Check certificate expiry dates on control plane node
# kubeadm-managed certificates:
kubeadm certs check-expiration
# Manual check:
openssl x509 -noout -dates -in /etc/kubernetes/pki/apiserver.crt
# If expired, renew with kubeadm:
kubeadm certs renew all
# After renewing: restart API server
systemctl restart kubelet
Step 6: Fix EKS private endpoint access
# Check EKS endpoint access settings
aws eks describe-cluster --name <cluster-name> --query 'cluster.resourcesVpcConfig.{Private:endpointPrivateAccess,Public:endpointPublicAccess}'
# If Public=false and you're connecting from outside the VPC, you need:
# 1. VPN connection to the VPC
# 2. Or enable public access temporarily:
aws eks update-cluster-config --region <region> --name <cluster-name> --resources-vpc-config endpointPublicAccess=true
5. Verification Steps
# Basic connectivity check
kubectl cluster-info
# Expected: "Kubernetes control plane is running at https://..."
# Run a basic command
kubectl get nodes
# Should return node list
# Check API server health directly
curl -k $(kubectl config view -o jsonpath='{.clusters[].cluster.server}')/healthz
# Expected: "ok"
6. Common Mistakes
- Assuming "connection refused" means the API server crashed — often the kubeconfig points to the wrong endpoint
- Forgetting to renew TLS certificates on self-managed clusters — they expire after 1 year by default
- Not checking VPN status before opening a support ticket — API server in a private VPC is unreachable without VPN
- Trying to debug from the wrong context —
kubectl config current-contextis the first thing to check - Ignoring etcd health — API server can appear to start correctly but becomes unavailable if etcd is down
7. Prevention Tips
- For self-managed clusters, set up certificate renewal alerts 30 days before expiry
- Use managed Kubernetes services (EKS, GKE, AKS) where certificate management is handled automatically
- Store kubeconfigs in a secret manager, not local files — reduces drift between engineers' local configs and actual cluster state
- Monitor API server availability with a simple
curl /healthzcheck from a pod inside the cluster - Configure API server audit logging to capture failed authentication attempts
- For node-level connectivity issues, see Fix Kubernetes Node Not Ready
8. FAQ
kubectl works from my laptop but not from CI/CD. Why?
The CI/CD environment has different network access and a different kubeconfig. Common causes: the CI runner doesn't have VPN access to the cluster's VPC, the kubeconfig in CI has an expired token, or the ServiceAccount used by CI doesn't have the right RBAC permissions. Check the exact error — "Unauthorized" means auth failed, not connectivity; "connection refused" means network.
The API server is responding but kubectl returns "Forbidden". Is that a connection issue?
No — "Forbidden" (403) means connectivity works fine but the requesting identity doesn't have permission for that operation. That's an RBAC issue, not an API server connection issue. Check kubectl auth can-i <verb> <resource> to understand what the current identity is allowed to do.
9. Summary
| Error | Cause | Fix |
|---|---|---|
| connection refused | Wrong endpoint or API server down | Check kubeconfig server URL; check API server pod |
| no such host | DNS can't resolve API server hostname | Check DNS; update kubeconfig with IP instead |
| certificate signed by unknown authority | Cert trust issue | Update kubeconfig CA cert; check cert expiry |
| context deadline exceeded | Firewall dropping packets | Check security group/firewall on port 6443 |
| Unauthorized (401) | Credentials expired or wrong | Re-authenticate; refresh kubeconfig token |
Explore More in This Category
Explore more in this category: Kubernetes guides. Browse all DevOps Compass articles or jump to: Kubernetes, AWS, CI/CD, Containers, Monitoring, Networking.