Deploy a Gateway with Docker

Run the Firezone Gateway as a Docker container on any host with Docker Engine for Linux. Docker Desktop can be used for testing but is not supported for production.

Before you start, open Sites → <your Site> → Deploy a Gateway in the admin portal, select the Docker tab, and copy the FIREZONE_TOKEN value. Review the sizing guidelines first.

Run the container

Substitute your token and a unique ID, then run:

docker run -d \
  --restart=unless-stopped \
  --pull=always \
  --health-cmd="ip link | grep tun-firezone" \
  --name=firezone-gateway \
  --cap-add=NET_ADMIN \
  --sysctl net.ipv4.ip_forward=1 \
  --sysctl net.ipv4.conf.all.src_valid_mark=1 \
  --sysctl net.ipv6.conf.all.disable_ipv6=0 \
  --sysctl net.ipv6.conf.all.forwarding=1 \
  --sysctl net.ipv6.conf.default.forwarding=1 \
  --device="/dev/net/tun:/dev/net/tun" \
  --env FIREZONE_TOKEN="<your-token>" \
  --env FIREZONE_ID="<unique-id>" \
  --env FIREZONE_NAME=$(hostname) \
  --env RUST_LOG=info \
  ghcr.io/firezone/gateway:1

FIREZONE_ID must be unique across all Gateways in your account. To deploy multiple Gateways in a Site, reuse the same FIREZONE_TOKEN with a different FIREZONE_ID on each host. See the Gateway CLI reference for all environment variables.

The Gateway can't raise the host's UDP buffer sizes from inside Docker (it mounts /proc/sys read-only), which can limit throughput. See performance tuning.

Docker Compose reference

To manage the Gateway declaratively, use the following docker-compose.yml. It uses the same options as the docker run command above:

services:
  firezone-gateway:
    image: "ghcr.io/firezone/gateway:1"
    environment:
      # Use a unique ID for each Gateway in your Firezone account. If left blank,
      # the Gateway will generate a random ID saved in /var/lib/firezone
      # FIREZONE_ID: <id>

      # REQUIRED. The token shown when deploying a Gateway in the admin portal.
      FIREZONE_TOKEN: <token>

      # Configure log output. Other options are "trace", "debug", "info", "warn", "error", and "off".
      # See https://docs.rs/env_logger/latest/env_logger/ for more information.
      RUST_LOG: info

      # Human-friendly name to use for this Gateway in the admin portal.
      # $(hostname) is used by default if not set.
      # FIREZONE_NAME: <name-of-gateway>
    volumes:
      # Persist the FIREZONE_ID. Can be omitted if FIREZONE_ID is set above.
      - /var/lib/firezone:/var/lib/firezone
    cap_add:
      - NET_ADMIN
    # Needed to properly handle signals for restarting
    init: true
    sysctls:
      # Enable IP forwarding
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1
      - net.ipv6.conf.all.disable_ipv6=0
      - net.ipv6.conf.all.forwarding=1
      - net.ipv6.conf.default.forwarding=1
    healthcheck:
      test: ["CMD-SHELL", "ip link | grep tun-firezone"]
      interval: 5s
      timeout: 10s
      retries: 3
      start_period: 1m
    devices:
      - /dev/net/tun:/dev/net/tun
# If you want to access services within the same compose file, they must share a network.
#    networks:
#      fz-net:
#
#   # Heads-up for Linux users.
#   # Due to limitations of `systemd-resolved` on Linux, a "two-label" domain is necessary if you want to define this as a DNS resource.
#   # i.e. just using `yourservice` won't work.
#   yourservice.docker:
#     image: "..."
#     networks:
#       fz-net:
#
# networks:
#   fz-net:

Need help? See all support options.

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