Runway
Runway is built and operated in the EU!

Tagging

Tagging lets you group devices and use Tailscale’s powerful access control features.

Runway specifically supports a TS_TAG variable, but before you get started, you need to run through these steps:

Define the tag in Tailscale

Go on your Tailscale admin console and create a tag. Unless you have previously created a policy, Tailscale’s access control policy editor will contain defaults.

In order to create a tag (e.g. runway), add the following to your policy:

{
  "tagOwners": {
    "tag:runway": ["email@example.org"],
  }
}

The email is your account/email address on Tailscale.

Use the tag for your Runway app

Use TS_TAG=tag-name. For example, for a tag:runway do:

runway app config set TS_AUTHKEY=tskey-... TS_TAG=runway

Follow up with a runway app restart and your app (“device”/“node” in Tailscale) will now be tagged.

Make sure the tag you are using actually exists in Tailscale. Do not include the tag: prefix in the TS_TAG value. If the app doesn’t re-connect to Tailscale after you set the TS_TAG, check your runway app logs (which include Tailscale’s error messages).

Optionally, you can also create an authorization key and select the tag with it. This encodes the tag into the key and will ensure that everything authenticated with that key has the tag.

You will still need to set TS_TAG to the tag in your Runway app in this case.

Control Access

Tags have the advantage that we can build powerful access controls. All that without having to remember an IP address ever again - this is what Tailscale does for us. Check out the example below!

Imagine you have a local database setup and some LLMs running — all tagged with homelab. You want to grant access to these services to your friends (and Runway!):

{
  "groups": {
    "group:me":      ["till"],
    "group:friends": ["dennis", "richard"],
  },
  "tagOwners": {
    "tag:private": ["group:me"],
    "tag:homelab": ["group:me"],
    "tag:runway":  ["group:friends"],
  },
  "grants": [
    {"src": ["tag:homelab"], "dst": ["tag:runway"], "ip": ["*"]},
    {"src": ["tag:runway"], "dst": ["tag:homelab"], "ip": ["*"]},
    {"src": ["group:friends"], "dst": ["tag:homelab", "tag:runway"], "ip": ["*"]},
    {"src": ["group:me"], "dst": ["*"], "ip": ["*"]}
  ],
  "tests": [
    {
      "src":    "group:me",
      "accept": ["tag:runway:5000", "tag:homelab:8080"],
    },
    {
      "src":  "group:friends",
      "deny": ["tag:private:1337"],
    },
    {
      "src":  "tag:runway",
      "deny": ["tag:private:1337"],
    }
  ],
}

If you noticed, we also included tests which validate our assumptions about the grants prior to saving the policy file on Tailscale.

There are plenty of other examples in the official documentation.