Hardening self-hosted GitLab runners
Isolate jobs, scope CI tokens, and pin executors so one poisoned pipeline can't pivot into your whole GitLab runner fleet or leak another team's secrets.
A self-hosted runner executes whatever a pipeline tells it to — including code from a fork or a poisoned dependency. If runners are shared, privileged, or over-scoped, one malicious job owns your whole CI fleet. Isolation and least privilege are the whole game.
Do not run privileged
Privileged runners exist mostly to build images with docker-in-docker. Use a rootless builder (Kaniko, Buildah) instead and keep the executor unprivileged.
[[runners]]executor = "docker"[runners.docker]privileged = falsevolumes = ["/cache"] # never mount /var/run/docker.sock
Scope the tokens
Use short-lived CI_JOB_TOKEN with the narrowest project scope, protect secret variables to protected branches, and give each team its own runner rather than one fleet for everything.