Linux Headless Mode

Linux Headless Mode

The Twingate Client for Linux may be run in headless mode, allowing it to be used in environments where a graphical user interface is not available. This is particularly useful in server environments where the Client is required to run as a background service.

Twingate’s Linux Client may be used in either interactive mode or headless mode.

  • Interactive mode is the default mode and is documented in our Linux Client documentation. The same platform support and installation instructions apply.
  • Headless mode is accessed by passing a --headless parameter to the twingate setup command with the path to a valid Service Key specified. The Service Key is obtained from the Service configuration in the Twingate Admin console.

Supported distributions

The Twingate Linux Client currently supports the following Linux distributions for x86/AMD64 and ARM64-based devices:

Additionally, the following are supported for x64/AMD64-based devices:

  • Arch Linux
  • ThinPro
  • NixOS
  • Gentoo

Prerequisites

  • A Twingate account with the necessary permissions to create and manage Services. See the Services documentation for more information on how to create a Service account and Service Keys.

Working with the Linux Client in systemd

Installation & configuration

  • The Linux Client is installed by following the existing installation process.
  • Configure the Linux Client in headless mode by running the twingate setup command with the --headless parameter.

For example:

curl https://binaries.twingate.com/client/linux/install.sh | sudo bash
sudo twingate setup --headless /path/to/service_key.json

Additional command line parameters, including the ability to set the default log level, are available. More information is available by running twingate help setup.

Starting & stopping the Client

# Start the client
sudo twingate start
# Check client status
twingate status
# Stop the client
sudo twingate stop

Troubleshooting

The Linux Client runs as a systemd service with logs retrievable via journalctl.

# Retrieve recent client logs
sudo journalctl -u twingate --since "1 hour ago"

Working with the Linux Client in Docker

The Linux Client may also be run in a Docker container. This may be useful in situations where only an individual service requires access to Twingate protected Resources.

Prior to running the Docker container, ensure that the Service Key is available on the host system. The Service Key should be mounted into the container using a volume.

Running the Client in Docker

The command below will run the Twingate Client in a Docker container, mounting the Service Key from the host system and starting the Client in headless mode. The command can be modified to suit the specific requirements of the environment, but the following parameters are required:

  • device /dev/net/tun
  • cap-add NET_ADMIN

Without these two parameters the Client running inside of the container will not be able to connect to Twingate.

docker run -d\
-v /path/to/service-key/:/etc/twingate-service-key/ \
--device /dev/net/tun \
--cap-add NET_ADMIN \
ubuntu:latest bash -c "
apt-get update &&
apt-get install curl -y &&
curl https://binaries.twingate.com/client/linux/install.sh | bash &&
sudo twingate setup --headless /etc/twingate-service-key/service-key.json &&
sudo twingate start &&
sleep infinity
"

Alternatively, you can use Docker Compose to manage the container. An example docker-compose.yml file is shown below:

version: "1.0"
services:
tg-headless-client:
image: ubuntu:latest
command: >
bash -c "apt-get update
&& apt-get install curl -y
&& curl https://binaries.twingate.com/client/linux/install.sh | bash
&& sudo twingate setup --headless /etc/twingate-service-key/service-key.json
&& sudo twingate start
&& sleep infinity"
volumes:
- /path/to/service-key/:/etc/twingate-service-key/
devices:
- /dev/net/tun
cap_add:
- NET_ADMIN
restart: unless-stopped

Finally, if you are using the Client to support a specific service and want it to only be available for that service, you can use a Compose file to load both containers and tie them together:

