Skip to content

Fix: The Connection to the Server localhost:8080 Was Refused (kubectl)

FixDevs · (Updated: )

Part of:  Docker, DevOps & Infrastructure

Quick Answer

How to fix 'the connection to the server localhost:8080 was refused' and other kubectl connection errors when the Kubernetes API server is unreachable.

The Error

You run a kubectl command and get:

The connection to the server localhost:8080 was refused - did you specify the right host or port?

Or one of these variations:

Unable to connect to the server: dial tcp 127.0.0.1:6443: connect: connection refused
error: You must be logged in to the server (Unauthorized)
Unable to connect to the server: x509: certificate signed by unknown authority

All of these mean kubectl cannot reach or authenticate with the Kubernetes API server.

Why This Happens

kubectl needs two things to work: a running API server and a valid kubeconfig that points to it. The localhost:8080 error specifically means kubectl has no kubeconfig at all — it falls back to the default localhost:8080, where nothing is listening.

The 127.0.0.1:6443 variant means kubectl has a kubeconfig, but the API server at that address is down or unreachable. The Unauthorized and x509 errors mean the server is reachable but your credentials or certificates are invalid.

Common causes:

  • Your cluster isn’t running. Minikube is stopped, Docker Desktop Kubernetes is disabled, or your kind cluster was deleted.
  • KUBECONFIG isn’t set or points to the wrong file. kubectl can’t find your cluster configuration.
  • Wrong context selected. Your kubeconfig has multiple clusters and the active context points to one that’s unavailable.
  • Expired credentials or certificates. Common with cloud clusters (EKS, GKE, AKS) where auth tokens have a limited lifespan.

Version history: kubeconfig precedence, KUBECONFIG, and version skew

A handful of kubectl behaviors have evolved enough over the years that they trip up developers who learned Kubernetes on an older version. Knowing where kubectl reads configuration from — and which version of the server it can talk to — solves a lot of “kubectl can’t connect” mysteries before they become long debugging sessions.

kubeconfig precedence. When kubectl decides where to read configuration from, it walks a fixed precedence chain. The --kubeconfig flag wins absolutely. Otherwise, the KUBECONFIG environment variable wins. If that is unset, kubectl falls back to ~/.kube/config. The KUBECONFIG variable accepts a colon-separated list of files (semicolons on Windows), and kubectl merges them at read time without writing anything back to disk. Editing the merged view does not edit the underlying files. This merge-on-read behavior has been stable since Kubernetes 1.8, but the KUBECONFIG variable itself was introduced earlier and old tutorials sometimes write KUBECONFIG=~/.kube/config as if it were a single file — fine, but unaware of the merge feature.

Client/server version skew. Kubernetes officially supports a kubectl that is one minor version newer or older than the API server (the “n±1” rule). A kubectl from 1.30 talking to a 1.29 server works. A 1.30 kubectl talking to a 1.27 server is unsupported and may return strange errors that look like connection refusals when they are actually version-skew bugs. Check with:

kubectl version

The output shows Client Version and Server Version. If they differ by more than one minor version, install a matching kubectl. Distros like Ubuntu often ship a kubectl that lags the latest stable release by several versions — install kubectl from the upstream apt/yum repo instead of the distro’s package.

Context-name vs cluster-name vs user-name. A kubeconfig “context” is a tuple of (cluster, user, namespace). Renaming a context with kubectl config rename-context only renames the context tuple — it does not rename the underlying cluster or user entries. If you copy a kubeconfig from another machine and the cluster CA path or user token is missing, kubectl will reach the server address (since the cluster entry still has it) but fail to authenticate. Always inspect the merged config with kubectl config view --minify after copying files across machines.

Plugin discovery via krew. kubectl plugins discovered via krew look like first-class subcommands. If a plugin breaks (for example, after a Kubernetes upgrade), the resulting error can look like a connection failure if the plugin shells out and silently fails. Test plain commands first (kubectl version --client, kubectl config view) before assuming a network issue.

Fix 1: Start Your Cluster

The most common cause is a cluster that isn’t running. Start it based on your setup.

Minikube:

minikube start

Check status:

minikube status

If the status shows Stopped, minikube start will bring it back with your previous configuration intact.

Docker Desktop:

  1. Open Docker Desktop
  2. Go to Settings > Kubernetes
  3. Check Enable Kubernetes
  4. Click Apply & Restart

Wait for the Kubernetes indicator in the bottom-left corner to turn green. This can take a few minutes on the first enable.

kind (Kubernetes in Docker):

kind get clusters

If no clusters are listed, create one:

kind create cluster

If a cluster exists but containers are stopped (e.g., after a Docker restart), delete and recreate it:

kind delete cluster
kind create cluster

Note: kind clusters don’t survive Docker daemon restarts. If you restarted Docker or your machine, you need to recreate the cluster.

