Linux Headless Client

The Linux Headless Client is intended for environments without a graphical desktop session. It supports two authentication modes:

  • Service account tokens -- long-lived, multi-owner tokens generated in the admin portal. Best for system-to-system connectivity where no user is present to authenticate. See Service Accounts.
  • User tokens via browser-based sign-in -- short-lived tokens obtained by signing the running user into the portal in a browser. Useful when you want a human user to authenticate the Client on a server they manage without installing a GUI. See Browser-based authentication.

If you're looking for a desktop Client that authenticates with your identity provider through a graphical interface, see the Linux GUI Client.

Prerequisites

  • Any Linux-based OS with kernel 3.10 or higher
  • ARM64, ARMv7l, or x86_64 CPU
  • A Firezone account, plus admin access to create a service account if you intend to authenticate with a service account token
  • Root access or sudo for Split DNS

Download

Download the Linux headless Client from our changelog page, or use one of the direct links below:

Mark the binary as executable:

chmod +x firezone-client-headless-linux_1.5.8_x86_64

Installing on NixOS

Firezone ships a first-party flake with a NixOS module for the headless Client, so you can manage it declaratively alongside the rest of your system configuration. The module wires the Client up as a hardened systemd service that authenticates with a service account token.

The NixOS module authenticates with a service account token only. For browser-based user sign-in, download the Client manually using the steps above and follow the usage instructions.

Add the flake as an input, pinning the release tag of the headless Client:

# flake.nix
{
inputs.firezone.url = "github:firezone/firezone/headless-client-1.5.10";
}

Then import the module and enable the service in your system configuration:

# configuration.nix
{ inputs, ... }:
{
  imports = [ inputs.firezone.nixosModules.headless-client ];

  # Substitute pre-built, Firezone-signed binaries instead of compiling the
  # Client from source. Optional, but strongly recommended.
  nix.settings = {
    extra-substituters = [ "https://artifacts.firezone.dev/nix" ];
    extra-trusted-public-keys = [
      "artifacts.firezone.dev/nix-1:T4LHdL1HeA6LE9qgu0Po0j3RIlelKZz8pzqnzEAIRfI="
    ];
  };

  services.firezone.headless-client = {
    enable = true;

    # Path to a file containing a service account token from the admin portal.
    # Keep this out of the Nix store — point it at a secret managed outside of
    # your configuration (e.g. with sops-nix or agenix), or place it manually.
    tokenFile = "/var/lib/secrets/firezone-client-token";

    # Optional. Defaults to the system hostname.
    name = "my-nixos-host";
  };
}

Rebuild to start the Client:

sudo nixos-rebuild switch

The service runs as firezone-headless-client. Check its status and logs with:

systemctl status firezone-headless-client
journalctl --unit firezone-headless-client --follow

Substituter changes only take effect after a rebuild, so the very first build with the cache configured may still compile from source. If the cache is unreachable, Nix falls back to building from source — slower, but never broken.

Module options

The most common options for services.firezone.headless-client:

OptionDefaultDescription
enablefalseEnable the headless Client systemd service.
tokenFile(required)Path to a file holding a service account token. Must not live in the Nix store.
namenetworking.hostNameFriendly name shown for this Client in the admin portal.
idnullStable identifier for this Client. When null, one is generated and persisted on first run.
apiUrlwss://api.firezone.dev/WebSocket URL of the Firezone control plane.
dnsControl"systemd-resolved"DNS control method: systemd-resolved, etc-resolv-conf, or disabled.
logLevel"info"MAX_PARTITION_TIME
FIREZONE_NO_TELEMETRYfalseSet to true to disable the Sentry crash-reporting agent.
RUST_LOG directives for the Client.
enableTelemetrytrueWhether to allow crash reporting and telemetry.
extraEnvironment{ }Extra FIREZONE_* environment variables for the service.

dnsControl = "systemd-resolved" (the default) requires services.resolved.enable = true.

Imperative install

To run the Client without the NixOS module — for example to try it out — install the package into your profile:

nix profile install github:firezone/firezone/headless-client-1.5.10#firezone-headless-client

Or run it directly without installing:

sudo nix run github:firezone/firezone/headless-client-1.5.10#firezone-headless-client

