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 old cert and key to a backup location

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

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 --apiserver-cert-extra-sans=',localhost,,'

Remember to list every single name you might use to connect to your kube-apiserver.

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: January 31, 2022