macOS Headless Client

firezone-cli runs the macOS Client from a terminal, without the menu bar app in the foreground. It is intended for Macs that are managed remotely or run unattended, such as build machines.

It is not a separate program. It is the macOS Client's own binary, started under a different name, which is what lets it use the same system extension, VPN profile, and stored token as the graphical Client.

If you're looking for the graphical macOS Client, see the macOS Client.

Prerequisites

  • macOS 13 or higher
  • The macOS Client installed in /Applications
  • A Firezone account, plus admin access to create a service account if you intend to authenticate with a service account token

Location

The binary lives inside the app bundle:

/Applications/Firezone.app/Contents/MacOS/firezone-cli

Add it to your PATH to invoke it as firezone-cli:

export PATH="/Applications/Firezone.app/Contents/MacOS:$PATH"

System extension

The Client cannot start without its system extension, which is the same extension the graphical Client uses. Launch Firezone.app once to install it. Installing one requires a person to approve it, so the CLI cannot do it for you.

To avoid prompting the user for approval, allowlist the extension ahead of time with MDM. See Allowlisting the macOS System Extension.

firezone-cli extension status reports whether the extension is installed and matches the installed app, exiting non-zero if it does not:

> firezone-cli extension status
installed

Usage

Signing in

The Client never asks for a token at a prompt. It runs inside the sandboxed macOS Client, and a sandboxed process isn't allowed to turn terminal echo off, so a token typed at a prompt would sit in the terminal's scrollback. It reads one from its standard input instead.

Start the Client with no token and it tells you what to do:

No token found. To sign in:

  1. Open this in a browser and sign in:

     https://app.firezone.dev/<ACCOUNT_SLUG>?as=headless-client

  2. Copy the token.

  3. Run:

     pbpaste | /Applications/Firezone.app/Contents/MacOS/firezone-cli

A token can also be set in FIREZONE_TOKEN, or read from a file:

     /Applications/Firezone.app/Contents/MacOS/firezone-cli < token

The token is saved to the Keychain, so later runs don't need one.

pbpaste writes the clipboard straight into the Client, so the token is never displayed and never reaches your shell history. The Keychain keeps it afterwards, so later runs need no token at all.

A service account token is supplied the same way, and is the right choice for a Mac that runs unattended.

Supplying a token without a person present

Read the token from a file:

firezone-cli < /path/to/token

Or set FIREZONE_TOKEN from somewhere your shell won't record:

export FIREZONE_TOKEN="$(cat /path/to/token)"
firezone-cli

Don't put a token directly on the command line, for example by prefixing firezone-cli with FIREZONE_TOKEN=. Your shell records the whole command in its history file, where the token stays readable long after the session ends. Service account tokens are long-lived, so treat one as you would a password.

Better still, keep a shell out of it. Give the token to launchd in the job's EnvironmentVariables, or have your MDM deliver it, and keep the file readable only by the account that runs the Client.

Running

connect is the default subcommand, so firezone-cli on its own brings the tunnel up and stays in the foreground until stopped. SIGINT and SIGTERM shut the tunnel down; SIGHUP restarts it. Logs are written to standard error.

Signing out

firezone-cli sign-out

This removes the stored token from the Keychain.

Help output

> firezone-cli --help

OVERVIEW: Firezone headless Client

USAGE: firezone-cli <subcommand>

OPTIONS:
  --version               Show the version.
  -h, --help              Show help information.

SUBCOMMANDS:
  connect (default)       Bring the tunnel up and stay running. This is the
                          default.
  sign-out                Sign out and remove the stored token.
  extension               Inspect and install the system extension.

  See 'firezone-cli help <subcommand>' for detailed help.
> firezone-cli connect --help

OVERVIEW: Bring the tunnel up and stay running. This is the default.

USAGE: firezone-cli connect [--activate-internet-resource] [--account-slug <account-slug>]

OPTIONS:
  --activate-internet-resource
                          Activate Internet Resource.
  --account-slug <account-slug>
                          Account slug.
  --version               Show the version.
  -h, --help              Show help information.
> firezone-cli extension --help

OVERVIEW: Inspect the system extension.

USAGE: firezone-cli extension <subcommand>

SUBCOMMANDS:
  status                  Report whether the system extension is installed and
                          current.

Environment variable reference

Variable NameDefault ValueDescription
FIREZONE_TOKENToken used to authenticate the Client. A token piped into the Client takes precedence. If neither is given, the Client uses the token stored in the Keychain, and explains how to supply one if there isn't one.
FIREZONE_ACCOUNT_SLUGAccount slug to sign in to. Equivalent to --account-slug.
FIREZONE_ACTIVATE_INTERNET_RESOURCESet to 1 to activate the Internet Resource. Equivalent to --activate-internet-resource.
FIREZONE_LOG_FILTERinfoLog level for the Client. Set to debug for verbose logging.
FIREZONE_API_URLwss://api.firezone.devControl plane URL. Only change this if you have been asked to.
FIREZONE_AUTH_BASE_URLhttps://app.firezone.devPortal URL used to build the browser sign-in link. Only change this if you have been asked to.

Upgrading

The CLI is part of the macOS Client, so upgrade the app as usual. The system extension is upgraded with it the next time Firezone.app runs.

Diagnostic logs

The CLI writes logs to standard error. Redirect them to a file to keep them:

firezone-cli > firezone.log 2>&1

Logs from the system extension are written to the same location the graphical Client uses. See the Troubleshooting guide for more.


Need help? See all support options.

Found a problem with this page? Open an issue
Last updated: August 08, 2026