{"slug":"expose-a-cloud-service-to-your-tailnet","title":"Expose a cloud service to your tailnet","tags":["tailscale","containers"],"agent_summary":"Last validated: Jan 5, 2026","trigger_phrases":[],"runnable":false,"markdown":"\r\n# Expose a cloud service to your tailnet\r\n\r\nLast validated: Jan 5, 2026\r\n\r\nExposing cloud services to your tailnetis currently [in beta](https://tailscale.com/docs/reference/tailscale-release-stages#beta).\r\n\r\nYou can use the Tailscale Kubernetes Operator to expose any cloud service, such as an RDS database on a cluster network, to your tailnet. If you have a cloud service that is not publicly accessible but is accessible to a Kubernetes cluster on that cloud, you can make it available to your tailnet using an operator deployed in the cluster.\r\n\r\n## [Prerequisites](https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services\\#prerequisites)\r\n\r\n- [Set up the Kubernetes Operator](https://tailscale.com/docs/features/kubernetes-operator#setup).\r\n\r\n## [Expose a cloud service using a Kubernetes `ExternalName``Service`](https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services\\#expose-a-cloud-service-using-a-kubernetes-externalname-service)\r\n\r\nIf the cloud service that you wish to expose has a DNS name that can be resolved from within the cluster, you can expose it using an [ExternalName Service](https://kubernetes.io/docs/concepts/services-networking/service/#externalname).\r\n\r\nFor example, to expose an RDS database and connect to it from a tailnet client:\r\n\r\n1. Deploy Tailscale Kubernetes Operator to a Kubernetes cluster that is on the same network as the RDS instance.\r\n\r\nFollow the [installation instructions](https://tailscale.com/docs/features/kubernetes-operator#setup) to deploy the operator.\r\n\r\n2. Create an `ExternalName``Service` with `tailscale.com/expose: \"true\"` annotation and `spec.externalName` set to the DNS name of the RDS instance:\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n```yaml\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n     name: my-rds\r\n     annotations:\r\n       tailscale.com/expose: \"true\"\r\nspec:\r\n     type: ExternalName\r\n     externalName: my-rds.eu-central-1.rds.amazonaws.com\r\n```\r\n\r\n3. Retrieve the Tailscale MagicDNS name of the cluster proxy that the operator creates for the `Service` using the [`view-secret kubectl` plugin](https://github.com/elsesiy/kubectl-view-secret):\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n```shell\r\nrds_magic_dns_name=$(kubectl view-secret \\\r\n     $(kubectl get secret -n tailscale   \\\r\n     --selector tailscale.com/parent-resource=my-rds,tailscale.com/parent-resource-ns=default,tailscale.com/parent-resource-type=svc \\\r\n     -ojsonpath='{.items[0].metadata.name}') \\\r\n     -n tailscale \\\r\n     device_fqdn)\r\n```\r\n\r\n4. You can now connect to the RDS instance from a tailnet client using the MagicDNS name of the proxy as the database hostname.\r\n\r\nFor example, for a PostgreSQL database:\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n```shell\r\npsql -h ${rds_magic_dns_name} -U postgres\r\n```\r\n\r\n\r\nThe cluster proxies created for `ExternalName``Service`s forward TCP traffic, so you should be able to use them with different backend protocols, such as PostgreSQL.\r\n\r\nThe Tailscale Kubernetes Operator periodically (every 10 minutes) attempts to resolve the IP addresses of the backend cloud service and reconfigures the proxy rules, if needed.\r\n\r\nFor proxies deployed with [firewall in nftables mode](https://tailscale.com/docs/features/firewall-mode), the traffic will only be proxied to the first IP address that the DNS name resolves to.\r\n\r\n`ExternalName``Service`s support the same `tailscale.com` labels and annotations as other `Service`s.\r\n\r\nWe are actively seeking feedback about this feature — [reach out](https://github.com/tailscale/tailscale/issues/new/choose) if you would like it to support additional workflows.\r\n\r\n## [Expose a cloud service or services using `Connector`](https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services\\#expose-a-cloud-service-or-services-using-connector)\r\n\r\nIf the cloud service that you intend to expose does not have a DNS name that can be resolved from within a cluster, or you want to expose a whole CIDR range, you can do so using [Connector](https://tailscale.com/docs/features/kubernetes-operator/how-to/connector):\r\n\r\n```yaml\r\napiVersion: tailscale.com/v1alpha1\r\nkind: Connector\r\nmetadata:\r\n  name: my-rds-instances\r\nspec:\r\n  subnetRouter:\r\n    advertiseRoutes:\r\n      - \"<rds-cidr-range>\"\r\n```\r\n\r\nThe above `Connector` instance configures the operator to deploy an in-cluster [subnet router](https://tailscale.com/docs/features/subnet-routers) that exposes the configured CIDR range to your tailnet.\r\n\r\n## [Customization](https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services\\#customization)\r\n\r\n[Customize the operator and resources it manages](https://tailscale.com/docs/features/kubernetes-operator/how-to/customize).\r\n\r\n## [Troubleshooting](https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services\\#troubleshooting)\r\n\r\n[Troubleshoot the operator and resources it manages](https://tailscale.com/docs/reference/troubleshooting/containers/kubernetes-operator).\r\n\r\n![Project Logo](https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed)\r\n\r\nAsk AI\r\n\r\nreCAPTCHA\r\n\r\nRecaptcha requires verification.\r\n\r\nprotected by **reCAPTCHA**\r\n","html":"<h1>Expose a cloud service to your tailnet</h1>\n<p>Last validated: Jan 5, 2026</p>\n<p>Exposing cloud services to your tailnetis currently <a href=\"https://tailscale.com/docs/reference/tailscale-release-stages#beta\">in beta</a>.</p>\n<p>You can use the Tailscale Kubernetes Operator to expose any cloud service, such as an RDS database on a cluster network, to your tailnet. If you have a cloud service that is not publicly accessible but is accessible to a Kubernetes cluster on that cloud, you can make it available to your tailnet using an operator deployed in the cluster.</p>\n<h2><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services#prerequisites\">Prerequisites</a></h2>\n<ul>\n<li><a href=\"https://tailscale.com/docs/features/kubernetes-operator#setup\">Set up the Kubernetes Operator</a>.</li>\n</ul>\n<h2><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services#expose-a-cloud-service-using-a-kubernetes-externalname-service\">Expose a cloud service using a Kubernetes <code>ExternalName``Service</code></a></h2>\n<p>If the cloud service that you wish to expose has a DNS name that can be resolved from within the cluster, you can expose it using an <a href=\"https://kubernetes.io/docs/concepts/services-networking/service/#externalname\">ExternalName Service</a>.</p>\n<p>For example, to expose an RDS database and connect to it from a tailnet client:</p>\n<ol>\n<li>Deploy Tailscale Kubernetes Operator to a Kubernetes cluster that is on the same network as the RDS instance.</li>\n</ol>\n<p>Follow the <a href=\"https://tailscale.com/docs/features/kubernetes-operator#setup\">installation instructions</a> to deploy the operator.</p>\n<ol start=\"2\">\n<li>Create an <code>ExternalName``Service</code> with <code>tailscale.com/expose: \"true\"</code> annotation and <code>spec.externalName</code> set to the DNS name of the RDS instance:</li>\n</ol>\n<pre><code class=\"language-yaml\">apiVersion: v1\r\nkind: Service\r\nmetadata:\r\n     name: my-rds\r\n     annotations:\r\n       tailscale.com/expose: \"true\"\r\nspec:\r\n     type: ExternalName\r\n     externalName: my-rds.eu-central-1.rds.amazonaws.com\n</code></pre>\n<ol start=\"3\">\n<li>Retrieve the Tailscale MagicDNS name of the cluster proxy that the operator creates for the <code>Service</code> using the <a href=\"https://github.com/elsesiy/kubectl-view-secret\"><code>view-secret kubectl</code> plugin</a>:</li>\n</ol>\n<pre><code class=\"language-shell\">rds_magic_dns_name=$(kubectl view-secret \\\r\n     $(kubectl get secret -n tailscale   \\\r\n     --selector tailscale.com/parent-resource=my-rds,tailscale.com/parent-resource-ns=default,tailscale.com/parent-resource-type=svc \\\r\n     -ojsonpath='{.items[0].metadata.name}') \\\r\n     -n tailscale \\\r\n     device_fqdn)\n</code></pre>\n<ol start=\"4\">\n<li>You can now connect to the RDS instance from a tailnet client using the MagicDNS name of the proxy as the database hostname.</li>\n</ol>\n<p>For example, for a PostgreSQL database:</p>\n<pre><code class=\"language-shell\">psql -h ${rds_magic_dns_name} -U postgres\n</code></pre>\n<p>The cluster proxies created for <code>ExternalName``Service</code>s forward TCP traffic, so you should be able to use them with different backend protocols, such as PostgreSQL.</p>\n<p>The Tailscale Kubernetes Operator periodically (every 10 minutes) attempts to resolve the IP addresses of the backend cloud service and reconfigures the proxy rules, if needed.</p>\n<p>For proxies deployed with <a href=\"https://tailscale.com/docs/features/firewall-mode\">firewall in nftables mode</a>, the traffic will only be proxied to the first IP address that the DNS name resolves to.</p>\n<p><code>ExternalName``Service</code>s support the same <code>tailscale.com</code> labels and annotations as other <code>Service</code>s.</p>\n<p>We are actively seeking feedback about this feature — <a href=\"https://github.com/tailscale/tailscale/issues/new/choose\">reach out</a> if you would like it to support additional workflows.</p>\n<h2><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services#expose-a-cloud-service-or-services-using-connector\">Expose a cloud service or services using <code>Connector</code></a></h2>\n<p>If the cloud service that you intend to expose does not have a DNS name that can be resolved from within a cluster, or you want to expose a whole CIDR range, you can do so using <a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/connector\">Connector</a>:</p>\n<pre><code class=\"language-yaml\">apiVersion: tailscale.com/v1alpha1\r\nkind: Connector\r\nmetadata:\r\n  name: my-rds-instances\r\nspec:\r\n  subnetRouter:\r\n    advertiseRoutes:\r\n      - \"&#x3C;rds-cidr-range>\"\n</code></pre>\n<p>The above <code>Connector</code> instance configures the operator to deploy an in-cluster <a href=\"https://tailscale.com/docs/features/subnet-routers\">subnet router</a> that exposes the configured CIDR range to your tailnet.</p>\n<h2><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services#customization\">Customization</a></h2>\n<p><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/customize\">Customize the operator and resources it manages</a>.</p>\n<h2><a href=\"https://tailscale.com/docs/features/kubernetes-operator/how-to/cloud-services#troubleshooting\">Troubleshooting</a></h2>\n<p><a href=\"https://tailscale.com/docs/reference/troubleshooting/containers/kubernetes-operator\">Troubleshoot the operator and resources it manages</a>.</p>\n<p><img src=\"https://cdn.brandfetch.io/tailscale.com/fallback/lettermark/theme/dark/h/256/w/256/icon?c=1bfwsmEH20zzEfSNTed\" alt=\"Project Logo\"></p>\n<p>Ask AI</p>\n<p>reCAPTCHA</p>\n<p>Recaptcha requires verification.</p>\n<p>protected by <strong>reCAPTCHA</strong></p>\n"}