{"slug":"tsnet","title":"tsnet","tags":["tailscale"],"agent_summary":"Last validated: Jan 27, 2026","trigger_phrases":[],"runnable":false,"markdown":"\r\n# tsnet\r\n\r\nLast validated: Jan 27, 2026\r\n\r\n`tsnet` is a library that lets you embed Tailscale inside a Go program. With `tsnet`, you can programmatically make direct connections to devices on your Tailscale network (known as a tailnet), just like any other device in your tailnet would. When combined with other features of Tailscale, you can create new and interesting ways to use your tailnet.\r\n\r\n## [Overview](https://tailscale.com/docs/features/tsnet\\#overview)\r\n\r\nTypically, every IP address in your tailnet has to be strongly associated with a single device. This makes it difficult to run multiple services on a single device, especially if all of those services have different [access control](https://tailscale.com/docs/features/access-control) rules. `tsnet` lets you run multiple services on the same device with different IP addresses, access control rules, and even user identities.\r\n\r\n`tsnet` uses a [userspace](https://tailscale.com/docs/concepts/userspace-networking) TCP/IP networking stack.\r\n\r\nInside Tailscale, we built and constantly use tools on top of `tsnet`. `tsnet` powers our internal URL shortener [golink](https://github.com/tailscale/golink). It powers the support tooling that our support team uses daily to help you resolve problems. Without `tsnet`, our Tailscale-enabled tools would be far more difficult to create and maintain.\r\n\r\nExternally, people have used `tsnet` to provide metrics, deliver administrative endpoints, and create other functionality. For examples, visit the Go [`tsnet` known importers](https://pkg.go.dev/tailscale.com/tsnet?tab=importedby) page.\r\n\r\n## [Include `tsnet` in your program](https://tailscale.com/docs/features/tsnet\\#include-tsnet-in-your-program)\r\n\r\nRun the following command to add `tsnet` to your dependencies and make it available for use in your Go program:\r\n\r\n```shell\r\ngo get tailscale.com/tsnet\r\n```\r\n\r\nIn your Go program, add the `tailscale.com/tsnet` package to the list of imports:\r\n\r\n```go\r\npackage main\r\n\r\nimport (\r\n    \"flag\"\r\n    \"fmt\"\r\n    \"html\"\r\n    \"log\"\r\n    \"net/http\"\r\n    \"strings\"\r\n\r\n    \"tailscale.com/tsnet\"\r\n)\r\n```\r\n\r\n## [Make calls with `tsnet.Server`](https://tailscale.com/docs/features/tsnet\\#make-calls-with-tsnetserver)\r\n\r\nThe [`tsnet.Server`](https://tailscale.com/docs/reference/tsnet-server-api) object provides the entry point for your program to connect with other devices in your tailnet. Initialize `tsnet.Server` by specifying an address to listen on and a hostname for the device that will be created.\r\n\r\n```go\r\nvar (\r\n\taddr     = flag.String(\"addr\", \":80\", \"address to listen on\")\r\n\thostname = flag.String(\"hostname\", \"tshello\", \"hostname to use in the tailnet\")\r\n)\r\n\r\nfunc main() {\r\n\tflag.Parse()\r\n\tsrv := new(tsnet.Server)\r\n\tsrv.Addr = *addr\r\n\tsrv.Hostname = *hostname\r\n\tif err := srv.Start(); err != nil {\r\n\t\tlog.Fatalf(\"can't start tsnet server: %v\", err)\r\n\t}\r\n\tdefer srv.Close()\r\n\r\n\t// Use the tsnet.Server object to interact with your tailnet\r\n    ...\r\n}\r\n```\r\n\r\n## [Device creation and authentication](https://tailscale.com/docs/features/tsnet\\#device-creation-and-authentication)\r\n\r\nIf you are not using an [auth key](https://tailscale.com/docs/features/access-control/auth-keys), or [trust credentials](https://tailscale.com/docs/reference/trust-credentials), the call to the [`Server.Start`](https://tailscale.com/docs/reference/tsnet-server-api#serverstart) function will result in creation and display of a Tailscale authentication URL. Use the URL to log in to Tailscale, same as you would for other devices in your tailnet. If [device approval](https://tailscale.com/docs/features/access-control/device-management/device-approval) is enabled for the tailnet, a tailnet admin will need to [approve the device](https://tailscale.com/docs/features/access-control/device-management/device-approval#approve-devices-from-the-admin-console).\r\n\r\nAs an alternative to logging in by using a Tailscale authentication URL, you can use an auth key as part of your `tsnet.Server` initialization. This lets you [pre-approve a device](https://tailscale.com/docs/features/access-control/device-management/device-approval#pre-approve-devices-with-an-auth-key).\r\n\r\nThe authentication process can occur even if `Server.Start` itself is not directly called in your code, because some `Server` functions will implicitly call `Server.Start`if it has not yet been called.\r\n\r\n## [Additional information](https://tailscale.com/docs/features/tsnet\\#additional-information)\r\n\r\n- For more information about the `tsnet.Server` functions, refer to [`tsnet.Server`](https://tailscale.com/docs/reference/tsnet-server-api).\r\n- For a complete example that uses `tsnet`, refer to [Hello tsnet](https://tailscale.com/docs/features/tsnet/how-to/create-basic-tsnet-app).\r\n- For a complete example that uses `Server.ListenService` and `ServiceModeHTTP` with Tailscale Services, refer to [`tsnet` and Tailscale Services](https://tailscale.com/docs/features/tsnet/how-to/register-service).\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>tsnet</h1>\n<p>Last validated: Jan 27, 2026</p>\n<p><code>tsnet</code> is a library that lets you embed Tailscale inside a Go program. With <code>tsnet</code>, you can programmatically make direct connections to devices on your Tailscale network (known as a tailnet), just like any other device in your tailnet would. When combined with other features of Tailscale, you can create new and interesting ways to use your tailnet.</p>\n<h2><a href=\"https://tailscale.com/docs/features/tsnet#overview\">Overview</a></h2>\n<p>Typically, every IP address in your tailnet has to be strongly associated with a single device. This makes it difficult to run multiple services on a single device, especially if all of those services have different <a href=\"https://tailscale.com/docs/features/access-control\">access control</a> rules. <code>tsnet</code> lets you run multiple services on the same device with different IP addresses, access control rules, and even user identities.</p>\n<p><code>tsnet</code> uses a <a href=\"https://tailscale.com/docs/concepts/userspace-networking\">userspace</a> TCP/IP networking stack.</p>\n<p>Inside Tailscale, we built and constantly use tools on top of <code>tsnet</code>. <code>tsnet</code> powers our internal URL shortener <a href=\"https://github.com/tailscale/golink\">golink</a>. It powers the support tooling that our support team uses daily to help you resolve problems. Without <code>tsnet</code>, our Tailscale-enabled tools would be far more difficult to create and maintain.</p>\n<p>Externally, people have used <code>tsnet</code> to provide metrics, deliver administrative endpoints, and create other functionality. For examples, visit the Go <a href=\"https://pkg.go.dev/tailscale.com/tsnet?tab=importedby\"><code>tsnet</code> known importers</a> page.</p>\n<h2><a href=\"https://tailscale.com/docs/features/tsnet#include-tsnet-in-your-program\">Include <code>tsnet</code> in your program</a></h2>\n<p>Run the following command to add <code>tsnet</code> to your dependencies and make it available for use in your Go program:</p>\n<pre><code class=\"language-shell\">go get tailscale.com/tsnet\n</code></pre>\n<p>In your Go program, add the <code>tailscale.com/tsnet</code> package to the list of imports:</p>\n<pre><code class=\"language-go\">package main\r\n\r\nimport (\r\n    \"flag\"\r\n    \"fmt\"\r\n    \"html\"\r\n    \"log\"\r\n    \"net/http\"\r\n    \"strings\"\r\n\r\n    \"tailscale.com/tsnet\"\r\n)\n</code></pre>\n<h2><a href=\"https://tailscale.com/docs/features/tsnet#make-calls-with-tsnetserver\">Make calls with <code>tsnet.Server</code></a></h2>\n<p>The <a href=\"https://tailscale.com/docs/reference/tsnet-server-api\"><code>tsnet.Server</code></a> object provides the entry point for your program to connect with other devices in your tailnet. Initialize <code>tsnet.Server</code> by specifying an address to listen on and a hostname for the device that will be created.</p>\n<pre><code class=\"language-go\">var (\r\n\taddr     = flag.String(\"addr\", \":80\", \"address to listen on\")\r\n\thostname = flag.String(\"hostname\", \"tshello\", \"hostname to use in the tailnet\")\r\n)\r\n\r\nfunc main() {\r\n\tflag.Parse()\r\n\tsrv := new(tsnet.Server)\r\n\tsrv.Addr = *addr\r\n\tsrv.Hostname = *hostname\r\n\tif err := srv.Start(); err != nil {\r\n\t\tlog.Fatalf(\"can't start tsnet server: %v\", err)\r\n\t}\r\n\tdefer srv.Close()\r\n\r\n\t// Use the tsnet.Server object to interact with your tailnet\r\n    ...\r\n}\n</code></pre>\n<h2><a href=\"https://tailscale.com/docs/features/tsnet#device-creation-and-authentication\">Device creation and authentication</a></h2>\n<p>If you are not using an <a href=\"https://tailscale.com/docs/features/access-control/auth-keys\">auth key</a>, or <a href=\"https://tailscale.com/docs/reference/trust-credentials\">trust credentials</a>, the call to the <a href=\"https://tailscale.com/docs/reference/tsnet-server-api#serverstart\"><code>Server.Start</code></a> function will result in creation and display of a Tailscale authentication URL. Use the URL to log in to Tailscale, same as you would for other devices in your tailnet. If <a href=\"https://tailscale.com/docs/features/access-control/device-management/device-approval\">device approval</a> is enabled for the tailnet, a tailnet admin will need to <a href=\"https://tailscale.com/docs/features/access-control/device-management/device-approval#approve-devices-from-the-admin-console\">approve the device</a>.</p>\n<p>As an alternative to logging in by using a Tailscale authentication URL, you can use an auth key as part of your <code>tsnet.Server</code> initialization. This lets you <a href=\"https://tailscale.com/docs/features/access-control/device-management/device-approval#pre-approve-devices-with-an-auth-key\">pre-approve a device</a>.</p>\n<p>The authentication process can occur even if <code>Server.Start</code> itself is not directly called in your code, because some <code>Server</code> functions will implicitly call <code>Server.Start</code>if it has not yet been called.</p>\n<h2><a href=\"https://tailscale.com/docs/features/tsnet#additional-information\">Additional information</a></h2>\n<ul>\n<li>For more information about the <code>tsnet.Server</code> functions, refer to <a href=\"https://tailscale.com/docs/reference/tsnet-server-api\"><code>tsnet.Server</code></a>.</li>\n<li>For a complete example that uses <code>tsnet</code>, refer to <a href=\"https://tailscale.com/docs/features/tsnet/how-to/create-basic-tsnet-app\">Hello tsnet</a>.</li>\n<li>For a complete example that uses <code>Server.ListenService</code> and <code>ServiceModeHTTP</code> with Tailscale Services, refer to <a href=\"https://tailscale.com/docs/features/tsnet/how-to/register-service\"><code>tsnet</code> and Tailscale Services</a>.</li>\n</ul>\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"}