Real-world scenario: A developer’s kubectl worked yesterday but fails today with “connection refused.” They rebooted their laptop overnight, which stopped Docker Desktop. Minikube, kind, and Docker Desktop Kubernetes all require the container runtime to be running. Starting Docker Desktop and waiting for the Kubernetes indicator to turn green resolves it.

k3d / k3s:

k3d cluster list
k3d cluster start <cluster-name>

After starting any of these, verify the connection:

kubectl cluster-info

Fix 2: Fix Your KUBECONFIG Path

If kubectl can’t find your kubeconfig file, it defaults to localhost:8080. Check what file it’s using:

kubectl config view

If this returns an empty config or an error, kubectl isn’t finding your kubeconfig.

The default path is ~/.kube/config. Verify the file exists:

ls -la ~/.kube/config

If your kubeconfig is at a different path, set the KUBECONFIG environment variable:

export KUBECONFIG=/path/to/your/kubeconfig

Add this to your ~/.bashrc or ~/.zshrc to make it permanent:

echo 'export KUBECONFIG=/path/to/your/kubeconfig' >> ~/.bashrc
source ~/.bashrc

Multiple kubeconfig files: You can merge multiple kubeconfigs by separating paths with a colon (semicolon on Windows):

# Linux / macOS
export KUBECONFIG=~/.kube/config:~/.kube/config-eks:~/.kube/config-gke

# Windows (PowerShell)
$env:KUBECONFIG = "$HOME\.kube\config;$HOME\.kube\config-eks"

To permanently merge them into a single file:

KUBECONFIG=~/.kube/config:~/.kube/config-other kubectl config view --flatten > ~/.kube/config-merged
mv ~/.kube/config-merged ~/.kube/config

Warning: Back up your existing kubeconfig before merging. A syntax error in the merged file can lock you out of all clusters.

Fix 3: Fix Your kubectl Context

Your kubeconfig may contain multiple clusters. If the active context points to a cluster that no longer exists or is unreachable, you get a connection error.

List all available contexts:

kubectl config get-contexts

The current context is marked with *. Switch to the correct one:

kubectl config use-context minikube

Or for Docker Desktop:

kubectl config use-context docker-desktop

Verify the switch worked:

kubectl cluster-info

If you don’t know which context to use, check what each one points to:

kubectl config view -o jsonpath='{range .contexts[*]}{.name}{"\t"}{.context.cluster}{"\n"}{end}'

Fix 4: Docker Desktop Kubernetes Not Enabled or Running

Docker Desktop ships with a built-in Kubernetes cluster, but it’s disabled by default and can get into a bad state.

If Kubernetes is not enabled:

  1. Open Docker Desktop
  2. Go to Settings > Kubernetes
  3. Check Enable Kubernetes
  4. Click Apply & Restart

If Kubernetes is enabled but not working:

The Kubernetes status indicator in Docker Desktop should be green. If it’s orange or red:

  1. Go to Settings > Kubernetes
  2. Click Reset Kubernetes Cluster
  3. Wait for the status to turn green

If a reset doesn’t fix it, try a full Docker Desktop restart:

  1. Quit Docker Desktop completely
  2. Reopen Docker Desktop
  3. Wait for both Docker and Kubernetes indicators to turn green

If the kubectl context isn’t set to Docker Desktop:

Docker Desktop creates a context called docker-desktop. Make sure it’s active:

kubectl config use-context docker-desktop

Note: Docker Desktop Kubernetes uses port 6443, not 8080. If you see the localhost:8080 error with Docker Desktop, your kubeconfig is likely missing or not pointing to the Docker Desktop cluster.

Fix 5: Certificate and Authentication Issues

The x509: certificate signed by unknown authority and Unauthorized errors mean kubectl reaches the server but can’t authenticate.

Expired or invalid certificates

Check your cluster’s certificate expiry:

kubectl config view --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d | openssl x509 -text -noout | grep "Not After"

For kubeadm-managed clusters, check and renew certificates:

sudo kubeadm certs check-expiration
sudo kubeadm certs renew all

After renewing, restart the control plane components:

sudo systemctl restart kubelet

Then update your kubeconfig:

sudo cp /etc/kubernetes/admin.conf ~/.kube/config
sudo chown $(id -u):$(id -g) ~/.kube/config

Cloud cluster auth tokens expired

AWS EKS:

Your AWS credentials or IAM token may have expired. Update the kubeconfig:

aws eks update-kubeconfig --region <region> --name <cluster-name>

Make sure your AWS CLI credentials are current:

aws sts get-caller-identity

If this fails, refresh your credentials with aws configure or update your AWS SSO session.

Google GKE:

gcloud container clusters get-credentials <cluster-name> --region <region> --project <project-id>

If your gcloud auth is expired:

gcloud auth login

Azure AKS:

az aks get-credentials --resource-group <rg-name> --name <cluster-name>

