GitHub Actions

Register Flux workflows from a GitHub Actions pipeline.

“Deploying” a Flux workflow is flux workflow register <file.py> against a running Flux server. The server compiles, versions, and stores the workflow; workers pick it up the next time they poll for work. CI/CD is a thin wrapper around that single command.

This page shows the GitHub Actions shape. The same pattern works for GitLab CI (see that page), CircleCI, Buildkite, and friends.

The shape of a deploy

For a single environment, three things need to be true on the runner:

  1. flux-core is installed.
  2. FLUX_SERVER_HOST and FLUX_SERVER_PORT point at the target Flux server (or --server-url is passed to flux workflow register).
  3. FLUX_AUTH_TOKEN is set to an API key with permission to register workflows.

That’s it. The CLI handles the rest.

Example workflow

# .github/workflows/deploy-flux.yml
name: Deploy Flux workflows

on:
  push:
    branches: [main]
    paths:
      - "workflows/**.py"
      - "pyproject.toml"

jobs:
  register:
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: "3.14"

      - name: Install flux-core
        run: |
          python -m pip install --upgrade pip
          pip install flux-core==0.56.0

      - name: Register workflows
        env:
          FLUX_SERVER_HOST: flux.internal.example.com
          FLUX_SERVER_PORT: "8000"
          FLUX_AUTH_TOKEN: ${{ secrets.FLUX_AUTH_TOKEN }}
        run: |
          for f in workflows/*.py; do
            echo "Registering $f"
            flux workflow register "$f"
          done

flux workflow register POSTs the file to /workflows. Re-registering the same workflow bumps its version; existing executions continue against the version that claimed them, new executions get the latest. There is no separate “deploy” step.

API keys for CI

Flux’s CLI auth lives under flux principals create-key. Create a service-account principal once, mint a key for CI, and store the key as a GitHub Actions secret:

# On a machine that already has admin auth to the Flux server:
flux principals create ci-github --type service_account --role operator
flux principals create-key ci-github --key-name github-actions-prod --expires 365d
# → "API key created: flux_pk_..."

Store the returned key as the FLUX_AUTH_TOKEN secret in the repository (or organization, if multiple repos share it). The CLI reads FLUX_AUTH_TOKEN from the environment and sends it as Authorization: Bearer <token> on every request (flux/cli_auth.py::get_auth_headers).

The operator built-in role can register workflows and run executions but cannot create users or roles. That’s the right least-privilege level for CI.

Multiple environments

For staging + production, the simplest setup keeps them fully separate:

jobs:
  register:
    strategy:
      matrix:
        target:
          - environment: staging
            host: flux-staging.internal
          - environment: production
            host: flux.internal
    environment: ${{ matrix.target.environment }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.14" }
      - run: pip install flux-core==0.56.0
      - env:
          FLUX_SERVER_HOST: ${{ matrix.target.host }}
          FLUX_SERVER_PORT: "8000"
          FLUX_AUTH_TOKEN: ${{ secrets.FLUX_AUTH_TOKEN }}
        run: flux workflow register workflows/main.py

GitHub’s environment-scoped secrets mean secrets.FLUX_AUTH_TOKEN resolves to a different value per matrix entry. The job-level environment: key is what does the routing.

Promotion gates

Wire the deploy job behind a manual approval by setting Required reviewers on the GitHub environment. The deploy job won’t run until a reviewer approves. Combine with on: workflow_dispatch if you want operator-triggered deploys rather than push-triggered ones.

What goes wrong

See also