Exposing Local Development Servers Securely with Tailscale Serve

As developers, we often need to expose local services to other devices—whether that’s for testing on a phone, sharing work-in-progress with a teammate, or running an integration test across machines. Tools like ngrok have been the go-to solution, but there’s a simpler, more secure option if you already use Tailscale: Tailscale Serve.

In this post, I’ll walk through how you can use Tailscale Serve to make a local Dockerized Rails + Ember setup available from your laptop, without editing your docker-compose.yml.

The Problem: Ports Bound to 127.0.0.1

Take this example docker-compose.yml snippet:

1
2
3
4
5
6
7
services:
  api:
    ports:
      - "127.0.0.1:3000:3000"
  ember:
    ports:
      - "127.0.0.1:4200:4200"

Both services are bound to localhost only. That means they’re accessible from the host machine, but not from your LAN or other devices on your Tailnet. Changing to 0.0.0.0:3000 would solve that—but sometimes you don’t want to (or can’t) touch your compose files.

The Tailscale Serve Solution

Tailscale Serve lets you proxy a port from your machine to the rest of your Tailnet, without changing your app’s binding. Think of it as a built-in, self-hosted ngrok.

Step 1: Install and log into Tailscale

On your dev machine (e.g., Arch Linux host):

1
2
3
sudo pacman -S tailscale
sudo systemctl enable --now tailscaled
sudo tailscale up

Do the same on your macOS laptop (download from tailscale.com/download).

Step 2: Serve your local services

Forward your Rails app running on localhost:3000:

1
sudo tailscale serve --tcp 3000 tcp://localhost:3000

And your Ember app on port 4200:

1
sudo tailscale serve --tcp 4200 tcp://localhost:4200

Step 3: Access from another device

On your Mac (or any device logged into the same tailnet):

  • Use the Tailscale IP: http://100.x.x.x:3000
  • Or, if you’ve enabled MagicDNS, use the hostname: http://archmachine.tailnet-name.ts.net:3000

That’s it. No config changes, no firewall rules, no router port forwards.

Going Further: TLS & Public Sharing

  • Want HTTPS? Tailscale can terminate TLS for you:sudo tailscale serve –tls-terminated-tcp 443 tcp://localhost:3000 Now you can visit https://archmachine.tailnet-name.ts.net/ with a valid cert.
  • Want to share outside your tailnet? Tailscale’s Funnel feature lets you route traffic from the public internet to your local service, securely and with ACLs.

Why Use Tailscale Serve Instead of ngrok?

  • No extra accounts: You already use Tailscale for secure networking.
  • End-to-end encrypted: Traffic stays inside your Tailnet.
  • Custom hostnames: MagicDNS gives you stable names.
  • Multi-device by default: Works across all machines in your tailnet, no tunnels to spin up.

It’s like having a private, secure ngrok alternative baked into your VPN.