Tailscale is a mesh VPN built on WireGuard. Every device gets a stable private address in the 100.64.0.0/10 range and talks to the others directly, with keys and NAT traversal handled by Tailscale's coordination server. In this tutorial you will install Tailscale on an Ubuntu 24.04 server, join it to your tailnet, turn it into a subnet router and exit node, and restrict who can reach it with an access policy and Tailscale SSH.
Prerequisites
To follow this tutorial, you will need:
- A server running Ubuntu 24.04 LTS, for example a CubePath VPS, with a non-root user that has
sudoprivileges. - A Tailscale account (sign in at
login.tailscale.comwith Google, Microsoft, GitHub or another identity provider). - At least one other device (laptop or second server) with the Tailscale client installed, to test connectivity.
- Outbound access to TCP 443 and UDP 41641. Tailscale works behind NAT and does not need inbound ports, although allowing UDP 41641 improves the chance of direct connections.
Step 1 - Installing Tailscale from the official repository
Tailscale publishes an APT repository per Ubuntu release. Download the signing key and the repository definition for Ubuntu 24.04 (noble):
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg > /dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
The .list file already contains signed-by=/usr/share/keyrings/tailscale-archive-keyring.gpg, which is why the key is stored at that path.
Install the package:
sudo apt update
sudo apt install tailscale
The package enables and starts the tailscaled daemon. Confirm it is running:
systemctl status tailscaled --no-pager
● tailscaled.service - Tailscale node agent
Loaded: loaded (/usr/lib/systemd/system/tailscaled.service; enabled; preset: enabled)
Active: active (running) since ...
Step 2 - Connecting the server to your tailnet
Bring the node up. Tailscale prints a login URL that you open in any browser to authorize the machine:
sudo tailscale up
To authenticate, visit:
https://login.tailscale.com/a/1a2b3c4d5e6f
After you approve it in the browser, the command returns. Check the address the server received and the other devices in your tailnet:
tailscale ip -4
tailscale status
100.101.102.103
100.101.102.103 vps1 your_user@ linux -
100.88.12.40 laptop your_user@ macOS -
Test the tunnel to another device. The last line tells you whether the connection is direct or goes through a DERP relay:
tailscale ping laptop
pong from laptop (100.88.12.40) via 198.51.100.24:41641 in 18ms
Headless servers and key expiry
For servers you provision automatically, generate an auth key in the admin console under Settings > Keys and pass it instead of using the browser flow:
sudo tailscale up --auth-key=tskey-auth-your_key
Node keys expire periodically by default (180 days) and the machine must re-authenticate. For long-lived servers, open Machines in the admin console, select the server and choose Disable key expiry.
Step 3 - Enabling IP forwarding
A subnet router or exit node forwards packets for other devices, so the kernel must allow forwarding. Create a dedicated sysctl file:
sudo nano /etc/sysctl.d/99-tailscale.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
Apply it and verify:
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
net.ipv4.ip_forward = 1
net.ipv6.conf.all.forwarding = 1
If you only want the server as a normal tailnet member, you can skip this step and Steps 4 and 5.
Step 4 - Advertising a subnet route
A subnet router lets tailnet devices reach machines that do not run Tailscale, such as other servers on a private network. Suppose the server has a private interface on 10.0.0.0/24. Advertise that range:
sudo tailscale set --advertise-routes=10.0.0.0/24
tailscale set changes a single preference without resetting the others, which is safer than re-running tailscale up with a new list of flags.
Advertised routes are not active until you approve them. In the admin console, open Machines, click the server, choose Edit route settings and enable 10.0.0.0/24.
Linux clients ignore subnet routes unless told otherwise. On any Linux device that should use the route, run:
sudo tailscale set --accept-routes
From that client, verify you can reach a host on the private network:
ping -c 3 10.0.0.5
Step 5 - Using the server as an exit node
An exit node routes all internet traffic of a client through the server, which is useful on untrusted Wi-Fi or when you need a fixed egress IP. Advertise the server as an exit node:
sudo tailscale set --advertise-exit-node
Approve it in the admin console in the same Edit route settings dialog (Use as exit node). Then select it on a Linux client, keeping access to the client's local LAN:
sudo tailscale set --exit-node=vps1 --exit-node-allow-lan-access
On desktop and mobile clients, choose the exit node from the Tailscale menu. Confirm the client now leaves through the server's public IP:
curl -4 https://ifconfig.me
The output should be your_server_ip. To stop using the exit node, run sudo tailscale set --exit-node=.
Step 6 - Restricting access with an ACL policy
By default every device in a personal tailnet can reach every other device. Tighten that in the admin console under Access controls, which edits the tailnet policy file (HuJSON). The following policy tags servers, lets tailnet admins reach them on any port and lets them serve HTTP and HTTPS to everyone else:
{
"tagOwners": {
"tag:server": ["autogroup:admin"]
},
"acls": [
{
"action": "accept",
"src": ["autogroup:admin"],
"dst": ["tag:server:*"]
},
{
"action": "accept",
"src": ["autogroup:member"],
"dst": ["tag:server:80,443"]
}
],
"ssh": [
{
"action": "accept",
"src": ["autogroup:admin"],
"dst": ["tag:server"],
"users": ["root", "your_user"]
}
]
}
Save the policy, then assign the tag to the server: in Machines, open the server's menu, choose Edit ACL tags and add tag:server. Tagged devices are owned by the tag rather than a user, and key expiry is disabled for them automatically.
WarningEditing the policy replaces the default allow-all rule. Keep a rule that covers your own access before saving, or you can lock yourself out of tailnet-only services.
Step 7 - Enabling Tailscale SSH and MagicDNS
Tailscale SSH lets the tailnet authenticate SSH sessions using the ssh rules in the policy above, so you do not have to distribute SSH keys. Enable it on the server:
sudo tailscale set --ssh
MagicDNS is enabled by default on new tailnets and gives each device a name such as vps1.your-tailnet.ts.net. From an admin device, connect with a regular SSH client:
ssh your_user@vps1
Once this works, you can stop exposing SSH to the internet and allow it only on the Tailscale interface. With UFW:
sudo ufw allow in on tailscale0
sudo ufw allow 41641/udp
sudo ufw delete allow OpenSSH
sudo ufw enable
sudo ufw status verbose
ImportantOnly remove the public SSH rule after confirming you can log in over Tailscale, and keep the CubePath web console available as a fallback.
Troubleshooting
tailscale ping always says via DERP. The two peers could not establish a direct UDP path. Run tailscale netcheck on both ends; if UDP: false appears, a firewall is blocking outbound UDP. Allowing inbound 41641/udp on the server usually fixes it. Relayed connections still work, just with higher latency.
Subnet route does not work. Check that the route is approved in the admin console, that sysctl net.ipv4.ip_forward returns 1 on the router, and that the client ran tailscale set --accept-routes. Hosts on the private network must also be allowed by their own firewalls to receive traffic from the router.
The node disappeared after months. Its key expired. Run sudo tailscale up again to re-authenticate, and disable key expiry or tag the node to prevent it. Service logs are available with sudo journalctl -u tailscaled.
Conclusion
Your Ubuntu 24.04 server is now part of a Tailscale tailnet, can route a private subnet and act as an exit node, and only accepts the connections your policy allows, including SSH authenticated by Tailscale. As next steps, invite teammates and put them in groups in the policy file, use tagged auth keys to join new servers automatically, or place internal dashboards behind the Tailscale interface instead of the public internet.
