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: 'WARNING: Failed to create cache ... permission denied'

Fix GitLab CI cache 'permission denied' errors: align container user UID with cache-dir ownership, fix volume permissions, and set FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR.

  • #gitlab-cicd
  • #troubleshooting
  • #errors
  • #cache

Exact Error Message

The job runs, but caching silently degrades with a permission warning at archive or extract time:

Creating cache default...
WARNING: vendor/: chmod vendor/: permission denied
WARNING: Failed to create cache: archive cache: ... open
  /cache/project-42/default/cache.zip: permission denied
Failed to create cache

Or on restore:

Checking cache for default...
WARNING: Failed to extract cache: ... permission denied
Failed to extract cache

Caching failures are warnings, so the job often still succeeds — but every run rebuilds from scratch because the cache is never written or read, slowing pipelines dramatically.

What the Error Means

GitLab Runner caches by writing a cache.zip into a cache directory (a host path or volume mounted into the build container) and re-extracting it on later jobs. permission denied means the user the job runs as inside the container cannot write to the cache path, or cannot chmod/read the files being cached. The mismatch is almost always between the container’s UID/GID and the ownership of the cache directory or the cached files.

Because the runner treats cache failures as non-fatal, the symptom is “everything is slow and nothing is cached” rather than a hard error.

Common Causes

  1. Container runs as non-root (UID 1000) but the cache volume/dir is owned by root.
  2. Files written by a root step then cached by a non-root step that can’t chmod them.
  3. A read-only or wrong-permission cache volume mounted into the container.
  4. umask/permission bits stripping write access on extracted cache files (Docker executor).
  5. S3/object-storage cache with credentials lacking write permission (manifests as upload failures, related symptom).

How to Reproduce the Error

Run a non-root image against files created with restrictive ownership, then cache them:

build:
  image: node:20-alpine
  variables:
    npm_config_unsafe_perm: "false"
  script:
    - mkdir -p vendor
    - touch vendor/lib.js
    - chmod 000 vendor/lib.js     # remove all permissions
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths: ["vendor/"]
WARNING: vendor/lib.js: chmod ...: permission denied
WARNING: Failed to create cache

Diagnostic Commands

Read-only checks inside the job and on the runner host:

# Who is the job running as, inside the container?
id

# Ownership/permissions of the paths being cached
ls -la vendor/

# Ownership of the runner's cache directory (run on the runner host)
ls -ld /cache /cache/project-42 2>/dev/null

# Is the cache volume mounted read-only? Check the runner config
grep -nE 'cache_dir|volumes' /etc/gitlab-runner/config.toml
uid=1000(node) gid=1000(node)
d---------  2 root root  4096 vendor
drwxr-xr-x  3 root root  4096 /cache

A container UID of 1000 against a cache dir owned by root (or files with 000 perms) confirms the ownership mismatch.

Step-by-Step Resolution

1. Make cached files readable/writable by the job user

Fix permissions of the paths you cache before the cache step runs:

build:
  image: node:20-alpine
  script:
    - mkdir -p vendor && echo build > vendor/lib.js
    - chmod -R u+rwX vendor    # ensure the job user can read/write
  cache:
    key: "$CI_COMMIT_REF_SLUG"
    paths: ["vendor/"]

2. Align the cache directory ownership (runner admin)

For the Docker executor with a host cache_dir, ensure the directory is writable by the container UID. In config.toml:

[[runners]]
  executor = "docker"
  [runners.docker]
    cache_dir = "/cache"
    volumes = ["/srv/gitlab-runner/cache:/cache:rw"]

Then sudo chown -R 1000:1000 /srv/gitlab-runner/cache (matching your image’s UID) and make sure the volume is mounted :rw, not :ro.

3. Disable the umask quirk on the Docker executor

If extracted cache files come back without write bits, enable the feature flag so the runner doesn’t apply a restrictive umask:

[[runners]]
  environment = ["FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR=true"]

This makes cache (and artifact) files keep usable permissions across jobs.

4. Don’t mix root and non-root cache writers

If one job writes files as root and another caches as a non-root user, the non-root user can’t chmod them. Standardise the image/user across jobs that share a cache key, or add a normalising chmod -R u+rwX step.

5. Re-run and confirm caching works

A healthy run logs Creating cache ... Created cache (and Successfully extracted cache on restore) with no permission denied warnings, and subsequent jobs skip rebuilds.

Prevention and Best Practices

  • Match the cache directory/volume ownership to the UID your images run as; document that UID for runner admins.
  • Add a chmod -R u+rwX <cached-path> normalisation step when jobs mix root and non-root users.
  • Mount cache volumes :rw and verify with ls -ld that the container user can write the path.
  • Set FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR=true if extracted cache/artifact files lose write permissions.
  • Treat cache warnings as real failures in review — a silently broken cache slows every pipeline. The free incident assistant flags UID/ownership mismatches from the log. More patterns live in the GitLab CI/CD guides.
  • ERROR: Failed to extract cache — a corrupt or incompatible cache archive rather than a permission issue; same subsystem, different cause.
  • no space left on device — the cache dir filled the disk; cache writes then fail for a different reason.
  • Access Denied on S3 cache — the object-storage credentials lack write permission; the distributed-cache analogue of this error.

Frequently Asked Questions

My job is green but nothing is cached. Is this the cause?

Very likely. Cache failures are warnings, not errors, so the job passes while the cache is never written or read. Look for WARNING: Failed to create cache ... permission denied in the log; every run then rebuilds from scratch.

Why does the container user matter?

The runner writes cache.zip and chmods cached files as the user the image runs as (often UID 1000). If the cache directory or the files are owned by root, that user is denied. Align ownership with the container UID.

What does FF_DISABLE_UMASK_FOR_DOCKER_EXECUTOR do?

It stops the Docker executor from applying a restrictive umask to cache and artifact files, so they keep usable read/write permissions across jobs. Enable it when extracted cache files come back without write bits.

We use S3 for distributed cache and see Access Denied. Same fix?

No — that is a credentials problem, not filesystem permissions. Grant the runner’s S3 credentials write access to the cache bucket/prefix. The on-disk permission denied covered here is about container UID vs cache-dir ownership.

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.