Configure the imperative Client with the same environment variables documented in the usage and environment variable reference sections below.

Usage

Signing in

The Client must be authenticated before it can connect. Pick one of the two flows below depending on whether you want to authenticate as a service account or as a user.

With a service account token

Service account tokens are long-lived and ideal for unattended systems. Generate one using the instructions in the service account documentation, then start the Client with the token in the environment:

sudo FIREZONE_TOKEN=<TOKEN> ./firezone-client-headless-linux_1.5.8_x86_64
sudo is required to control the system's DNS.

Set some environment variables to configure it:

export FIREZONE_NAME="Development API test client"
export FIREZONE_ID=$(head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1)
export FIREZONE_TOKEN=<TOKEN>
export LOG_DIR="./"
sudo -E ./firezone-client-headless-linux_1.5.8_x86_64

See below for a full list of environment variables.

Browser-based authentication

If you want to authenticate the Client as a normal user with your identity provider, use the sign-in subcommand. The Client prints a URL for you to open in any browser; after signing in, the portal displays a token that you paste back into the terminal. The token is then stored on disk at /etc/dev.firezone.client/token (configurable with --token-path or FIREZONE_TOKEN_PATH) and reused on subsequent runs.

sudo ./firezone-client-headless-linux_1.5.8_x86_64 sign-in \
    --account-slug <YOUR_ACCOUNT_SLUG>

The --account-slug flag is optional; if omitted, the URL takes you to the generic sign-in page where you can pick your account.

Once a token is saved you can start the Client without setting FIREZONE_TOKEN:

sudo ./firezone-client-headless-linux_1.5.8_x86_64

To remove a saved token, run sign-out:

sudo ./firezone-client-headless-linux_1.5.8_x86_64 sign-out

