Skip to main content
DevOps Insightsguides & answers

Kubernetes · troubleshooting

The connection to the server localhost:8080 was refused

kubectl found no kubeconfig, so it fell back to its built-in default, http://localhost:8080, where no API server is listening. The fix is to give kubectl the cluster's kubeconfig, not to start anything on port 8080. A different host in the same message means a configured cluster is not answering.

Reproduced on kubectl 1.37.1, . Where the text says “reproduced”, the behaviour was run on that version, not taken from documentation.

First published

New to this? Read what is actually happening first, then come back to the steps.

On this page · 9 sections

Before you start

  • A kubeconfig for the cluster: from kubeadm (/etc/kubernetes/admin.conf), your cloud provider's CLI, or the cluster's administrator.
  • Read access to that file as the user who runs kubectl.
  • Network access from your machine to the API server address inside the kubeconfig.

Run this first

step 1 of 3
kubectl config view --minify

Look for An empty result points to the first two causes; a cluster entry points to the third

Shows the configuration in effect for this command, or nothing at all.

Work out which cause you have

A few questions to narrow the list. Every answer ends in a command that confirms or rules the cause out — this cannot see your cluster, so nothing here is a certainty until you have checked.

Narrow it down

0 answered · nothing is sent anywhere

Which host does the error name, or what did kubectl print?

Read the message, or run kubectl get pods -v=6 and look at url=

Cause space

4 of 4 still possible

  • This user has no kubeconfigCommon
  • The kubeconfig exists, but not where this command looksCommon
  • A cluster is configured, but its API server is not answeringCommon
  • No kubeconfig, and something else is answering on port 8080Occasional

Nothing ruled out yet. Answer the question above and the branches your answer eliminates will strike through here.

Or diagnose it manually

In this order. The first command usually contains the whole answer.

  1. Step 1

    kubectl config view --minify

    Shows the configuration in effect for this command, or nothing at all.

    Look for An empty result points to the first two causes; a cluster entry points to the third

  2. Step 2

    echo "$KUBECONFIG" && ls -l ~/.kube/config

    Shows where kubectl looks and whether the default file exists.

    Look for An unset or wrong KUBECONFIG, and a missing ~/.kube/config

  3. Step 3

    kubectl get nodes -v=6 2>&1 | grep -m3 -iE 'config|url='

    Shows the file loaded and the URL requested, which settles which cause applies.

    Look for url=http://localhost:8080 means no configuration was used

Every cause, and how to fix it

Ordered by how often each one turns out to be the answer.

This user has no kubeconfig

Common

The cluster exists but this user's ~/.kube/config does not. After kubeadm init, admin.conf stays in /etc/kubernetes until someone copies it, as kubeadm's output instructs. A new laptop, a new CI runner or a fresh container is the same situation.

Confirm

kubectl config view && ls -l ~/.kube/config

clusters: null, and No such file or directory for ~/.kube/config

Fix

  • On a kubeadm control-plane node, copy admin.conf into your home directory as kubeadm's own instructions show, then confirm with kubectl get nodes.
  • For a managed cluster, write the kubeconfig with the provider's CLI, for example aws eks update-kubeconfig, gcloud container clusters get-credentials or az aks get-credentials.
  • Otherwise ask the cluster's administrator for a kubeconfig with the access you need, rather than admin.conf.
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes

The commands kubeadm init prints for a regular user. admin.conf grants cluster-admin; use it only for people who should have that.

The kubeconfig exists, but not where this command looks

Common

kubectl reads the home directory and environment of whoever runs it. sudo kubectl reads root's home; a cron job or systemd unit has a different environment; KUBECONFIG may name a file that has moved. Each finds nothing and falls back to localhost:8080.

Confirm

echo "KUBECONFIG=$KUBECONFIG HOME=$HOME" && whoami && kubectl config view --minify

A user, HOME or KUBECONFIG different from where the working configuration lives, or an empty --minify output

Fix

  • Run kubectl as the user who owns the kubeconfig, without sudo.
  • Point KUBECONFIG, or --kubeconfig, at the file explicitly in scripts, cron jobs and CI.
  • If KUBECONFIG lists several files, check each exists; kubectl merges the ones it can read.

A cluster is configured, but its API server is not answering

Common

The message names a host other than localhost:8080. The kubeconfig is fine; the cluster is stopped or unreachable: minikube, kind or Docker Desktop not running, a control-plane node down, or an API server address that changed.

Confirm

kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}'

The address in the error message. If it is a local cluster, check that cluster's own status command; if remote, whether the address is reachable

Fix

  • Start the local cluster with its own tool, for example minikube start or Docker Desktop's Kubernetes setting.
  • For kubeadm, on the control-plane node check the API server's static pod and the kubelet: sudo systemctl status kubelet.
  • If the server address changed, update the kubeconfig from the provider or the administrator.

No kubeconfig, and something else is answering on port 8080

Occasional

Same missing configuration, different symptom. If Jenkins, Tomcat or a development server is listening on localhost:8080, kubectl reaches it, gets an answer that is not the Kubernetes API, and reports NotFound instead of a refused connection.

Confirm

kubectl get pods -v=6 2>&1 | grep -m1 localhost:8080

A request to http://localhost:8080. Reproduced on kubectl 1.37.1 with a Python server on 8080: the command printed Error from server (NotFound)

Fix

  • Fix the configuration as in the first cause. The program on 8080 is unrelated and needs no change.

Understanding it properly

