GitLab CI Error Guide: 'pull policy ... is not one of the allowed_pull_policies'
Fix GitLab Runner's 'pull_policy not allowed' error: align job-level image pull_policy with the runner's allowed_pull_policies in config.toml.
- #gitlab-cicd
- #troubleshooting
- #errors
- #runner-config
Exact Error Message
A job that sets a pull_policy on its image is rejected before it even starts:
Preparing the "docker" executor
ERROR: Preparation failed: the configured PullPolicies ([always]) are not one of the
allowed_pull_policies ([if-not-present])
ERROR: Job failed (system failure): the configured PullPolicies ([always]) are not one of the
allowed_pull_policies ([if-not-present])
The Kubernetes executor produces the same idea with slightly different wording:
ERROR: Preparation failed: invalid pull policy for container "build":
pull policy ([Always]) defined in GitLab pipeline config is not one of the
allowed_pull_policies ([IfNotPresent])
What the Error Means
GitLab Runner (15.1+) lets jobs request an image pull policy per job (image: { name, pull_policy }). To stop pipelines from forcing expensive or unsafe pulls, runner administrators can restrict which policies jobs may request via allowed_pull_policies in config.toml. When a job asks for a policy that is not in that allow-list, the runner refuses to prepare the executor and fails the job as a system failure — your script never runs.
This is an authorization-style guardrail, not a registry or network problem. The job and runner simply disagree about which pull policies are permitted.
Common Causes
- Job sets
pull_policy: alwaysbut the runner only allowsif-not-present(or vice versa). - A pipeline migrated from a runner with no restriction to one that enforces
allowed_pull_policies. - Kubernetes executor case/format —
Always/IfNotPresentvsalways/if-not-presentconfusion across docs. - A
servicesimage requesting a disallowed policy even though the mainimageis fine. - A shared runner fleet where one runner enforces the allow-list and you assumed all do.
How to Reproduce the Error
Point a job at a runner whose config.toml allows only if-not-present, then request always:
build:
image:
name: alpine:3.20
pull_policy: always
script:
- echo "this never runs"
ERROR: Job failed (system failure): the configured PullPolicies ([always]) are not one of the
allowed_pull_policies ([if-not-present])
Diagnostic Commands
Read-only inspection of the runner side (run on the runner host) and the job side:
# What does this runner allow?
grep -n -A2 allowed_pull_policies /etc/gitlab-runner/config.toml
# What pull_policy is the default for this runner's docker executor?
grep -n pull_policy /etc/gitlab-runner/config.toml
# Confirm the runner version supports allowed_pull_policies (>=15.1)
gitlab-runner --version
# For Kubernetes executor, see the allow-list in the runner config map
kubectl -n gitlab-runner get configmap -o yaml | grep -A2 allowed_pull_policies
allowed_pull_policies = ["if-not-present"]
pull_policy = ["if-not-present"]
If the printed allowed_pull_policies does not contain the policy your job requested, you have found the cause.
Step-by-Step Resolution
You can fix this from either side — change the job to request an allowed policy, or widen the runner’s allow-list. Choose based on who owns the runner.
1. Make the job request an allowed policy (job owner)
If you don’t administer the runner, match what it permits:
build:
image:
name: alpine:3.20
pull_policy: if-not-present
script:
- echo "ok"
Or omit pull_policy entirely to use the runner’s default, which is always within the allow-list.
2. Widen the runner’s allow-list (runner admin)
If forcing a fresh pull is legitimate (e.g. mutable tags), add the policy to config.toml and restart:
[[runners]]
executor = "docker"
[runners.docker]
image = "alpine:3.20"
pull_policy = ["always", "if-not-present"]
allowed_pull_policies = ["always", "if-not-present"]
allowed_pull_policies controls what jobs may request; pull_policy is the default when a job specifies none. Apply with sudo gitlab-runner restart.
3. Match case on the Kubernetes executor
The K8s executor maps policies to Kubernetes imagePullPolicy values. Use the GitLab spellings in the job (always, if-not-present, never); the runner translates them to Always/IfNotPresent/Never. Ensure allowed_pull_policies in the runner config uses the same lowercase GitLab spellings.
4. Don’t forget service images
A services: entry can also carry a pull_policy. If only the service trips the guard, align it too:
services:
- name: postgres:16
pull_policy: if-not-present
5. Re-run and confirm
Once the requested policy is within the allow-list, the runner prepares the executor normally and your script runs.
Prevention and Best Practices
- Decide a fleet-wide policy: pin immutable image tags and standardise on
if-not-presentso jobs rarely needalways. - If teams need
alwaysfor mutable tags, add it toallowed_pull_policiesexplicitly rather than removing the guard. - Document each runner’s
allowed_pull_policiesso job authors know what to request; mismatched assumptions cause most of these failures on shared fleets. - Keep job
image.pull_policyspellings in GitLab’s lowercase form (if-not-present), not Kubernetes’IfNotPresent. - Pasting the preparation log into the free incident assistant immediately shows the requested-vs-allowed mismatch. More patterns live in the GitLab CI/CD guides.
Related Errors
- ERROR: Preparation failed: failed to pull image — a different preparation failure where the policy was allowed but the pull itself failed (auth/network).
Job failed (system failure)— the generic class this error belongs to; preparation errors always present as system failures.ImagePullBackOff— the Kubernetes-side symptom when the pull is attempted but the registry rejects it.
Frequently Asked Questions
Why is this a “system failure” and not a normal job failure?
The runner rejects the job during executor preparation, before your script runs. Anything that fails at prepare time is reported as a system failure. Your build never executed, so retries with the same config will fail identically.
I don’t manage the runner — what’s the quickest fix?
Change your job’s image.pull_policy to a value in the runner’s allow-list (often if-not-present), or remove pull_policy altogether to inherit the runner default. Both avoid touching config.toml.
Does allowed_pull_policies differ from pull_policy?
Yes. pull_policy is the runner’s default when a job specifies none; allowed_pull_policies is the whitelist of policies a job is permitted to request. A job is rejected when its requested policy is outside allowed_pull_policies.
My Kubernetes runner uses IfNotPresent — should the allow-list match?
In the runner config and job YAML use GitLab’s lowercase spellings (if-not-present); the executor translates them to the Kubernetes IfNotPresent form internally. Mixing the two spellings is a common cause of the mismatch.
Download the Free 500-Prompt DevOps AI Toolkit
500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.
- 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
- Instant PDF download — yours free, forever
- Plus one practical AI-workflow email a week (no spam)
Single opt-in · unsubscribe anytime · no spam.