If your Azure CLI session is expired:

az login

Wrong user credentials in kubeconfig

If someone manually edited the kubeconfig or you copied it from another machine, the user credentials may not match the cluster. Regenerate the kubeconfig using the commands above for your cluster type.

Still Not Working?

Check if the API server is actually running

On a self-managed cluster, check the API server pod or process:

# kubeadm clusters
sudo crictl ps | grep kube-apiserver

# Or check the kubelet service
sudo systemctl status kubelet
sudo journalctl -u kubelet --no-pager -n 50

If the API server isn’t running, check its logs:

sudo journalctl -u kubelet --no-pager | grep "kube-apiserver"

Firewall or security group blocking the port

If your cluster is on a remote machine, verify the API server port is accessible:

curl -k https://<server-ip>:6443/healthz

An ok response means the server is reachable. A timeout means a firewall is blocking port 6443.

For cloud clusters, check the security group or firewall rules allow inbound traffic on the API server port from your IP address. EKS, GKE, and AKS all have options to restrict API server access to specific CIDR ranges.

VPN blocking the connection

If you connect to your cluster over a VPN and the connection suddenly stopped working:

  • Check your VPN is connected. Many corporate clusters are only reachable on the internal network.
  • Check for split-tunnel issues. Some VPN configurations route all traffic through the VPN, while others only route specific subnets. If your cluster IP isn’t in the VPN’s routed subnets, traffic goes over the public internet and gets blocked.
  • DNS resolution may differ. The cluster hostname might resolve to a different IP inside vs. outside the VPN. Test with nslookup <cluster-hostname> while connected and disconnected.

Proxy settings interfering

If you’re behind a corporate proxy, kubectl traffic might be getting routed through it. The API server is typically not reachable via a web proxy. Add your cluster IP or hostname to the no-proxy list:

export NO_PROXY=$NO_PROXY,<cluster-ip>,<cluster-hostname>
export no_proxy=$no_proxy,<cluster-ip>,<cluster-hostname>

For Minikube specifically:

export NO_PROXY=$NO_PROXY,$(minikube ip)

Pro Tip: Run kubectl config view --minify to see only the current context’s configuration. This strips out all other clusters and users, making it much easier to spot issues with the server address, certificate path, or user credentials for the cluster you are actually trying to reach.

Get full diagnostic output

If you’ve partially fixed the issue and kubectl connects but things still seem wrong, run a full diagnostic dump:

kubectl cluster-info dump

If kubectl still can’t connect at all, start with these client-side checks:

kubectl cluster-info
kubectl version --client
kubectl config current-context
kubectl config view --minify

The --minify flag shows only the current context’s config, which makes it easier to spot issues with the server address, certificate path, or user credentials.

KUBECONFIG list contains a missing file

If KUBECONFIG=/path/a:/path/b:/path/c includes a file that no longer exists, kubectl behavior depends on the version. Newer kubectl prints a warning and continues; older versions silently ignored the missing file and produced a confusingly empty merged config. Inspect with:

echo $KUBECONFIG
for f in $(echo $KUBECONFIG | tr ':' ' '); do [ -f "$f" ] && echo "OK: $f" || echo "MISSING: $f"; done

Remove dead paths from the list. The same issue affects Windows when stale paths from old Docker Desktop installations linger in the KUBECONFIG environment variable.

Context name conflict across kubeconfigs

If two merged kubeconfigs both define a context called default, only the first one wins. The second is silently shadowed. You will then “switch” contexts and end up on the wrong cluster, hitting the wrong API server, and getting a connection refusal that has nothing to do with networking. Rename one of the contexts:

kubectl config rename-context default eks-prod --kubeconfig ~/.kube/config-eks

For broader context-resolution issues, see Fix: kubectl context not found.

kubectl proxy left running on port 8080

If you previously ran kubectl proxy --port=8080 and forgot to stop it, a subsequent kubectl call with no kubeconfig may “connect” to the proxy but get unexpected responses. Check for stray proxies:

ps aux | grep "kubectl proxy"

Kill them, then re-run your command. This is a common cause of the localhost:8080 response succeeding partially with strange errors instead of cleanly refusing.

apply failures masquerading as connection errors

A malformed manifest can produce error output that looks like a connection failure, especially when piped to kubectl apply -f -. If kubectl cluster-info works but kubectl apply fails with a connection-style message, the manifest itself may be the problem. See Fix: kubectl apply invalid object for diagnosis steps.

Helm operations failing with the same error

Helm uses your kubeconfig the same way kubectl does, so any “connection refused” issue here will also affect helm install, helm upgrade, and helm list. If you see Helm fail with the same message, the fix is the same. For Helm-specific issues that persist after kubectl is healthy, see Fix: helm not working.


Related: If you’re getting Docker socket errors, see Fix: Docker Permission Denied.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles