Safeguard
Security Guides•

Should CI job containers run as root?

Run CI job containers as a non-root user where you can. Here is what that limits, what it breaks, and how to fix the common build failures.

Safeguard Security Team
3 min read

Run CI job containers as a non-root user wherever your build allows it. A non-root process has fewer ways to tamper with the container, install system packages or exploit a container escape. It does not make a hostile job harmless, but it removes an easy step from many attacks.

What the platform guidance says

GitLab's runner security guidance says its Docker executor can be considered safe when running in non-privileged mode, and that to make this more secure you should run jobs as a non-root user in Docker containers, with sudo disabled or the SETUID and SETGID capabilities dropped. The same page covers more granular permission options for Kubernetes jobs. GitHub's container job syntax likewise lets you pass container options, including a user, when running a job in a container.

What non-root actually limits

  • Writing to system directories and replacing system binaries.
  • Installing packages with a system package manager at job time.
  • Binding to low numbered ports.
  • Many kernel interfaces that need elevated capabilities.

It does not stop a job from reading secrets the job itself was given, or from calling the network.

Why builds break and how to fix them

SymptomLikely causeFix
Permission denied writing to the workspaceFiles owned by another userSet ownership in the image, or run with a matching user ID
Package install failsNeeds system privilegesInstall it in the image at build time
Cannot bind to port 80Low port needs privilegeUse a high port in tests
Cache directory not writableMounted as rootCreate it with correct ownership in the image

Most failures point to something that should have been in the image anyway.

Steps to roll it out

  1. Create a non-root user in the runner image with a fixed user ID.
  2. Fix ownership of the directories jobs write to.
  3. Switch one pipeline to the non-root image and fix failures.
  4. Set the platform's allowed users list, if it has one, so pipelines cannot quietly request root.
  5. Disable sudo in the image, or remove it.
  6. Drop Linux capabilities the job does not need, and do not run privileged containers for ordinary builds.

Jobs that really need root

Some jobs, such as building images with a daemon, need elevated rights. Isolate them on dedicated runners rather than loosening every pipeline. See dedicated runners for privileged CI jobs.

Quick check

Is a container root the same as host root?

Not by default, but a privileged container or a mounted Docker socket can reach host level control. Avoid both for untrusted jobs.

Do I need non-root if the runner is deleted after each job?

Yes. Deleting the runner limits persistence, not what the job can do while it runs.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.