GitLab CI
Register Flux workflows from a GitLab CI/CD pipeline.
The pattern is identical to GitHub Actions: install flux-core on the runner, set FLUX_SERVER_HOST / FLUX_SERVER_PORT / FLUX_AUTH_TOKEN, run flux workflow register. The differences are GitLab-specific: how secrets are declared, how environments are scoped, and how to gate production behind a manual job.
Minimal .gitlab-ci.yml
stages:
- deploy
deploy-flux:
stage: deploy
image: python:3.14-slim
rules:
- if: $CI_COMMIT_BRANCH == "main"
changes:
- workflows/**/*.py
variables:
FLUX_SERVER_HOST: flux.internal.example.com
FLUX_SERVER_PORT: "8000"
before_script:
- pip install --upgrade pip
- pip install flux-core==0.56.0
script:
- |
for f in workflows/*.py; do
echo "Registering $f"
flux workflow register "$f"
done
FLUX_AUTH_TOKEN is not in the YAML — it comes from a CI/CD variable defined in Settings → CI/CD → Variables. Mark it Masked (so it doesn’t appear in job logs) and Protected (so it’s only available on protected branches and tags). For production deploys, both flags should always be on.
Environments and protection
GitLab’s environment: keyword scopes variables per environment. The conventional structure:
.deploy: &deploy
stage: deploy
image: python:3.14-slim
before_script:
- pip install flux-core==0.56.0
script:
- flux workflow register workflows/main.py
deploy:staging:
<<: *deploy
environment:
name: staging
url: https://flux-staging.internal
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy:production:
<<: *deploy
environment:
name: production
url: https://flux.internal
rules:
- if: $CI_COMMIT_TAG
when: manual
Define FLUX_AUTH_TOKEN, FLUX_SERVER_HOST, and FLUX_SERVER_PORT separately for each environment via Settings → CI/CD → Variables → Environments. GitLab resolves the right value at job runtime; the YAML stays environment-agnostic.
when: manual on the production job means a human has to click “Play” in the pipeline view. Combined with Protected environments (Settings → CI/CD → Protected environments), only specific roles can trigger the deploy.
API key
Same as GitHub Actions: create a service-account principal once, mint an API key, store it as the masked-and-protected FLUX_AUTH_TOKEN variable.
flux principals create ci-gitlab --type service_account --role operator
flux principals create-key ci-gitlab --key-name gitlab-ci-prod --expires 365d
The operator role can register workflows and run executions. Reserve a separate principal per environment so a leaked staging key can’t deploy to production.
Self-hosted runners and network paths
If the Flux server lives on a private network, the runner needs to reach it. Two common shapes:
- GitLab-hosted runner + public Flux endpoint. Put the Flux server behind a reverse proxy with an IP allowlist; resolve
FLUX_SERVER_HOSTto the public name; setFLUX_AUTH_TOKENas usual. - Self-hosted runner inside the same network. No proxy needed. Tag the runner (
tags: [internal]) and pin the deploy job to it.
What goes wrong
- Variable not in scope. A variable defined for
productionis not visible to thestagingjob. Watch theenvironment:key. Maskedrejected the value. Masked variables can’t contain certain characters (older GitLab versions reject=,+, etc.). Re-mint the key if masking fails — flux API keys are generated, not user-chosen.- Unprotected branch, protected variable. A feature branch can’t read a protected variable. Either remove the protected flag (for non-production environments) or restrict the job to
main/tags. - Workflow file not under the registered path.
flux workflow registertakes a file path, not a glob. Loop in shell or list the files explicitly.
See also
- GitHub Actions — same pattern, more detail on multi-environment promotion.
- Reference → CLI → workflow — full
flux workflowsubcommand reference.