Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for GitLab CI/CD By James Joyner IV · · 9 min read

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

  1. Job sets pull_policy: always but the runner only allows if-not-present (or vice versa).
  2. A pipeline migrated from a runner with no restriction to one that enforces allowed_pull_policies.
  3. Kubernetes executor case/formatAlways/IfNotPresent vs always/if-not-present confusion across docs.
  4. A services image requesting a disallowed policy even though the main image is fine.
  5. 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-present so jobs rarely need always.
  • If teams need always for mutable tags, add it to allowed_pull_policies explicitly rather than removing the guard.
  • Document each runner’s allowed_pull_policies so job authors know what to request; mismatched assumptions cause most of these failures on shared fleets.
  • Keep job image.pull_policy spellings 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.
  • 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.

Free download · 368-page PDF

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.