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:
flux-coreis installed.FLUX_SERVER_HOSTandFLUX_SERVER_PORTpoint at the target Flux server (or--server-urlis passed toflux workflow register).FLUX_AUTH_TOKENis 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:
- Separate Flux servers per environment.
- Separate principal + API key per environment.
- Separate GitHub Actions environments (
environment: staging,environment: production), each with its ownFLUX_AUTH_TOKEN,FLUX_SERVER_HOST, andFLUX_SERVER_PORTsecrets. - One workflow file per environment, or one workflow with a matrix and per-environment
environment:keys.
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
FLUX_AUTH_TOKENnot set, no--server-url. The CLI tries to hithttp://localhost:8000with no auth header and fails confusingly. Always set both env vars in the job.- Workflow file not found at registration time. Paths are relative to the runner’s working directory;
actions/checkoutputs you at the repo root. Use absolute paths or${{ github.workspace }}if you have multiple checkouts. - Version doesn’t bump.
flux workflow registerversions by content hash. Re-registering the same source is a no-op — you’ll see the same version returned. That’s correct behavior; if your change isn’t taking effect, the file you’re registering doesn’t contain the change. - API key with too-narrow permissions. A
viewerkey can list workflows but can’t register. Useoperatorfor deploys; reserveadminfor principal/role management itself.
See also
- GitLab CI — same shape, different YAML.
- Reference → CLI → workflow — full
flux workflowsubcommand reference. - Operate → Security → Principals and API keys — full key lifecycle.