Skip this if you are mid-incident — the working part of the page is above. Worth reading afterwards, because understanding the mechanism is what stops the next one.

In plain terms

kubectl needs a configuration file, the kubeconfig, that says which cluster to talk to and how to log in. When it cannot find one, it quietly tries http://localhost:8080 instead. On almost every machine nothing Kubernetes-related listens there, so the connection is refused. The error is about the missing file, not about port 8080.

No kubeconfig, then a configured cluster that is down

Reproduced with kubectl 1.37.1 on macOS, with HOME pointed at an empty directory and KUBECONFIG unset, so kubectl found no configuration. On this machine another program already listened on 127.0.0.1:8080, so the no-config case got a 404 from it rather than a refusal; the exact refusal sentence is shown from the second case, where nothing listened on 127.0.0.1:6443.

Command

kubectl config view
kubectl config current-context
kubectl get pods -v=6 2>&1 | grep localhost:8080
kubectl get pods    # kubeconfig pointing at a stopped cluster

Output

$ kubectl config view
apiVersion: v1
clusters: null
contexts: null
current-context: ""
kind: Config
users: null

$ kubectl config current-context
error: current-context is not set

$ kubectl get pods -v=6 2>&1 | grep localhost:8080
… "Response" verb="GET" url="http://localhost:8080/api?timeout=32s" …

$ kubectl get pods
E1018 …  "Couldn't get current server API group list" err="Get \"https://127.0.0.1:6443/api?timeout=32s\": dial tcp 127.0.0.1:6443: connect: connection refused"
The connection to the server 127.0.0.1:6443 was refused - did you specify the right host or port?

How to read it

  • clusters: null and current-context: "" mean kubectl has no configuration at all.
  • The -v=6 trace shows the request going to http://localhost:8080. That is the fallback in client-go's source, used when nothing configures a server.
  • With nothing listening on that address, kubectl turns the refused connection into the sentence in the title, naming the host it tried. Here that was 127.0.0.1:6443 from a kubeconfig; with no kubeconfig it is localhost:8080.
  • Because another program answered on 8080 on this machine, the no-config case instead printed Error from server (NotFound): the server could not find the requested resource. Same cause, different symptom: anything on port 8080, such as Jenkins or a dev server, produces it.

Words used on this page

kubeconfig
The YAML file that lists clusters, users and contexts for kubectl. By default it is ~/.kube/config; the KUBECONFIG variable or --kubeconfig can point elsewhere.
Context
A named pairing of a cluster and a user in a kubeconfig. current-context decides which one kubectl uses.
API server
The Kubernetes control-plane component every kubectl command talks to, usually on port 6443.
Default server
What kubectl uses when no kubeconfig supplies a cluster: http://localhost:8080, or the KUBERNETES_MASTER variable if set (client-go source).
admin.conf
The kubeconfig kubeadm generates on a control-plane node. It carries cluster-admin credentials.
What is actually happening

kubectl builds its connection from the kubeconfig: the --kubeconfig flag, then the files named in KUBECONFIG, then ~/.kube/config. If none supplies a cluster, client-go fills in defaults, and its default server is http://localhost:8080, or KUBERNETES_MASTER if that variable is set.

When the connection to that address is refused, kubectl's error helper rewrites Go's connection error into one sentence: The connection to the server <host> was refused - did you specify the right host or port? The host it names tells you which situation you are in.

localhost:8080 means no configuration was found for the user and environment that ran the command. Any other host, such as 127.0.0.1:6443 or a cloud endpoint, means a configuration was found and the cluster it names is not accepting connections.

Port 8080 is a leftover. Early Kubernetes API servers offered an unauthenticated local port; current ones serve only the secure port, usually 6443. Nothing in a modern cluster listens on 8080 for kubectl.

How to tell this is your problem
WhereWhat you see
kubectl outputThe connection to the server localhost:8080 was refused - did you specify the right host or port?
kubectl output, another hostThe connection to the server 127.0.0.1:6443 was refused, or a cloud address
kubectl config viewclusters: null and current-context: ""
kubectl config current-contexterror: current-context is not set
kubectl output, something else on port 8080Error from server (NotFound): the server could not find the requested resource

How to know it is actually fixed

  • kubectl config current-context prints a context name.
  • kubectl get nodes lists the cluster's nodes.
  • The same works from the script, cron job or CI step that failed, not only from your shell.

Stopping it happening again

  • In CI, provide the kubeconfig explicitly, as a secret mounted to a known path with KUBECONFIG set, rather than relying on a home directory.
  • Give each person their own kubeconfig with the access they need instead of passing admin.conf around.
  • Check kubectl config current-context before running commands in a new shell, especially when you work with several clusters.

Security considerations

  • admin.conf is a cluster-admin credential. kubeadm's documentation says not to share it with anyone. Copy it only into the home directory of people who should administer the whole cluster, readable only by them.
  • Do not chmod a kubeconfig to make it readable by others, and do not commit one to a repository. Anyone holding it acts as that user against the cluster.
  • Avoid running kubectl with sudo to get around this. It reads root's configuration instead of yours and blurs who did what in the audit log.
  • Never answer this by running something on port 8080. The error is about a missing configuration, and an unauthenticated local endpoint is not one.

Sources

Behaviour described here is drawn from official documentation. Where a figure could not be confirmed on an official page it is attributed in the text rather than stated as canonical.

Did this get you to an answer?

No text box on purpose — please do not paste production logs anywhere

Something wrong or out of date? Report it