version: "1.0"
volumes:
uptime-kuma:
external: true
services:
tg-headless-client:
image: ubuntu:latest
container_name: tg-headless-client
command: >
bash -c "apt-get update
&& apt-get install curl -y
&& curl https://binaries.twingate.com/client/linux/install.sh | bash
&& sudo twingate setup --headless /etc/twingate-service-key/service-key.json
&& sudo twingate start
&& sleep infinity"
volumes:
- /path/to/service-key/:/etc/twingate-service-key/
devices:
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- "3001:3001"
restart: always
uptime-kuma:
image: louislam/uptime-kuma:1
container_name: uptime-kuma
volumes:
- uptime-kuma:/app/data
network_mode: "service:tg-headless-client"
depends_on:
- tg-headless-client
restart: always

In this example we are loading the Twingate Client in headless mode and then loading the Uptime Kuma service. Uptime Kuma is a commonly used open-source uptime monitoring and alerting system, and we want it to be able to access Resources in Remote Networks via the Twingate service. The network_mode: "service:tg-headless-client" line ties the Uptime Kuma service to the Twingate Client service, allowing it to access the network tunnel created by the Client.

Running the Twingate Client in CI/CD Jobs & Workflows

Twingate’s Headless Client can be flexibly integrated into existing CI/CD workflows. In addition to the above examples for systemd and docker, the following methods are commonly used by teams to enable private, protected access to Resources.

Using Github Actions

Twingate’s Github Action is a pre-defined step that integrates seamlessly into existing workflows to ensure the runner has access to private Resources (essentially installs as systemd like the example above).

Self-hosted Runners

Self-hosted runners can use either the systemd or docker methods to access Resources that are not directly routable from the runner’s subnet. This provides greater flexibility for private Resource access in CI/CD pipelines.

Containerized Jobs

In some cases, third-party services provide Github Actions or steps that spin up Docker containers on the runner to deploy infrastructure (e.g. via Terraform/Pulumi). Here’s how to ensure these containers can access private Resources through Twingate:

Shared Docker Network: If possible, run the container in the same Docker network as the Twingate Client, where the service in question uses the Twingate Client’s network stack, similar to the Docker Compose setup above.

When Sharing Networks Is Not Possible: Some services don’t allow multiple containers to share the same network namespace. In these cases, containers will typically use the host’s /etc/resolv.conf for DNS resolution unless explicitly overridden. This can cause DNS queries for private Resources to fail, as the container doesn’t know to forward DNS queries to the Twingate Client’s local DNS resolvers running on the host.

To ensure containers use Twingate’s DNS and route traffic correctly through Twingate’s tunnel, follow these steps:

  • Install the Twingate Client in Headless Mode: Run the Twingate Client in headless mode on the runner’s host (as a systemd service or using the GitHub Action).
  • Stop Docker Daemon: Temporarily stop the Docker service to make changes that apply to all containers.
  • Update Docker’s DNS Configuration: Add the Twingate Client’s local DNS resolvers (100.95.0.251, 100.95.0.252, 100.95.0.253, 100.95.0.254) to Docker’s /etc/docker/daemon.json.
  • Restart Docker: Restart Docker to apply the new DNS settings and ensure subsequent containers use Twingate for DNS resolution.

Example:

- name: Configure Docker to Use Twingate DNS
run: |
# Step 1: Stop Docker service to make changes
sudo systemctl stop docker
# Step 2: Create a temporary file to store the updated Docker configuration
tmp=$(mktemp)
# Step 3: Add Twingate DNS resolvers to Docker's daemon configuration file
sudo jq '. + { "dns": ["100.95.0.251", "100.95.0.252", "100.95.0.253", "100.95.0.254"] }' /etc/docker/daemon.json > "$tmp"
# Step 4: Move the updated configuration back to the Docker daemon file
sudo mv "$tmp" /etc/docker/daemon.json
# Step 5: Restart Docker service to apply changes
sudo systemctl start docker
# Step 6: Verify Docker is using the updated DNS resolvers
docker info | grep "DNS"

Other CI/CD platforms

As long as your CI/CD platform supports installing the Twingate Client in headless mode, allows underlying network configuration, and runs on a supported OS distro, you should be able to tunnel traffic to private Resources with relative success.

Last updated 14 hours ago