User tokens expire based on the client session lifetime configured for your auth provider in the Firezone admin portal (not your identity provider's own session lifetime). When the token expires the Client will fail to authenticate, and you'll need to run sign-in again. For long-running unattended deployments, prefer a service account token.

Accessing a Resource

When Firezone is signed in, HTTP clients, SQL clients, and other programs will automatically use it to securely connect to Resources.

Split DNS

By default, Split DNS is enabled for the Linux headless Client as of version 1.1.5.

To disable Split DNS for the Linux Client, set the FIREZONE_DNS_CONTROL environment variable to disabled.

To control /etc/resolv.conf directly, set FIREZONE_DNS_CONTROL to etc-resolv-conf.

Read more below to figure out which DNS control method is appropriate for your system.

systemd-resolved

On most modern Linux distributions, DNS resolution is handled by systemd-resolved. If this is the case for you, do not set FIREZONE_DNS_CONTROL. If you're not sure whether you use systemd-resolved, you can check by running the following command:

systemctl status systemd-resolved

Ensure that /etc/resolv.conf is a symlink to /run/systemd/resolve/stub-resolv.conf:

# Check if /etc/resolv.conf is already a symlink to /run/systemd/resolve/stub-resolv.conf
stat /etc/resolv.conf

# If it's not, create the symlink
sudo ln -sf /run/systemd/resolve/stub-resolv.conf /etc/resolv.conf.new
sudo mv /etc/resolve.conf.new /etc/resolv.conf

NetworkManager

In most cases, if you're using NetworkManager your system already uses systemd-resolved, and you should leave FIREZONE_DNS_CONTROL unset, which will use the default systemd-resolved DNS control method.

When NetworkManager detects that symlink exists, it will automatically use systemd-resolved for DNS resolution and no other configuration is necessary.

/etc/resolv.conf

If you're not using systemd-resolved, Firezone supports using the /etc/resolv.conf file to configure Split DNS as a fallback. To do this, set FIREZONE_DNS_CONTROL to etc-resolv-conf, and the Linux Client will override the /etc/resolv.conf file with the Firezone internal proxy.

When the Linux Client process exits, it will revert the /etc/resolv.conf file back to its original state. If for some reason this isn't the case, you can easily restore it by running the following command:

sudo mv /etc/resolv.conf.before-firezone /etc/resolv.conf

Read more about how DNS works in Firezone.

Help output

> sudo ./firezone-client-headless-linux_1.5.8_x86_64 --help

Command-line args for the headless Client

Usage: firezone-client-headless-linux_1.5.8_x86_64 [OPTIONS] [COMMAND]

Commands:
  sign-in   Sign in via browser-based authentication
  sign-out  Sign out by removing the stored token
  help      Print this message or the help of the given subcommand(s)

Options:
      --dns-control <DNS_CONTROL>
          Possible values:
          - disabled:         Explicitly disable DNS control
          - etc-resolv-conf:  Back up `/etc/resolv.conf` and replace it with our own
          - systemd-resolved: Cooperate with `systemd-resolved`

          [env: FIREZONE_DNS_CONTROL=]
          [default: systemd-resolved]

  -l, --log-dir <LOG_DIR>
          File logging directory. Should be a path that's writeable by the current user

          [env: LOG_DIR=]

  -m, --max-partition-time <MAX_PARTITION_TIME>
          Maximum length of time to retry connecting to the portal if we're having internet issues or it's down. Accepts human times. e.g. "5m" or "1h" or "30d"

          [env: MAX_PARTITION_TIME=]

      --firezone-name <FIREZONE_NAME>
          Friendly name for this client to display in the UI

          [env: FIREZONE_NAME=]

  -i, --firezone-id <FIREZONE_ID>
          Identifier used by the portal to identify and display the device

          [env: FIREZONE_ID=]

      --activate-internet-resource
          Activate the Internet Resource.

          To actually use the Internet Resource, the user must also have a policy granting access to the Internet Resource.

          [env: FIREZONE_ACTIVATE_INTERNET_RESOURCE=]

      --no-telemetry
          Disable sentry.io crash-reporting agent

          [env: FIREZONE_NO_TELEMETRY=]

      --token-path <TOKEN_PATH>
          A filesystem path where the token can be found

          [env: FIREZONE_TOKEN_PATH=]
          [default: /etc/dev.firezone.client/token]

  -h, --help
          Print help (see a summary with '-h')

  -V, --version
          Print version

The sign-in subcommand accepts --auth-base-url (FIREZONE_AUTH_BASE_URL, default https://app.firezone.dev) and --account-slug (FIREZONE_ACCOUNT_SLUG). The sign-out subcommand accepts -f, --force to skip the confirmation prompt.

Environment variable reference

Variable NameDefault ValueDescription
FIREZONE_TOKENToken used to authenticate the Client. Either a service account token from the admin portal or a user token obtained via sign-in. If unset, the Client reads the token from FIREZONE_TOKEN_PATH.
FIREZONE_TOKEN_PATH/etc/dev.firezone.client/tokenFilesystem path the Client reads the token from when FIREZONE_TOKEN is unset, and the path the sign-in subcommand writes to.
FIREZONE_ACTIVATE_INTERNET_RESOURCEWhether to activate the Internet Resource for this Client. Default is blank, which is disabled. Set to 1 or true to enable.
FIREZONE_NAME<system hostname>Friendly name for this client to display in the UI.
FIREZONE_IDIdentifier used by the portal to identify this client for metadata and display purposes.
FIREZONE_DNS_CONTROL(blank)The DNS control method to use. The default is systemd-resolved. Set this to disabled to disable DNS control, or etc-resolv-conf to use the /etc/resolv.conf file. Do not use etc-resolv-conf if /etc/resolv.conf is not a regular file, e.g. if it's a symlink to /run/systemd/resolve/stub-resolv.conf
LOG_DIRFile logging directory. Should be a path that's writeable by the current user. If unset, logs will be written to stdout only.
RUST_LOGinfoLog level for the client. Set to debug for verbose logging. Read more about configuring Rust log levels here.

Upgrading

  1. Download a newer binary from one of the links above.
  2. Stop the running Client.
  3. Replace the existing binary with the new one.
  4. Start the Client with the same environment variables as before.

Diagnostic logs

Firezone writes log files to disk. These logs stay on your computer and are not transmitted anywhere. If you encounter a bug, sending us the files may help us fix the bug.

Uninstalling

  1. Stop the running Client
  2. Delete the binary

Troubleshooting

For DNS checks, reverting DNS control after a crash, and known issues, see the Troubleshooting guide.


Need help? See all support options.

Found a problem with this page? Open an issue
Last updated: July 01, 2026