Skip to content

Exposing kube-apiserver

This is done on your own peril but if you want to mimic managed kubernetes solutions where you can connect to an external URL/address over the internet to manage your cluster then this is how you can do it.

I won't go into detail on each step as this is just a personal memo for myself until I can automate the process in my Ansible setup.

Install Traefik

Traefik is best for this, if you already have nginx-ingress then you need another IP for Traefik. Or migrate all your nginx ingresses over to Traefik.

I use the traefik/traefik Helm chart from their own repo, just make sure you know which public IP Traefik is using.

Reason I use Traefik is because I need TCP passthrough because kube-apiserver handles its own TLS. At this moment in time I am not aware how to handle TLS termination for kube-apiserver.

Create a domain

I usually create a domain like, and point its DNS to your Traefik IP.

Add a SAN (Subject Alternative Name) in the TLS cert

You must repeat this step on each control node because they all run an instance of kube-apiserver. Then it's up to you to balance connections to them from some external load balancer like HAproxy.

The two files /etc/kubernetes/pki/apiserver.{crt,key} contain the cert generated by kubeadm init when you installed the control node.

Move them to a backup location

mv /etc/kubernetes/pki/apiserver.{crt,key} /var/backups/

Dump the current kubeadm-config ConfigMap to a file

kubectl -n kube-system get configmap kubeadm-config -o jsonpath='{.data.ClusterConfiguration}' > kubeadm.yaml

Edit the kubeadm.yaml config

There's a list called certSANs where you add any names you need, kubeadm will self-sign these so go nuts. There should be an IP there that belongs to your control node, leave it.


Run kubeadm init

Now we only run one phase of the kubeadm init process which will create a new cert based on the config we modified.

sudo kubeadm init phase certs apiserver --config kubeadm.yaml

Expose kube-apiserver as a Service

Here's an example manifest based on what I use;

apiVersion: v1
kind: Service
  name: kube-apiserver
  namespace: kube-system
    component: kube-apiserver

  type: ClusterIP
    - port: 443
      targetPort: 6443
      protocol: TCP
      name: apiserver

    component: kube-apiserver

kind: IngressRouteTCP
  name: kube-apiserver
  namespace: kube-system

  - match: HostSNI(`*`)
    - name: kube-apiserver
      port: 443

    passthrough: True

Last update: August 13, 2021