Access Resources from GitHub Actions CI

This guide shows how to reach private Resources — a staging database, an internal API, a private package registry — from a GitHub Actions workflow. Because a CI runner is unattended, it authenticates with a service account token rather than a browser sign-in, then connects with the headless Linux Client.

Step 1: Create a service account and token

  1. In the admin portal, go to Actors → Add Actor.
  2. Select Service Account as the type and give it a name (e.g. github-ci).
  3. Set an appropriate token expiration.
  4. Copy the token and store it somewhere safe — it's only shown once.

You can add multiple tokens to a service account (under Actors → <account> → ⋮ → Add Token) to rotate credentials or issue a separate token per repository, and revoke any token from the service account's detail page. Tokens default to a 365-day lifetime.

Step 2: Grant the service account access

Add the service account to a Group, then create a Policy granting that Group access to the Resource your workflow needs. Without a Policy, the CI runner will connect but won't be able to reach anything.

Step 3: Store the token as a GitHub secret

In your repository, go to Settings → Secrets and variables → Actions and add a new secret named FIREZONE_TOKEN containing the token from Step 1.

Step 4: Connect from your workflow

Add a step that downloads the headless Linux Client, starts it with the service account token, and waits for the tunnel to come up before the rest of your job runs:

jobs:
  integration-tests:
    runs-on: ubuntu-latest
    steps:
      - name: Start Firezone Client
        env:
          FIREZONE_TOKEN: ${{ secrets.FIREZONE_TOKEN }}
        run: |
          curl -fsSL -o firezone-client \
            https://www.firezone.dev/dl/firezone-client-headless-linux/latest/x86_64
          chmod +x firezone-client
          # A unique ID per run keeps concurrent jobs from sharing an identity.
          export FIREZONE_ID=$(head -c 32 /dev/urandom | sha256sum | cut -d' ' -f1)
          export FIREZONE_NAME="github-actions-${{ github.run_id }}"
          sudo --preserve-env=FIREZONE_TOKEN,FIREZONE_ID,FIREZONE_NAME \
            ./firezone-client &
          # Give the tunnel a moment to establish before continuing.
          sleep 5

      - name: Run tests against the private Resource
        run: |
          # The Resource is now reachable by its defined address, e.g.:
          psql "postgres://ci@db.internal.example.com/app" -c '\dt'

Resources are reached by the address defined in Firezone (DNS name, IP, or CIDR), exactly as you configured the Resource. Make sure the Policy from Step 2 grants this service account access, and that a Gateway in the Resource's Site is online.

The Client runs in the background for the life of the job; GitHub tears the runner down when the job finishes, so there's no explicit sign-out step. For long-lived self-hosted runners, sign out or stop the Client when you're done.


Need help? See all support options.

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