Skip to content

Cloud & Infrastructure · Networking

Tailscale for Distributed Dev Teams: Private Networks Without the VPN Pain

Tailscale creates a private mesh network across any combination of cloud servers, developer laptops, and office machines — without port forwarding, firewall rules, or dedicated VPN hardware.

Anurag Verma

Anurag Verma

7 min read

Tailscale for Distributed Dev Teams: Private Networks Without the VPN Pain

Sponsored

Share

Traditional VPNs work by routing all traffic through a central server. Every packet from every device goes through the hub, the hub applies policies, and the packet goes to its destination. This is simple to reason about but creates a bottleneck: the hub’s bandwidth limits how fast the network is, and if the hub goes down, everyone is disconnected.

WireGuard changed the underlying protocol. WireGuard is a modern VPN protocol that’s built into the Linux kernel (and available on every major OS), uses efficient modern cryptography, and creates direct peer-to-peer tunnels between devices. The performance is significantly better than IPSec or OpenVPN. But WireGuard is just the transport. You still need to distribute and manage keys, handle NAT traversal, and wire everything together.

Tailscale is the automation layer on top of WireGuard. You install the Tailscale client on a device, authenticate to your account, and that device joins your private network (called a tailnet). It’s on the network in about 30 seconds. Direct WireGuard tunnels form between devices automatically.

What You Get

Every device in your tailnet gets a stable private IP in the 100.x.y.z range. This IP is consistent across network changes. Your developer’s laptop keeps the same tailnet IP whether they’re on office Wi-Fi, a hotel network, or home broadband.

The network is peer-to-peer: two devices that can reach each other directly will connect directly, without traffic going through Tailscale’s servers. For devices behind NAT (which is most devices), Tailscale uses DERP relay servers as a fallback, but direct connections are attempted first and preferred.

This means:

  • A developer can SSH into a production server using its tailnet IP from anywhere
  • An internal dashboard running on a developer’s laptop is accessible to the team at a stable address without exposing it to the internet
  • A database or API running in a cloud VM is accessible to the whole team without opening a public port

Getting Started

Install on macOS:

brew install tailscale
sudo tailscale up

Install on Linux (Debian/Ubuntu):

curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up

Install on a cloud server:

# Same as Linux, then authenticate headlessly
sudo tailscale up --authkey=tskey-auth-YOUR_KEY_HERE

Auth keys are generated from the Tailscale admin console and can be set to expire or be reusable. For servers and CI environments where interactive authentication isn’t possible, pre-authorized keys are how you automate enrollment.

After setup, check what’s on the network:

tailscale status
100.64.0.1    my-laptop       macOS   -
100.64.0.2    prod-server     linux   active; relay "fra", tx 1.5MiB rx 2.1MiB
100.64.0.3    staging-server  linux   active; direct 203.0.113.1:41641

The direct vs relay indicator shows whether the connection is peer-to-peer (direct) or going through a relay server. Direct is better; if a connection shows relay after a few minutes, a firewall is likely blocking UDP.

Accessing Services Without Port Forwarding

The most immediate use case: accessing internal services on cloud servers without opening ports in your security group.

Suppose you have a Postgres database on a cloud VM. Without Tailscale, you either open port 5432 to your static IPs (brittle if developers are remote), set up a bastion host (operational overhead), or use your cloud provider’s VPN product (expensive and slow to configure).

With Tailscale, you install it on the VM, and anyone on the tailnet can reach the database directly:

psql -h 100.64.0.2 -U myapp -d production

No public port. No bastion. No changes to your security group. The database port never leaves the WireGuard tunnel.

This also applies to internal tooling. A Grafana instance, a Redis commander, an internal API with admin endpoints — all can be accessible to the team at their tailnet addresses without any public exposure.

Sharing Dev Environments With Teammates

A dev environment running on your laptop is normally not accessible to anyone else. Tailscale changes this without any port forwarding.

If you’re running a local dev server at localhost:3000, it’s accessible to teammates at your tailnet address:

# Run your dev server bound to 0.0.0.0 so it listens on all interfaces
npm run dev -- --host 0.0.0.0

# Teammate accesses it at your tailnet IP
open http://100.64.0.1:3000

For a more stable URL, Tailscale Funnel can expose a port at a stable *.ts.net domain, optionally with HTTPS:

# Expose port 3000 at https://my-laptop.tailnetname.ts.net
tailscale funnel 3000

This is useful when you need to share a webhook endpoint during development, show a client a work-in-progress without deploying it, or test a mobile app that needs to hit a real URL.

Access Controls With ACLs

By default, all devices in a tailnet can reach all other devices. For most small teams this is fine. When it isn’t, Tailscale access control lists (ACLs) let you restrict which devices can talk to which:

{
  "acls": [
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["tag:server:*"]
    },
    {
      "action": "accept",
      "src": ["group:developers"],
      "dst": ["tag:database:22", "tag:database:5432"]
    }
  ],
  "groups": {
    "group:developers": ["email@example.com", "other@example.com"]
  },
  "tagOwners": {
    "tag:server": ["group:developers"],
    "tag:database": ["group:developers"]
  }
}

Tags are applied to devices during enrollment using auth keys. A CI system enrolled with a tag:ci auth key only gets access to the resources that tag allows.

SSH Through Tailscale

Tailscale SSH replaces ~/.ssh/authorized_keys management. When enabled on a server, Tailscale handles authentication using the same identity used for tailnet membership. No more distributing SSH keys manually or rotating them when someone leaves.

# Enable Tailscale SSH on a server
sudo tailscale up --ssh

# SSH from any tailnet device
ssh 100.64.0.2
# or using the stable hostname
ssh prod-server

Access to the SSH service is controlled by the same ACL rules as everything else. Remove someone from the tailnet, they lose SSH access to all servers simultaneously.

What Tailscale Is Not

Tailscale is not a replacement for your cloud provider’s security groups or firewall rules. The private tailnet is an additional network layer, not the only one. Services that should be public (your web app’s 443 port) should still be accessible publicly. Tailscale handles the private network between your team and internal services.

It’s also not free for teams beyond a certain size. The free plan covers one user with limited devices. The Teams plan (currently around $5 per user/month) covers multiple users with audit logs and policy controls. The main limitations on the free plan for small projects are the single-user restriction and lack of audit logging.

The Practical Setup for a Small Agency

For a typical 5-10 person dev agency running a mix of cloud servers and remote developers, the working setup is:

  1. Install Tailscale on every developer’s laptop and every cloud server
  2. Use pre-authorized keys for servers so enrollment is part of your provisioning script
  3. Disable public database ports in security groups — access goes through the tailnet
  4. Use Tailscale SSH on servers instead of managing SSH keys manually
  5. Use Tailscale Funnel for sharing local dev environments with clients or teammates

The enrollment of a new developer or server takes about 5 minutes. When someone leaves, removing them from the tailnet revokes all their access to private resources simultaneously, without having to hunt down which SSH keys to delete or which security group rules to update.

Sponsored

Enjoyed it? Pass it on.

Share this article.

Sponsored

The dispatch

Working notes from
the studio.

A short letter twice a month — what we shipped, what broke, and the AI tools earning their keep.

No spam, ever. Unsubscribe anytime.

Discussion

Join the conversation.

Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.

Sponsored