A runner image should never contain credentials, saved registry logins, SSH keys, copies of private source code, passwordless sudo, or tools no pipeline uses. Every job starts from the image, so anything inside it is available to every job and to anyone who can pull the image.
Why the image is a high value target
With ephemeral runners, the image replaces the long-lived server. It is pulled by every job, often from a registry many people can read. A secret placed inside is effectively shared with all of them, and it stays in the image layers even if a later step deletes the file.
GitLab's runner guidance gives a concrete example of the wrong pattern: credentials kept in the runner host's Docker configuration files are available to every job the runner executes, which is why it advises against running docker login as the runner user on shared runners.
Do not include
| Item | Why not | What to do instead |
|---|---|---|
| Cloud keys or API tokens | Shared by every job | Inject per job, short lived |
| Registry logins | Usable by any job | Per job token |
| SSH private keys | Reads as full access | Deploy keys per repository, issued at runtime |
| Source of private projects | Leaks code to anyone who pulls | Check out at job time with scoped access |
| Passwordless sudo | Easy privilege step | Remove sudo, fix ownership at build |
| Package manager caches with credentials | Config files can hold tokens | Clean in the same layer |
| Unused compilers and debug tools | Extra attack surface | Split images by need |
| Shell history and temp files | Can hold pasted secrets | Build from clean definition |
How to check an image
- List the layers and read the build commands, looking for copied files and environment settings.
- Search the filesystem for key files, config directories and token patterns.
- Review environment variables set at build and run time.
- Run a dependency and image scan, and read the inventory for tools nobody expects.
- Compare against the written image definition. Anything present that the definition does not explain is a finding.
Remember the limit of scanning: image scanners report known vulnerabilities and inventory, and are not a guarantee that no secret is present. Review build steps as well.
Keep it small and specific
Several smaller images with clear purposes are easier to review than one image with everything. The language build image does not need the deploy CLI, and the deploy image does not need compilers.
Keep it current
An image that is clean today ages. Rebuild on a schedule and rescan. See how often to rebuild runner images. For build time secrets in particular, see Docker build secrets without leaking into layers.
Quick check
If I delete a secret in a later layer, is it gone?
No. Earlier layers keep it and anyone who can pull the image can extract it. Rebuild without ever adding it.
Can the image hold a public CA bundle or config?
Yes. Public, non-secret configuration is fine. Keep anything that grants access out.
Sources
Last reviewed: 19 October 2026.