Direct Answer
Podman's default --pull=missing policy reuses cached layers and manifests keyed by digest. When a registry retags an image (new tag, same digest), Podman sees no digest change and serves the local cache — even if the tag now points to different content. There is no built-in TTL eviction. The practical middle ground is: use --pull=newer (Podman 4.4+) for routine pulls, schedule conservative podman image prune -a --filter until=72h to evict stale layers, and enforce --pull=always only in CI/CD steps that require guaranteed freshness. The robust fix is to stop relying on mutable tags entirely — pin deployments to digests (image@sha256:…) or use immutable tags (git SHA, timestamp).
Why the Cache Serves Stale Content
Podman stores images by content-addressable digest. A podman pull with the default policy fetches the remote manifest, compares its digest to the local store, and downloads layers only when the digest differs. If a CI pipeline retags myapp:v1.2 to point to a new build without changing the digest (common when the build is reproducible), the registry manifest digest stays the same. Podman therefore treats it as unchanged and reuses the cached layers. The local tag myapp:v1.2 still points to the old digest, so containers started from that tag run the old image.
Pull Policies Compared
| Policy | Behavior | Traffic | Freshness Guarantee |
|---|
--pull=missing (default) | Reuse local manifest/layers if digest matches | Minimal | None for retags |
--pull=always | Fetch manifest and all layers every time | Maximum | Full (but re-downloads unchanged layers) |
--pull=newer (4.4+) | Fetch manifest; compare Last-Modified; pull only if remote newer | Low (manifest only unless changed) | Depends on registry timestamp accuracy |
Caution: --pull=newer relies on the registry updating Last-Modified on tag mutation. Docker Hub, GHCR, and many others do not update this header when a tag is moved, so the policy may not help in retag scenarios.
Middle-Ground Strategy
- Default to
--pull=newer for interactive and scheduled pulls. It avoids full downloads when the manifest timestamp hasn't changed. - Schedule pruning with a safe window:
podman image prune -a --filter until=72h --dry-run first, then remove --dry-run. This evicts layers unreferenced for 72 hours, reducing stale cache buildup without deleting active bases. - Enforce
--pull=always only in deployment pipelines where you must guarantee the latest tag content. Pair with authenticated pulls to mitigate rate limits. - Pin production references to digests:
myapp@sha256:abc123…. This eliminates mutable-tag ambiguity entirely.
Verification Steps
# 1. Observe default behavior
podman pull --pull=missing alpine:latest
podman images --digests
# 2. Simulate remote retag (requires registry access)
# 3. Compare policies
podman pull --pull=always alpine:latest
podman pull --pull=newer alpine:latest
# 4. Check reclaimable space before pruning
podman system df -v
podman image prune -a --filter until=24h --dry-run
One Diagnostic Detail Needed
Which registry are you pulling from? The effectiveness of --pull=newer depends entirely on whether that registry updates Last-Modified on tag mutation. If it does not (Docker Hub, GHCR, Quay default), --pull=newer provides no benefit over --pull=missing for retag scenarios, and you must rely on pruning + digest-pinning.