We use cookies to make your experience better.
Learn how to use Tailscale in your Coder workspace.
This guide walks you through configuring Tailscale networking for use inside Coder workspaces. With Tailscale networking, you can access services running inside Coder and services running on your tailnet (Tailscale private network).
As part of this tutorial, you'll create an image with the following that you'll use to create new Coder workspaces. The container image will include:
tailscaled
)proxychains4
)tailscaled
to facilitate connections to the
tailnet, listening on localhost:1080
tailscaled
to facilitate connections to the
tailnet, listening on localhost:3128
This guide describes how to install Tailscale in a Ubuntu base image using the package manager and running it in userspace networking mode. As such:
systemd
can start the
Tailscale daemon (tailscaled
)sudo
access to configure the Tailscale tunnellocalhost
proxychains4
Tailscale does not require root access to operate in userspace networking mode, and the requirement to use container-based virtual machine workspaces applies only to the instructions in this guide. Contact our support team if you are interested in using Tailscale in your Coder workspace without root access.
In Coder, developer workspaces are defined by a Dockerfile that contains the apps, tools, and dependencies that you need to work on the project.
See our custom image docs and Docker’s guide to writing Dockerfiles for more information.
To simplify creating and maintaining the image, we recommend structuring your
source code so that files added or modified in the image match the hierarchy of
the target files, in a subdirectory called files
:
.
├── Dockerfile
└── files
├── etc
│ ├── apt
│ │ ├── preferences.d
│ │ │ └── tailscale
│ │ └── sources.list.d
│ │ └── tailscale.list
│ └── systemd
│ └── system
│ └── tailscaled.service.d
│ └── tailscale.conf
└── usr
└── share
└── keyrings
└── tailscale.gpg
While it is possible to configure everything directly in a single Dockerfile, we recommend using a folder hierarchy, since this makes it easier to create a reproducible image and examine the change history for individual files.
Create the folder hierarchy:
mkdir --parents \
files/etc/apt/preferences.d \
files/etc/apt/sources.list.d \
files/etc/systemd/system/tailscaled.service.d \
files/usr/share/keyrings
Then, create the following files (we'll walk you through the contents of each in the following steps):
touch files/etc/apt/preferences.d/tailscale \
files/etc/apt/sources.list.d/tailscale.list \
files/etc/systemd/system/tailscaled.service.d/tailscale.conf
Add the Tailscale package repository to tailscale.list
in the local path
files/etc/apt/sources.list.d
. This will appear in /etc/apt/sources.list.d
in
the resulting image, with the following contents:
deb [signed-by=/usr/share/keyrings/tailscale.gpg] https://pkgs.tailscale.com/stable/ubuntu focal main
This configures apt
and apt-get
to install packages from the Tailscale
repository and verify package signatures with the specified public key.
For improved security, you can configure apt
to deny package installation from
a given repository by default and allow specific packages by name. To do this,
create files/etc/apt/preferences.d/tailscale
with the following contents:
# Ignore all packages from this repository by default
Package: *
Pin: origin pkgs.tailscale.com
Pin-Priority: 1
Package: tailscale
Pin: origin pkgs.tailscale.com
Pin-Priority: 500
Retrieve the signing key from Tailscale, and store the binary (dearmored) key
file in files/usr/share/keyrings/tailscale.gpg
:
curl --silent --show-error --location "https://pkgs.tailscale.com/stable/ubuntu/focal.gpg" | \
gpg --dearmor --yes --output=files/usr/share/keyrings/tailscale.gpg
tailscaled
service settingsBy default, tailscaled
will store its internal state in a state file
located at /var/lib/tailscale/tailscaled.state
(this is is ephemeral in
Coder). We will need to modify the service settings to:
/home/coder
)If you do not require outbound connections from the workspace to other services running in the tailnet, you may skip the steps where you configure the proxies.
Override the ExecStart
setting for the tailscaled
service by saving the
following to files/etc/systemd/system/tailscaled.service.d/tailscale.conf
:
[Service]
ExecStart=
ExecStart=-/usr/sbin/tailscaled --state=/home/coder/.config/tailscaled.state --socket=/var/run/tailscale/tailscaled.sock --port $PORT --tun=userspace-networking --socks5-server=localhost:1080 --outbound-http-proxy-listen=localhost:3128 $FLAGS
Create a Dockerfile
, build it, and push to an external repository, such as
Docker Hub:
FROM codercom/enterprise-base:ubuntu
USER root
# Copy configuration files to appropriate locations
COPY files /
ARG DEBIAN_FRONTEND="noninteractive"
RUN apt-get update && \
apt-get install --no-install-recommends --yes --quiet \
netcat-openbsd \
proxychains4 \
python3 && \
apt-get install --no-install-recommends --yes --quiet \
tailscale && \
# Delete package cache to avoid consuming space in layer
apt-get clean && \
rm -rf /var/lib/apt/lists/*
USER coder
ENV ALL_PROXY=socks5://localhost:1080
ENV http_proxy=http://localhost:3128
Create a workspace using the container image. Initially, tailscaled
should
be running, but it will indicate that it requires authentication:
systemctl status tailscaled
Authenticate using sudo tailscale up
, then verify that other network devices
are visible:
tailscale status
You may also use a pre-authentication key with
tailscale up --authkey
to avoid needing to sign in via a web browser.
tailscale
should maintain connectivity across workspace rebuilds, since we
chose to store the state file in a persistent volume.
By creating two workspaces from the same image, both authenticated to Tailscale, we can verify connectivity works as expected. In one workspace, run the Python web server:
python3 -m http.server 3000
In another workspace, verify that tailscaled
is listening for connections on
the configured proxy ports:
ss -nltp
Check that the http_proxy
environment variable is set to the address of the
local tailscaled
proxy:
env | grep -i proxy
Run curl
(which respects the http_proxy
command) to connect to the webserver
running in the other workspace. Since we proxy the connection through the local
tailscaled
instance, we can use the internal hostname:
curl http://jawnsy-tailscale-1:3000
For applications that do not respect the http_proxy
or ALL_PROXY
environment
variables, consider using a tool like proxychains4
to intercept the socket
system calls and transparently route traffic through the proxy.
See an opportunity to improve our docs? Make an edit.