---
# System prepended metadata

title: Running Tailscale with Docker
tags: [docker, tailscale]

---

# Running Tailscale with Docker

Tailscale provides a simple and secure way to connect servers and services using a **mesh VPN**, without exposing ports or managing complex firewall rules.

In this post, I’ll walk through running **Tailscale as a Docker container** using the **host network**, allowing the server itself to join the Tailscale network with minimal configuration.

The focus is on a setup that is:
- Simple and reproducible
- Restart-safe
- Suitable for production environments

The full script is provided at the end.

---

## What are we trying to achieve?

The goal of this setup is to:

- Join a server to a Tailscale network
- Avoid manual Tailscale installation on the host
- Keep the setup containerized and easy to manage
- Allow Tailscale to operate at the **host network level**

This is especially useful for servers, VMs, or bare-metal machines.

---

## General approach

In this setup:

- The official `tailscale/tailscale` Docker image is used
- The container runs on the **host network**
- Required Linux capabilities are explicitly granted
- Authentication is handled via a **Tailscale auth key**
- No configuration files are required

This keeps the container **stateless** and easy to recreate.

---

## Prerequisites

Before starting, make sure that:

- Docker is installed on the server
- You have a Tailscale account
- You have generated a valid **Auth Key** from the Tailscale admin panel

> Auth keys can be created under **Tailscale Admin → Keys**.

---

## Step 1: Running Bash in safe mode

The script starts by enabling strict Bash settings:

- Exit immediately on errors
- Fail on undefined variables
- Catch errors in pipelines

This prevents silent failures and is considered a best practice for production scripts.

---

## Step 2: Defining base variables

The script defines the following core variables:

| Variable        | Description                                               |
|-----------------|-----------------------------------------------------------|
| `CONTAINER`     | Container name, used for easier management and debugging  |
| `IMAGE`         | Docker image pinned to a specific version for reproducibility |
| `AUTH_KEY`      | Authentication key used to join the Tailscale network     |

> In real environments, the auth key should be stored as a secret  
> (e.g. `.env`, Docker secrets, CI/CD secrets).

---


## Step 3: Using the host network

The container is started with `--network host`.

This allows:

- Tailscale to use the host’s network stack directly
- VPN interfaces to be created at the host level
- No additional port mapping or routing configuration

This mode is ideal for VPN-style workloads like Tailscale.

---

## Step 4: Granting required Linux capabilities

Tailscale requires specific capabilities to function correctly:

- `NET_ADMIN` – to manage network interfaces and routing
- `SYS_MODULE` – to interact with kernel modules when needed

Without these, the Tailscale daemon will not be able to establish connectivity.

---

## Full Bash script (copy & paste)

Below is the complete script containing all the steps described above:

```bash
#!/usr/bin/env bash
set -euo pipefail

# ============================================================
# Tailscale setup script (bash)
#
# This script:
# - starts a Tailscale container using the host network
# - joins the server to a Tailscale network
# ============================================================

# ------------------------------------------------------------
# 0) Base variables
# ------------------------------------------------------------
CONTAINER="tailscale"
IMAGE="tailscale/tailscale:v1.90.9"
TS_AUTHKEY="tskey-XXXX"  # replace with your real auth key

# ------------------------------------------------------------
# 1) Start Tailscale container
# ------------------------------------------------------------
docker run -d \
  --name "$CONTAINER" \
  --network host \
  --cap-add NET_ADMIN \
  --cap-add SYS_MODULE \
  -e TS_AUTHKEY="$TS_AUTHKEY" \
  "$IMAGE"
