Get started
WireGuard is a registered trademark of Jason A. Donenfeld.
© 2024 Tailscale Inc. All rights reserved. Tailscale is a registered trademark of Tailscale Inc.
Go back

Private go links for your tailnet

November 30 2022
Will Norris
Will Norris

Today, we’re sharing golink, an open source private URL shortener service for tailnets. Using golink, you can create and share simple go/name links for commonly accessed websites, so that anyone in your network can access them no matter the device they’re on — without requiring browser extensions or fiddling with DNS settings. And because golink integrates with Tailscale, links are private to users in your tailnet without any separate user management, logins, or security policies.

A screenshot of the golink application homepage. A form allows a new link to be created and popular links are listed: go/meet, go/slack, go/search, go/email

The first go link service was an internal tool built at Google around 2006, and was eventually copied by dozens of other tech companies (as good ideas have a tendency to be). These go link services are a convenient way to provide memorable links to commonly used resources like design docs (go/foo-design), monitoring dashboards (go/mon), or the menu at your favorite lunch spot (go/lunch). Rather than bookmarking or trying to remember the URL, you can just type go/lunch in your browser and it takes you right to the page. Because the data model is so simple (just a key-value store in the simplest form), it’s a popular type of service to build as a weekend project, right up there with to-do lists and Hacker News clients. There are thousands of open source URL shorteners, and even multiple startups providing go links as a service. So why did we bother building a new one?

Tailscale’s golink service started in the first few hours of my first day of work. Brad had just built a simple internal company directory called “who” (inspired by another Google tool), and I immediately asked where “go” was. Within the hour, Brad had the first version of golink that stored link metadata in simple JSON files on disk running locally and serving links for the company. Even though I had been using Tailscale at home for months, as I started using and working on golink, a few unique things about building services on top of Tailscale became more apparent:

  • Applications can really run anywhere. golink ran on a developer laptop for two days before moving to a production environment, and no one could tell the difference. Even the transition to production was as trivial as renaming two nodes.
  • tsnet allows an application to join your tailnet directly, which simplifies deployment. golink is a single static go binary that can run practically anywhere. It doesn’t require a Tailscale client to be installed and doesn’t require root access, since tsnet manages its own Tailscale connection.
  • MagicDNS makes it possible to host golink at the short URL http://go/ without needing any browser extensions or DNS hacks to resolve the host name. It just works, even on mobile devices.
  • Because Tailscale authenticates all peer connections, users are automatically authenticated without needing to do anything. There is no separate identity provider, no protocols to implement, and no user database.

By running golink on your tailnet, Tailscale doesn’t see the links you create or the content of the links. Your DNS queries pass through your device’s local Tailscale DNS proxy, but they are not logged.

Just enough features

After completing the initial prototype, we migrated the JSON files to sqlite (as we do) and set out to add just enough additional features to make golink useful but not too heavy. I drew some inspiration from the go link service at Twitter, adding support for getting link metadata by appending a “+” to a URL: go/name will resolve the link, but go/name+ just serves information about the link without resolving it. We made links case insensitive and ignored dashes, eliminating a common problem in some implementations where go/enghandbook and go/eng-handbook might resolve to different destinations. We kept a simple ownership model where links can only be updated by their owner, ownership can be transferred simply by editing the link, and orphaned links can be taken over by anyone on the tailnet. We track basic stats so that the golink homepage can list popular links to help with discovery.

But my favorite feature by far is templated links. It’s pretty common for go links to have any remaining path segments after the link be appended to the destination. So, for example, if go/who redirects to http://who/, then go/who/amelie would go to http://who/amelie. But what if you don’t simply want to append the remaining path, and instead include it in a query parameter? golink solves this by using go templates for all destinations links. We provide a simple data object containing the remaining path data, as well as some basic functions for escaping values. Seeing templates inside of URLs looks a little unusual at first, but you can quickly see how incredibly powerful it is.

Here are some of my favorite go links that we use at Tailscale, and the destination link templates:

  • go/prs: Without a path, this list GitHub pull requests in the Tailscale organization that are awaiting my review. With a path (like go/prs/bradfitz), it shows pull requests awaiting review by the specified GitHub user.{{with .Path}}{{QueryEscape .}}{{else}}@me{{end}}+archived:false+org:tailscale
  • go/cs: This uses the path to perform a code search for the specified term in all GitHub repositories in the Tailscale organization.{{with .Path}}+{{QueryEscape .}}{{end}}
  • go/code: Given a code path, such as go/code/cmd/tailscale, this link opens that path in the main Tailscale GitHub repository.{{with .Path}}/tree/main/{{PathEscape .}}{{end}}
  • go/meet: With an extra path, this creates impromptu Google Meet meetings like go/meet/breakroom. (This is actually our most used link at Tailscale by a 2-to-1 margin!)
  • go/cal or go/c: Without a path, this opens Google Calendar. With a path, it opens a given Tailscalar’s calendar.{{with .Path}}calendar/embed?mode=week&src={{.}}{{end}}
  • go/slack: Without a path, this opens Tailscale’s Slack. With a path, it opens a specific Slack channel.{{with .Path}}channels/{{PathEscape .}}{{end}}
  • go/wwwpr: Given a pull request number for the GitHub repository, this opens the automatically provisioned staging site to preview the change. Note that this uses the path to modify the destination hostname rather than the path or query string.

    {{if .Path}}https://website-preview-{{.Path}}{{end}}
  • go/go/gadget: Okay, this is really two links. go/go is a recursive link to http://go/, and go/gadget redirects to

We also use go links to link to monitoring dashboards, specific reports on metrics, design docs, popular blog posts or references, our internal tools (like our support ticketing system, or our HR system), production runbooks, frequently rerun CI/CD jobs, our product roadmap, and — of course — a long-form document on all the ways DNS is broken (go/how-dns-works).

golink is still experimental, but we’ve been using it daily for the last six months and have been quite happy with it. To start using go links on your tailnet, head to

Subscribe to Tailscale’s blog

We have a deep commitment to keeping your data safe.

Too much email?RSSX

Try Tailscale for free

Schedule a demo
Contact sales
cta phone
Hugging Face