Richard North’s Blog

Using Tailscale with Docker

Tailscale is a re­ally nice prod­uct that com­bines the mod­ern VPN ca­pa­bil­i­ties of Wireguard with a re­ally slick and well thought out user ex­pe­ri­ence.
I’ve been us­ing it for per­sonal pro­jects for a short while, and it feels like a tech­nol­ogy that I’ll be very happy to stick with over the long term.

It’s free for per­sonal use, very easy to use, and I’m con­fi­dent in its se­cu­rity, so I’d highly rec­om­mend it to oth­ers.

Background #

One thing I’ve been mean­ing to try for a while is ex­pos­ing a docker con­tainer over a Tailscale net­work. It’s easy to just run a con­tainer on a server ex­posed on a par­tic­u­lar host port, and then ac­cess that via Tailscale.

However, I’d pre­fer to ex­pose in­di­vid­ual Docker con­tain­ers rather than the en­tire host’s ser­vices. This way, we can ac­cess our con­tain­ers in a host ag­nos­tic’ man­ner: we don’t need to think about where a con­tainer is run­ning or which of a host’s lim­ited ports be­longs to which con­tainer — it’s just there on our pri­vate net­work.

Solution #

The fol­low­ing docker-compose.yml file demon­strates one ap­proach, es­sen­tially set­ting up a Tailscale side­car con­tainer for our ser­vice:

Example docker-compose.yml file — cus­tomi­sa­tions are an ex­er­cise for the reader!

version: "2.4"
services:
tailscale:
hostname: myservice # This will become the tailscale device name
image: richnorth/tailscale:v0.99.1
volumes:
- "./tailscale_var_lib:/var/lib" # State data will be stored in this directory
- "/dev/net/tun:/dev/net/tun" # Required for tailscale to work
cap_add: # Required for tailscale to work
- net_admin
- sys_module
command: tailscaled

myservice-container:
image: nginx # Your image goes here
network_mode: service:tailscale

Notice that we:

After start­ing the pair of con­tain­ers, we need to au­then­ti­cate the Tailscale dae­mon with our Tailscale ac­count:

$ docker-compose up

...

$ docker-compose exec tailscale tailscale up


To authenticate, visit:

	https://login.tailscale.com/a/SOME_HEX_CODE

Tailscale gives us a lo­gin URL, which is used to au­then­ti­cate this new Dockerised Tailscale dae­mon to your pri­vate Tailscale ac­count.

After com­plet­ing lo­gin, Tailscale will store a small amount of state data in­side the tailscale_var_lib vol­ume-mounted di­rec­tory. This data needs to be re­tained be­tween restarts of the con­tain­ers to avoid hav­ing to re­peat the lo­gin process.

We can find the ser­vice’s Tailscale IP ad­dress via the ad­min con­sole:

Finding the IP address of the new service in the Tailscale admin console
Finding the IP ad­dress of the new ser­vice in the Tailscale ad­min con­sole

If we were us­ing the nginx ex­am­ple ser­vice given above, we can now sim­ply ac­cess http://​SER­VICE_AD­DRESS from any de­vice con­nected to our Tailscale net­work. Notice that there’s no ex­plicit need to share the con­tain­er’s ports, and we can mi­grate this con­tainer + side­car to an­other host as long as we move the tailscale_var_lib di­rec­tory or per­form the Tailscale lo­gin step again.

And that’s it! Our ser­vice is just an­other server on our pri­vate Tailscale net­work, ready for use!

Followup thoughts #

← Home