{"slug":"access-google-compute-engine-vms-privately-using-tailscale","title":"Access Google Compute Engine VMs privately using Tailscale","tags":["tailscale","access-control","setup"],"agent_summary":"Last validated: Dec 4, 2025","trigger_phrases":[],"runnable":false,"markdown":"\r\n# Access Google Compute Engine VMs privately using Tailscale\r\n\r\nLast validated: Dec 4, 2025\r\n\r\nGoogle Cloud provides Linux virtual machines and you can use Tailscale to provide secure connectivity.\r\n\r\n## [Prerequisites](https://tailscale.com/docs/install/cloud/gce\\#prerequisites)\r\n\r\nBefore you begin this guide, you'll need a Tailscale network (known as a tailnet) set up and configured with at least one existing device. Read our [getting started guide](https://tailscale.com/docs/how-to/quickstart) if you need help with this.\r\n\r\n## [Step 1: Set up the Tailscale client for the VM](https://tailscale.com/docs/install/cloud/gce\\#step-1-set-up-the-tailscale-client-for-the-vm)\r\n\r\nFirst, [create a Virtual Machine in the GCE Console](https://cloud.google.com/compute/docs/instances/create-start-instance).\r\n\r\nWhen creating the instance select **Management, security, disks, networking, sole tenancy**, select **Networking**, and select the **Network Interface**. Because we're later going to enable subnet routing on this VM, we want to set **IP forwarding** to **On**.\r\n\r\n![The Network Interface configurations featuring the IP forwarding setting.](https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-ip-forwarding.1b910e77.png&w=1920&q=75)\r\n\r\nOnce you create the VM, SSH to the system and follow the steps to [install Tailscale on Linux](https://tailscale.com/docs/install/linux).\r\n\r\n## [Step 2: Allow UDP port 41641](https://tailscale.com/docs/install/cloud/gce\\#step-2-allow-udp-port-41641)\r\n\r\nIf at least one side of a tunnel has \"easy NAT,\" where Tailscale can determine the UDP port number on the far side of the NAT device, then it will make [direct connections to minimize latency.](https://tailscale.com/blog/how-tailscale-works). We ensure that GCE nodes can make direct connections by allowing UDP port `41641` to ingress through the firewall.\r\n\r\nIn **VPC Network** \\> **Firewall** we add two rules:\r\n\r\n1. An ingress rule to allow `0.0.0.0/0` for UDP port `41641` to all instances.\r\n2. An ingress rule to allow `::/0` for UDP port `41641` to all instances.\r\n\r\n![The firewall rule for an example project featuring Direction of traffic set to Ingress and Protocols and ports set to 41641.](https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-firewall-rule.d7563267.png&w=750&q=75)\r\n\r\n## [Step 3: Advertise routes from the VM](https://tailscale.com/docs/install/cloud/gce\\#step-3-advertise-routes-from-the-vm)\r\n\r\nTo enable connections from your tailnet to the GCP subnet, configure the VM to act as a [subnet router](https://tailscale.com/docs/features/subnet-routers).\r\n\r\nFirst, enable IP forwarding on the VM.\r\n\r\nIf your Linux system has a `/etc/sysctl.d` directory, use:\r\n\r\n```shell\r\necho 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf\r\necho 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf\r\nsudo sysctl -p /etc/sysctl.d/99-tailscale.conf\r\n```\r\n\r\nOtherwise, use:\r\n\r\n```shell\r\necho 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf\r\necho 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf\r\nsudo sysctl -p /etc/sysctl.conf\r\n```\r\n\r\nAfter enabling IP forwarding, configure the VM to advertise routes for the subnet it sits on. For example, if the subnet address range is `10.182.0.0/24`, the command would be:\r\n\r\n```shell\r\ntailscale set --advertise-routes=10.182.0.0/24 --accept-dns=false\r\n```\r\n\r\nFor GCE VMs it is generally best to let Google handle the DNS configuration, not have Tailscale override it, so we added `--accept-dns=false`.\r\n\r\n## [Step 4: Add GCE DNS for your tailnet](https://tailscale.com/docs/install/cloud/gce\\#step-4-add-gce-dns-for-your-tailnet)\r\n\r\nFor the benefit of the _other_ nodes in the tailnet we'll set up [split DNS](https://tailscale.com/docs/reference/dns-in-tailscale#tailscale-dns-settings) to allow use of the same DNS names as the ones that are inside of GCE.\r\n\r\nThe hostnames inside of GCE are of the form:\r\n\r\n```shell\r\n<vm-name>.<gce-project-name>.internal\r\n```\r\n\r\nUse the Google Cloud CLI command [`gcloud dns policies create`](https://cloud.google.com/sdk/gcloud/reference/dns/policies/create) to create a new [Cloud DNS](https://cloud.google.com/dns) policy that lets inbound forwarding for your tailnet:\r\n\r\n```shell\r\ngcloud dns policies create inbound-dns \\\r\n  --project=\"YOUR_VPC_PROJECT\" \\\r\n  --description=\"Expose DNS endpoints per subnet\" \\\r\n  --networks=\"YOUR_VPC\" \\\r\n  --enable-inbound-forwarding\r\n```\r\n\r\nwhere:\r\n\r\n- `YOUR_VPC_PROJECT` is your Google Cloud [project ID](https://cloud.google.com/sdk/gcloud/reference#--project).\r\n- `YOUR_VPC` is the comma separated list of network names to associate with the policy.\r\n\r\nUse the [`gcloud compute addresses list`](https://cloud.google.com/sdk/gcloud/reference/compute/addresses/list) to verify that your tailnet recognizes the DNS resolver for your tailnet subnet:\r\n\r\n```shell\r\ngcloud compute addresses list \\\r\n  --project=\"YOUR_VPC_PROJECT\" \\\r\n  --filter='purpose=\"DNS_RESOLVER\"' \\\r\n  --format='csv(address, region, subnetwork)' \\\r\n  | grep YOUR_TAILNET_SUBNET\r\n```\r\n\r\nwhere:\r\n\r\n- `YOUR_VPC_PROJECT` is your Google Cloud [project ID](https://cloud.google.com/sdk/gcloud/reference#--project).\r\n- `YOUR_TAILNET_SUBNET` is your subnet machine name.\r\n\r\nUse the IP address returned from this command as a DNS resolver for your tailnet:\r\n\r\n1. Open the [DNS](https://login.tailscale.com/admin/dns) page in the admin console.\r\n\r\n2. Select **Add name server**.\r\n\r\n3. Select **Custom**.\r\n\r\n4. For **Nameserver**, enter the IP address from the `gcloud compute addresses list` command that you ran above. In this example, we use `10.243.117.59`.\r\n\r\n5. Ensure **Restrict to search domain** is checked.\r\n\r\n6. For **Search Domain**, enter **internal**.\r\n\r\n7. Select **Save**.\r\n![The Add nameserver configuration with an IP address and Search Domain set to internal.](https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-add-nameserver.4bd56bd3.png&w=3840&q=75)\r\n\r\nNow the same hostnames which work between nodes running within GCE will also be available to all nodes in your tailnet.\r\n\r\n## [Step 5: Remove public SSH access](https://tailscale.com/docs/install/cloud/gce\\#step-5-remove-public-ssh-access)\r\n\r\nAs we can now SSH to the system over the private Tailscale network, there is no reason to leave the SSH port open on a public IP address. You can delete the `default-allow-ssh` rule from **VPC network** \\> **Firewall**.\r\n\r\n![The firewall rule details for an example project highlighting a the Delete command in the top right.](https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-remove-ssh.fc7a024a.png&w=3840&q=75)\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>Access Google Compute Engine VMs privately using Tailscale</h1>\n<p>Last validated: Dec 4, 2025</p>\n<p>Google Cloud provides Linux virtual machines and you can use Tailscale to provide secure connectivity.</p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#prerequisites\">Prerequisites</a></h2>\n<p>Before you begin this guide, you'll need a Tailscale network (known as a tailnet) set up and configured with at least one existing device. Read our <a href=\"https://tailscale.com/docs/how-to/quickstart\">getting started guide</a> if you need help with this.</p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#step-1-set-up-the-tailscale-client-for-the-vm\">Step 1: Set up the Tailscale client for the VM</a></h2>\n<p>First, <a href=\"https://cloud.google.com/compute/docs/instances/create-start-instance\">create a Virtual Machine in the GCE Console</a>.</p>\n<p>When creating the instance select <strong>Management, security, disks, networking, sole tenancy</strong>, select <strong>Networking</strong>, and select the <strong>Network Interface</strong>. Because we're later going to enable subnet routing on this VM, we want to set <strong>IP forwarding</strong> to <strong>On</strong>.</p>\n<p><img src=\"https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-ip-forwarding.1b910e77.png&#x26;w=1920&#x26;q=75\" alt=\"The Network Interface configurations featuring the IP forwarding setting.\"></p>\n<p>Once you create the VM, SSH to the system and follow the steps to <a href=\"https://tailscale.com/docs/install/linux\">install Tailscale on Linux</a>.</p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#step-2-allow-udp-port-41641\">Step 2: Allow UDP port 41641</a></h2>\n<p>If at least one side of a tunnel has \"easy NAT,\" where Tailscale can determine the UDP port number on the far side of the NAT device, then it will make <a href=\"https://tailscale.com/blog/how-tailscale-works\">direct connections to minimize latency.</a>. We ensure that GCE nodes can make direct connections by allowing UDP port <code>41641</code> to ingress through the firewall.</p>\n<p>In <strong>VPC Network</strong> > <strong>Firewall</strong> we add two rules:</p>\n<ol>\n<li>An ingress rule to allow <code>0.0.0.0/0</code> for UDP port <code>41641</code> to all instances.</li>\n<li>An ingress rule to allow <code>::/0</code> for UDP port <code>41641</code> to all instances.</li>\n</ol>\n<p><img src=\"https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-firewall-rule.d7563267.png&#x26;w=750&#x26;q=75\" alt=\"The firewall rule for an example project featuring Direction of traffic set to Ingress and Protocols and ports set to 41641.\"></p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#step-3-advertise-routes-from-the-vm\">Step 3: Advertise routes from the VM</a></h2>\n<p>To enable connections from your tailnet to the GCP subnet, configure the VM to act as a <a href=\"https://tailscale.com/docs/features/subnet-routers\">subnet router</a>.</p>\n<p>First, enable IP forwarding on the VM.</p>\n<p>If your Linux system has a <code>/etc/sysctl.d</code> directory, use:</p>\n<pre><code class=\"language-shell\">echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf\r\necho 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.d/99-tailscale.conf\r\nsudo sysctl -p /etc/sysctl.d/99-tailscale.conf\n</code></pre>\n<p>Otherwise, use:</p>\n<pre><code class=\"language-shell\">echo 'net.ipv4.ip_forward = 1' | sudo tee -a /etc/sysctl.conf\r\necho 'net.ipv6.conf.all.forwarding = 1' | sudo tee -a /etc/sysctl.conf\r\nsudo sysctl -p /etc/sysctl.conf\n</code></pre>\n<p>After enabling IP forwarding, configure the VM to advertise routes for the subnet it sits on. For example, if the subnet address range is <code>10.182.0.0/24</code>, the command would be:</p>\n<pre><code class=\"language-shell\">tailscale set --advertise-routes=10.182.0.0/24 --accept-dns=false\n</code></pre>\n<p>For GCE VMs it is generally best to let Google handle the DNS configuration, not have Tailscale override it, so we added <code>--accept-dns=false</code>.</p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#step-4-add-gce-dns-for-your-tailnet\">Step 4: Add GCE DNS for your tailnet</a></h2>\n<p>For the benefit of the <em>other</em> nodes in the tailnet we'll set up <a href=\"https://tailscale.com/docs/reference/dns-in-tailscale#tailscale-dns-settings\">split DNS</a> to allow use of the same DNS names as the ones that are inside of GCE.</p>\n<p>The hostnames inside of GCE are of the form:</p>\n<pre><code class=\"language-shell\">&#x3C;vm-name>.&#x3C;gce-project-name>.internal\n</code></pre>\n<p>Use the Google Cloud CLI command <a href=\"https://cloud.google.com/sdk/gcloud/reference/dns/policies/create\"><code>gcloud dns policies create</code></a> to create a new <a href=\"https://cloud.google.com/dns\">Cloud DNS</a> policy that lets inbound forwarding for your tailnet:</p>\n<pre><code class=\"language-shell\">gcloud dns policies create inbound-dns \\\r\n  --project=\"YOUR_VPC_PROJECT\" \\\r\n  --description=\"Expose DNS endpoints per subnet\" \\\r\n  --networks=\"YOUR_VPC\" \\\r\n  --enable-inbound-forwarding\n</code></pre>\n<p>where:</p>\n<ul>\n<li><code>YOUR_VPC_PROJECT</code> is your Google Cloud <a href=\"https://cloud.google.com/sdk/gcloud/reference#--project\">project ID</a>.</li>\n<li><code>YOUR_VPC</code> is the comma separated list of network names to associate with the policy.</li>\n</ul>\n<p>Use the <a href=\"https://cloud.google.com/sdk/gcloud/reference/compute/addresses/list\"><code>gcloud compute addresses list</code></a> to verify that your tailnet recognizes the DNS resolver for your tailnet subnet:</p>\n<pre><code class=\"language-shell\">gcloud compute addresses list \\\r\n  --project=\"YOUR_VPC_PROJECT\" \\\r\n  --filter='purpose=\"DNS_RESOLVER\"' \\\r\n  --format='csv(address, region, subnetwork)' \\\r\n  | grep YOUR_TAILNET_SUBNET\n</code></pre>\n<p>where:</p>\n<ul>\n<li><code>YOUR_VPC_PROJECT</code> is your Google Cloud <a href=\"https://cloud.google.com/sdk/gcloud/reference#--project\">project ID</a>.</li>\n<li><code>YOUR_TAILNET_SUBNET</code> is your subnet machine name.</li>\n</ul>\n<p>Use the IP address returned from this command as a DNS resolver for your tailnet:</p>\n<ol>\n<li>\n<p>Open the <a href=\"https://login.tailscale.com/admin/dns\">DNS</a> page in the admin console.</p>\n</li>\n<li>\n<p>Select <strong>Add name server</strong>.</p>\n</li>\n<li>\n<p>Select <strong>Custom</strong>.</p>\n</li>\n<li>\n<p>For <strong>Nameserver</strong>, enter the IP address from the <code>gcloud compute addresses list</code> command that you ran above. In this example, we use <code>10.243.117.59</code>.</p>\n</li>\n<li>\n<p>Ensure <strong>Restrict to search domain</strong> is checked.</p>\n</li>\n<li>\n<p>For <strong>Search Domain</strong>, enter <strong>internal</strong>.</p>\n</li>\n<li>\n<p>Select <strong>Save</strong>.\r\n<img src=\"https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-add-nameserver.4bd56bd3.png&#x26;w=3840&#x26;q=75\" alt=\"The Add nameserver configuration with an IP address and Search Domain set to internal.\"></p>\n</li>\n</ol>\n<p>Now the same hostnames which work between nodes running within GCE will also be available to all nodes in your tailnet.</p>\n<h2><a href=\"https://tailscale.com/docs/install/cloud/gce#step-5-remove-public-ssh-access\">Step 5: Remove public SSH access</a></h2>\n<p>As we can now SSH to the system over the private Tailscale network, there is no reason to leave the SSH port open on a public IP address. You can delete the <code>default-allow-ssh</code> rule from <strong>VPC network</strong> > <strong>Firewall</strong>.</p>\n<p><img src=\"https://tailscale.com/_next/image?url=%2F_next%2Fstatic%2Fmedia%2Fgce-remove-ssh.fc7a024a.png&#x26;w=3840&#x26;q=75\" alt=\"The firewall rule details for an example project highlighting a the Delete command in the top right.\"></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"}