By default, Compose pulls an image when it’s missing locally and uses the cached one otherwise. pull_policy lets you change that behavior per service.
The policies
services:
web:
image: nginx
pull_policy: always
Available values:
missing(default when nobuild:is defined): pull only if the image is not present locally. Alias:if_not_present. Note: thelatesttag is always pulled even with this policy.always: pull on everyup, even if the local image is recentnever: don’t pull, fail if the image is missingbuild: build the image, rebuilds even if it’s already present locallydaily: check the registry if the last pull was more than 24 hours agoweekly: check the registry if the last pull was more than 7 days agoevery_<duration>: check the registry if the last pull was older than the given duration. Units:w,d,h,m,s(or any combination, e.g.every_12h,every_30m,every_1d12h)
When to use each
always for development against a fast-moving tag:
services:
api:
image: myregistry/api:latest
pull_policy: always
Every docker compose up checks the registry. Useful when you want to track the latest build of a feature branch image.
never in CI to catch accidental dependencies on registries:
services:
app:
image: myregistry/app:${CI_COMMIT_SHA}
pull_policy: never
If the image isn’t already on the runner, the build fails fast. This forces explicit docker compose pull steps and prevents flaky CI runs caused by registry outages.
build to force a local build:
services:
app:
image: myapp:dev # used for tagging the built image
build: ./app
pull_policy: build
Without pull_policy: build, Compose would pull myapp:dev if available and skip the build: step. This makes local builds authoritative.
daily, weekly, or every_<duration> for periodic refreshes:
services:
scanner:
image: security-scanner:latest
pull_policy: daily
test-runner:
image: my-test-suite:main
pull_policy: every_12h # twice a day
Refreshes without the cost of always on every command. every_<duration> accepts combinations like every_2d, every_1h30m, every_45s for fine-grained control.
CLI overrides
The --pull flag on up overrides the file setting for that run:
# Force a pull regardless of pull_policy
docker compose up --pull always
# Skip pulls
docker compose up --pull never
# Standalone pull command (respects pull_policy unless --policy given)
docker compose pull
docker compose pull --policy always
Handy for one-off behavior changes without editing the file.
Match the policy to your tag
The right policy depends on how mutable your image tag is:
# Immutable: pinned by digest, no point ever re-pulling
image: nginx@sha256:abc123...
pull_policy: missing
# Slow-moving: latest tag, refresh periodically
image: nginx:latest
pull_policy: daily
# Fast-moving: branch tag that changes often, always grab the newest
image: myteam/app:main
pull_policy: always
A digest-pinned image never changes, so missing is enough. A :latest tag moves occasionally, so daily or weekly keeps you reasonably current without thrashing. A branch tag like :main may change every commit, so always is the safe default.