kubectl "connection to the server localhost:8080 was refused" in CI
When kubectl has no kubeconfig and no in-cluster service account, it falls back to its built-in default API server at localhost:8080. In CI nothing is listening there, so every request is refused - the real problem is that credentials were never loaded.
What this error means
Every kubectl command fails with The connection to the server localhost:8080 was refused - did you specify the right host or port?. The localhost:8080 address is the giveaway: kubectl is using its no-config default, not your cluster.
The connection to the server localhost:8080 was refused - did you specify the
right host or port?Common causes
KUBECONFIG never set or file missing
The job did not write a kubeconfig (or KUBECONFIG points at a path that does not exist), so kubectl has no cluster, user, or context and defaults to localhost:8080.
Credentials written in a different step/shell
A kubeconfig set up in one step does not carry to a later step’s fresh shell unless exported to the job environment, leaving the later step config-less.
How to fix it
Materialize the kubeconfig and export KUBECONFIG
Write the cluster credentials and point KUBECONFIG at them before any kubectl call.
echo "$KUBECONFIG_DATA" | base64 -d > "$RUNNER_TEMP/kubeconfig"
export KUBECONFIG="$RUNNER_TEMP/kubeconfig"
kubectl config current-contextGenerate cluster credentials in CI
- For cloud clusters, write kubeconfig from the provider (
aws eks update-kubeconfig,gcloud container clusters get-credentials,az aks get-credentials). - Persist
KUBECONFIGto the job env (e.g.echo "KUBECONFIG=..." >> "$GITHUB_ENV") so every step sees it. - Assert
kubectl config current-contextearly so a config-less job fails loudly.
How to prevent it
- Set
KUBECONFIG(or generate credentials) as an early, asserted step in the pipeline. - Export it to the job environment, not just one step’s shell.
- Treat a
localhost:8080error as "no kubeconfig loaded", not a cluster outage.