Kubernetes operator

The Tailscale Kubernetes operator allows you to expose services in your Kubernetes cluster to your Tailscale network, and use an authentication proxy for secure connectivity to the Kubernetes control plane. Once installed, you can mark services in your cluster as Tailscale-accessible, and the operator will take care of the plumbing to add those services as nodes on your tailnet.

Kubernetes operator is currently in alpha. To try it, follow the steps below to enable it for your network using Tailscale v1.37.40 or later.

Setting up the Kubernetes operator

  1. In your tailnet policy file, create the ACL tags tag:k8s-operator and tag:k8s, and make tag:k8s-operator an owner of tag:k8s. If you want your services to be exposed with tags other than the default tag:k8s, create those as well and make tag:k8s-operator an owner.

    "tagOwners": {
       "tag:k8s-operator": [],
       "tag:k8s": ["tag:k8s-operator"],
  2. Create an OAuth client in the OAuth clients page of the admin console. Create the client with Devices write scope and the tag tag:k8s-operator.

  3. Download the Tailscale Kubernetes operator manifest file from the tailscale/tailscale repo.

  4. Edit your version of the manifest file:

    1. Find # SET CLIENT ID HERE and replace it with your OAuth client ID.
    2. Find # SET CLIENT SECRET HERE and replace it with your OAuth client secret.

    For both the client ID and secret, quote the value, to avoid any potential yaml misinterpretation of unquoted strings. For example, use:

    client_id: "k123456CNTRL"
    client_secret: "tskey-client-k123456CNTRL-abcdef"

    instead of:

    client_id: k123456CNTRL
    client_secret: tskey-client-k123456CNTRL-abcdef
  5. Apply the edited file to your Kubernetes cluster:

    kubectl apply -f manifest.yaml

    This creates the “tailscale” namespace in your cluster, and deploys the Tailscale operator within it.

  6. Verify that the Tailscale operator has joined your tailnet. Open the Machines page of the admin console and look for a node named tailscale-operator, tagged with the tag:k8s-operator tag. It may take a minute or two for the operator to join your tailnet, due to the time required to download and start the container image in Kubernetes.

Using the Kubernetes operator

You can expose a Kubernetes service over Tailscale in two ways: by making it a LoadBalancer type with the tailscale loadBalancerClass, or by annotating an existing service.

Exposing a service using loadBalancerClass

Edit the service you want to expose and make it a load balancer:

  1. Set spec.type to LoadBalancer.
  2. Set spec.loadBalancerClass to tailscale.

Once provisioning is complete, the service’s status will show the fully-qualified domain name of the service in your tailnet. You can view the service’s status by running kubectl get service <service name>.

You should also see a new node with that name appear in the Machines page of the admin console.

Exposing a service using annotations

If the service you want to expose already exists, you can expose it to Tailscale using object annotations.

Edit the service and under metadata.annotations, add the annotation tailscale.com/expose with the value "true". Note that "true" is quoted because annotation values are strings, and an unquoted true will be incorrectly interpreted as a boolean.

In this mode, Kubernetes doesn’t tell you the Tailscale machine name. You can look up the node in the Machines of the admin console to learn its machine name. By default, the machine name of an exposed service is <k8s-namespace>-<k8s-servicename>.

Using a custom machine name

If you want the service to have a machine name other than the default <k8s-namespace>-<k8s-servicename>, you can provide your own machine name by setting the tailscale.com/hostname annotation on the service, with your desired machine name as the value.

Machine names are subject to the constraints of DNS: they can be up to 63 characters long, must start and end with a letter, and consist of only letters, numbers, and -.

Customizing tags

By default, services join your tailnet tagged with tag:k8s. You can request a different set of tags by setting the tailscale.com/tags annotation on the service, with your desired tags (comma-separated) as the value.

For example, setting tailscale.com/tags = tag:foo,tag:bar will result in the tailnet node having the tags tag:foo and tag:bar.

The Tailscale operator must be a tag owner of all the specified tags: if you want to expose a service with tag:foo,tag:bar, the tagOwners section of the tailnet policy file must list tag:k8s-operator as one of the owners of both tag:foo and tag:bar.

Enabling an auth proxy

A Tailscale Kubernetes auth proxy allows users to expose and access their Kubernetes control plane (kube-apiserver) over Tailscale. When a user tries to access the Kubernetes control plane over Tailscale, they will hit the kube-apiserver with the same user identity that they have in Tailscale. For example, alice@example.com will have the user alice@example.com in an auth proxy. To use a Tailscale Kubernetes auth proxy, you also need to enable HTTPS for your tailnet.

To configure the auth proxy:

  1. In your Tailscale Kubernetes operator.yml file, add the following lines to the env section:

    name: AUTH_PROXY
    value: "true"

    The kube-apiserver is automatically discovered by the operator.

  2. Apply the changes from the example to your operator’s manifest file.

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    name: tailscale-auth-proxy
    - apiGroups: [""]
    resources: ["users"]
    verbs: ["impersonate"]
    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    name: tailscale-auth-proxy
    - kind: ServiceAccount
    name: operator
    namespace: tailscale
    kind: ClusterRole
    name: tailscale-auth-proxy
    apiGroup: rbac.authorization.k8s.io
  3. Add an access rule in your tailnet’s policy file to grant access to the auth proxy over Tailscale.

  4. Use the tailscale configure kubeconfig <hostname-or-fqdn> CLI command to configure your local kubeconfig file to manage how to authenticate to kubectl as the Tailscale Kubernetes auth proxy.

  5. Run the following command to grant a user the Kubernetes cluster-admin role in your cluster.

    kubectl create clusterrolebinding --clusterrole cluster-admin --user alice@example alice@example

To validate the auth proxy allows you to access the kube-apiserver over Tailscale:

  1. Run the kubectl get pods -A command to run another test and verify that you have authorization.
    NAMESPACE     NAME                               READY   STATUS    RESTARTS      AGE
    kube-system   cilium-6b2x8                       1/1     Running   0             28d
    kube-system   cilium-operator-759999b555-qbsrk   1/1     Running   3 (21d ago)   29d
    kube-system   coredns-7697897646-4vh2l           1/1     Running   0             29d
    kube-system   coredns-7697897646-rshwm           1/1     Running   0             29d
    kube-system   cpc-bridge-proxy-xksns             1/1     Running   0             29d
    kube-system   csi-do-node-k5snn                  2/2     Running   0             29d
    kube-system   do-node-agent-n8nrr                1/1     Running   0             29d
    kube-system   konnectivity-agent-k846g           1/1     Running   0             29d
    kube-system   kube-proxy-lgzr9                   1/1     Running   0             29d
    tailscale     operator-6b94c54478-n6tmc          1/1     Running   0             14d


Any of the following actions remove the service from your tailnet:

  • Delete the service entirely.
  • If you are using the tailscale.com/expose annotation, remove the annotation.
  • If you are using type=LoadBalancer, set type back to ClusterIP, or remove loadBalancerClass=tailscale.

Note that deletion is a one-way operation: deleting a service’s Tailscale node in the admin console does not clean up the Kubernetes state associated with that service.


The alpha Tailscale operator has the following known issues and limitations that we plan to address.

  • The auth proxy runs inside of the cluster. If your cluster is non-functional or is unable to schedule pods, you may lose access to the auth proxy.
  • Only development (“unstable”) builds are usable for now, as the operator depends on some changes that happened after the release of Tailscale v1.36.
  • There are no deployment options other than applying manifest.yaml. Let us know what other deployment methods you would like!
  • Tags are only considered during initial provisioning. That is, editing tailscale.com/tags on an already exposed service doesn’t update the tags until you clean up and re-expose the service.
  • The requested machine name is only considered during initial provisioning. That is, editing tailscale.com/hostname on an already exposed service doesn’t update the machine name until you clean up and re-expose the service.
  • There are no automated updates. The operator and proxy pods will not update automatically to newer Tailscale releases as they become available.
  • There are no dashboards or metrics.

Last updated

WireGuard is a registered
trademark of Jason A. Donenfeld.

© 2023 Tailscale Inc.

Privacy & Terms