Skip to content

Instantly share code, notes, and snippets.

@bnhf
Last active October 8, 2024 23:18
Show Gist options
  • Save bnhf/fed4cc3035f32a0f086b1da074a3d50b to your computer and use it in GitHub Desktop.
Save bnhf/fed4cc3035f32a0f086b1da074a3d50b to your computer and use it in GitHub Desktop.
Tailscale - Deploying with Docker and Portainer

Just thought I'd put together some detail on deploying Tailscale using Docker and Portainer. These bits-and-pieces are available elsewhere, but not together, so hopefully this will save someone a bit of time if you'd like to add Tailscale to an existing Docker install:

Here's my annotated recommended docker-compose, to use with Portainer-Stacks. Note that I'm not using a pre-made Auth Key. I started that way, but realized it was very easy to simply check the Portainer log for the tailscaled container once the stack is running. In that log you'll see the standard Auth link that you can use to authorize the container. This way you don't need to create a key in advance, or create a reusable key that introduces a security risk:

version: '3.9'
services:
  tailscale:
    image: tailscale/tailscale
    container_name: tailscaled
    cap_add:
      - NET_ADMIN
      - NET_RAW
    environment:
#      - TS_HOSTNAME=${TS_HOSTNAME} # Usually not necessary for your hostname to be the same name on the tailscale network
#      - TS_AUTHKEY=${TS_AUTHKEY} # Generate auth keys here: https://login.tailscale.com/admin/settings/keys
#      - TS_ROUTES=${TS_ROUTES} # Creates a subnet router for Tailscale. Use your subnet's CIDR in the form: 192.168.1.0/24
#      - TS_ACCEPT_DNS=${TS_ACCEPT_DNS} # Set to false for Pi-hole Docker setups
      - TS_SOCKET=${TS_SOCKET} # Specifying the /var/lib/tailscale/tailscaled.sock location allows use of standard Tailscale commands 
      - TS_EXTRA_ARGS=${TS_EXTRA_ARGS} # Add any other supported arguments in the docker commandline style: e.g. --advertise-exit-node
      - TS_STATE_DIR=${TS_STATE_DIR} # Required to create a persistent container state that will survive reboots
    volumes:
      - /data:/var/lib # Creates a tailscale directory under /data for persistence
      - /dev/net/tun:/dev/net/tun
    network_mode: host
    restart: unless-stopped

These are the minimum environment variables you'll want to define in the Portainer-Environment section:

TS_SOCKET=/var/run/tailscale/tailscaled.sock
TS_EXTRA_ARGS=--accept-routes
TS_STATE_DIR=/var/lib/tailscale

With these variables, you'll be able to exec into the container to run commands like "tailscale version" and "tailscale status". Your container will accept routes advertised by a designated node, and your setup (including authorization) will persist across reboots.

@MichaelMichaelMichaelMichaelMichael

Thank you - works fine!

@mrlopezco
Copy link

Thanks mate!

@z1haze
Copy link

z1haze commented May 5, 2024

Hi, thank you for sharing this. I'm pretty new to this, so do you mind explaining this var, and what it would be used for? I have a pihole server running in a container and I am wanting to use tailscale with it as my dns. I did see where you said to set accept dns to false, which I've done, but what is this one for? - TS_ROUTES=${TS_ROUTES} # Creates a subnet router for Tailscale. Use your subnet's CIDR in the form: 192.168.1.0/24. I will share my entire file if it helps:

version: "3"

services:
  pihole:
    container_name: pihole
    image: pihole/pihole:latest
    ports:
      - "53:53/tcp"
      - "53:53/udp"
      - "8080:80/tcp"
    environment:
      TZ: 'America/Somewhere'
      WEBPASSWORD: 'REDACTED'
    volumes:
      - './etc-pihole:/etc/pihole'
      - './etc-dnsmasq.d:/etc/dnsmasq.d'
    cap_add: []
    restart: unless-stopped
  tailscale:
    image: tailscale/tailscale
    container_name: tailscaled
    cap_add:
      - NET_ADMIN
      - NET_RAW
    environment:
#      - TS_HOSTNAME=${TS_HOSTNAME} # Usually not necessary for your hostname to be the same name on the tailscale network
#      - TS_AUTHKEY=${TS_AUTHKEY} # Generate auth keys here: https://login.tailscale.com/admin/settings/keys
#      - TS_ROUTES=${TS_ROUTES} # Creates a subnet router for Tailscale. Use your subnet's CIDR in the form: 192.168.1.0/24
      - TS_ACCEPT_DNS=${TS_ACCEPT_DNS}
      - TS_SOCKET=${TS_SOCKET}
      - TS_EXTRA_ARGS=${TS_EXTRA_ARGS}
      - TS_STATE_DIR=${TS_STATE_DIR}
    volumes:
      - /data:/var/lib
      - /dev/net/tun:/dev/net/tun
    network_mode: host
    restart: unless-stopped

@bnhf
Copy link
Author

bnhf commented May 5, 2024

Hi, thank you for sharing this. I'm pretty new to this, so do you mind explaining this var, and what it would be used for? I have a pihole server running in a container and I am wanting to use tailscale with it as my dns. I did see where you said to set accept dns to false, which I've done, but what is this one for? - TS_ROUTES=${TS_ROUTES} # Creates a subnet router for Tailscale. Use your subnet's CIDR in the form: 192.168.1.0/24. I will share my entire file if it helps:

@z1haze

In Tailscale, setting up a subnet router allows you access to devices on your LAN (by IP address), when you're remote. Installing Tailscale directly on devices is preferred, but since that's not possible for every device on a given network, a subnet router fills the gap.

@kraizelburg
Copy link

Hi, how can I use this compose file to have access to the host instead of a particular container. I mean I want to install tailscale in my server with docker and be able to ssh to and from it. Is this even possible?

@bnhf
Copy link
Author

bnhf commented Jul 18, 2024

Hi, how can I use this compose file to have access to the host instead of a particular container. I mean I want to install tailscale in my server with docker and be able to ssh to and from it. Is this even possible?

That's what this compose does. It installs Tailscale via Docker (Portainer recommended), and gives you access to the host via its Tailnet IP or MagicDNS hostname. It works great, and keeps you from having to install Tailscale directly on your Docker host computer. I have 50+ devices running Tailscale, and most of the Linux hosts are using this containerized approach.

@kraizelburg
Copy link

Hi, how can I use this compose file to have access to the host instead of a particular container. I mean I want to install tailscale in my server with docker and be able to ssh to and from it. Is this even possible?

That's what this compose does. It installs Tailscale via Docker (Portainer recommended), and gives you access to the host via its Tailnet IP or MagicDNS hostname. It works great, and keeps you from having to install Tailscale directly on your Docker host computer. I have 50+ devices running Tailscale, and most of the Linux hosts are using this containerized approach.

Hi again, I just tried your compose file and it does work. I mean it kind of works as I can ping my server from other devices via tailscale IP but I cannot ping or access other devices from the server where docker-compose is installed.

It seems that it works one way only. tailscale devices >>> server but no server >>> tailscale devices

@bnhf
Copy link
Author

bnhf commented Jul 18, 2024

It seems that it works one way only. tailscale devices >>> server but no server >>> tailscale devices

Makes sense, as Docker containers can't modify resolv.conf on the host. If you want that capability as well, you'd likely need to install Tailscale directly on the host. I've done that in at least one instance where I needed access from inside a container, to a remote device that could only be reached via my Tailnet. But, at least in my case, having access to my servers via my Tailnet and the servers being able access whatever resources they need via my LAN has worked well.

@kraizelburg
Copy link

kraizelburg commented Jul 18, 2024

It seems that it works one way only. tailscale devices >>> server but no server >>> tailscale devices

Makes sense, as Docker containers can't modify resolv.conf on the host. If you want that capability as well, you'd likely need to install Tailscale directly on the host. I've done that in at least one instance where I needed access from inside a container, to a remote device that could only be reached via my Tailnet. But, at least in my case, having access to my servers via my Tailnet and the servers being able access whatever resources they need via my LAN has worked well.

Ok understood, then I guess docker is not a good solution for me, I have to be able to access other devices from the tailscale host. Thanks anyway. I thought I could install tailscale via docker instead of normal way and have bidirectional connection but I see it is not possible.

Basically I wanted to install tailscale with docker in a NAS and being able to communicate from and to it.

@tuxpowered
Copy link

@kraizelburg les say your LAN is 192.168.1.0/24 When you install tailscale on your nas, regardless of vendor nas, in your compose file you would set - TS_ROUTES=192.168.1.0/24.
Now lets pretend your NAS LAN IP is 192.168.1.100.

Now if your in the coffee shop or on your phone or somewhere that is not the 192.168.1.0/24 network, and you have tailscale installed and --accept-routes set, than you can just go to http://192.168.1.100 as if it was on your network.

Under the hood the tailscale client on your device does a lookup on the tailscale network and sees that its allowed to forward 192.168.1.0/24 traffic to the instance installed on your nas.

That instance ALREADY has access to the nas, and simply redirects the traffic to http://192.168.1.100. This will work for any device on your LAN now. Unless the individual device is blocking traffic, etc.

This works flawlessly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment