How to fix fatal: cannot create worktree: read-only filesystem in CI in Git

GitINTERMEDIATEMEDIUMUpdated Dec 6, 2025· 204 views

This error occurs when Git attempts to create a worktree in a CI/CD environment but the filesystem is mounted as read-only. The fix involves using a writable directory, configuring the CI workspace correctly, or adjusting container mount options.

Quick fix

Fixes EROFS

# Check if current directory is writable
touch .write-test && rm .write-test && echo "Writable" || echo "Read-only"

# Check mount options for the filesystem
mount | grep $(df . --output=source | tail -1)

# List common writable directories
for dir in /tmp /workspace $HOME $CI_PROJECT_DIR; do
  if [ -d "$dir" ]; then
    touch "$dir/.test" 2>/dev/null && rm "$dir/.test" && echo "$dir: writable" || echo "$dir: read-only"
  fi
done
  1. Verify filesystem write permissions
  2. Create worktree in a writable directory
  3. Configure Docker volume mounts correctly
  4. Update GitHub Actions workflow
git — error
# Check if current directory is writable
touch .write-test && rm .write-test && echo "Writable" || echo "Read-only"
# Check mount options for the filesystem
mount | grep $(df . --output=source | tail -1)
# List common writable directories
for dir in /tmp /workspace $HOME $CI_PROJECT_DIR; do
if [ -d "$dir" ]; then
touch "$dir/.test" 2>/dev/null && rm "$dir/.test" && echo "$dir: writable" || echo "$dir: read-only"
fi
done
fatal: cannot create worktree: read-only filesystem in CI

What this error means

The "fatal: cannot create worktree: read-only filesystem" error indicates that Git is trying to create a linked worktree on a filesystem that has been mounted in read-only mode. This is particularly common in CI/CD environments where certain directories are intentionally mounted as read-only for security or reproducibility reasons. In CI systems like GitHub Actions, GitLab CI, Jenkins, and CircleCI, the build environment often has a mix of read-only and writable directories: - **Read-only areas**: The container image, system binaries, and sometimes the checkout directory - **Writable areas**: Typically `/tmp`, the workspace directory, and designated cache directories When you run `git worktree add` and Git cannot write to the target location, you'll encounter this error. The filesystem being read-only is often intentional - CI systems use read-only mounts to prevent builds from modifying the base environment and to ensure reproducible builds. This error can also occur when: - A Docker container mounts the repository as read-only - The CI runner uses a read-only filesystem overlay - Security policies (like SELinux or AppArmor) prevent writes - The disk has been remounted as read-only due to filesystem errors

Typical symptoms

  • git worktree add fails with 'fatal: cannot create worktree dir: Read-only file system'
  • CI pipeline fails during checkout or workspace setup
  • Error appears after updating CI runner or container image
  • The same commands work locally but fail in CI
  • Build logs show EROFS or 'Read-only file system' errors
  • Worktree operations succeed in some directories but fail in others

Common causes

  1. CI workspace is mounted as a read-only volume in Docker or Kubernetes
  2. The target worktree path is on a read-only filesystem overlay
  3. Container image uses a read-only root filesystem for security
  4. CI system's checkout directory is protected to prevent modification
  5. Filesystem remounted as read-only due to disk errors or I/O issues
  6. Security context or policy restricts write access to certain paths
  7. Build cache directory is incorrectly configured as read-only
  8. Using a minimal CI container that mounts most paths as read-only

How to fix "fatal: cannot create worktree: read-only filesystem in CI"

1Verify filesystem write permissions

First, identify which directories are writable in your CI environment:

bash
# Check if current directory is writable
touch .write-test && rm .write-test && echo "Writable" || echo "Read-only"

# Check mount options for the filesystem
mount | grep $(df . --output=source | tail -1)

# List common writable directories
for dir in /tmp /workspace $HOME $CI_PROJECT_DIR; do
  if [ -d "$dir" ]; then
    touch "$dir/.test" 2>/dev/null && rm "$dir/.test" && echo "$dir: writable" || echo "$dir: read-only"
  fi
done

This helps identify where you can safely create worktrees.

2Create worktree in a writable directory

Move the worktree target to a directory that's writable in your CI environment:

bash
# Use /tmp which is typically writable
git worktree add /tmp/my-worktree feature-branch

# Or use the CI's designated workspace directory
# GitHub Actions
git worktree add "$GITHUB_WORKSPACE/../worktree" feature-branch

# GitLab CI
git worktree add "$CI_PROJECT_DIR/../worktree" feature-branch

# Jenkins
git worktree add "$WORKSPACE/../worktree" feature-branch

Note: Using ../ paths keeps the worktree close to your project while typically being outside the read-only mounted source directory.

3Configure Docker volume mounts correctly

If using Docker in your CI pipeline, ensure the repository is mounted with write permissions:

yaml
# docker-compose.yml
services:
  build:
    image: your-image
    volumes:
      - .:/app:rw  # Explicitly set read-write (default, but clear)
      # NOT: - .:/app:ro (read-only)
bash
# Docker run command
docker run -v "$(pwd):/app:rw" your-image

# If you need the source read-only but want writable worktrees,
# mount a separate writable volume
docker run \
  -v "$(pwd):/app:ro" \
  -v "/tmp/worktrees:/worktrees:rw" \
  your-image

Then create worktrees in /worktrees instead:

bash
git worktree add /worktrees/feature feature-branch
4Update GitHub Actions workflow

For GitHub Actions, ensure you have write permissions and use the correct directories:

yaml
name: Build
on: [push, pull_request]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0  # Needed for worktrees with full history

      - name: Create worktree
        run: |
          # GitHub Actions workspace is writable
          # Create worktree in a sibling directory
          git worktree add ../feature-worktree origin/feature-branch

          # Or use runner temp directory
          git worktree add "$RUNNER_TEMP/worktree" origin/feature-branch

The $RUNNER_TEMP directory is guaranteed to be writable and cleaned up after the job.

5Update GitLab CI configuration

For GitLab CI, use the appropriate build directories:

yaml
# .gitlab-ci.yml
build:
  script:
    # GitLab CI's build directory is writable
    - git worktree add "$CI_BUILDS_DIR/worktree-$CI_JOB_ID" origin/feature

    # Or use the project directory's parent
    - git worktree add "$CI_PROJECT_DIR/../feature-worktree" origin/feature

    # Clean up after use
    - git worktree remove "$CI_BUILDS_DIR/worktree-$CI_JOB_ID" || true

If using a custom Docker image, ensure it doesn't use --read-only:

yaml
build:
  image: your-image
  variables:
    # Some images require this
    GIT_STRATEGY: clone
6Handle Kubernetes read-only root filesystem

In Kubernetes environments with readOnlyRootFilesystem: true, you need writable volumes:

yaml
# Pod spec
apiVersion: v1
kind: Pod
spec:
  containers:
    - name: build
      securityContext:
        readOnlyRootFilesystem: true  # Security requirement
      volumeMounts:
        - name: workspace
          mountPath: /workspace
        - name: tmp
          mountPath: /tmp
        - name: worktrees
          mountPath: /worktrees
  volumes:
    - name: workspace
      emptyDir: {}
    - name: tmp
      emptyDir: {}
    - name: worktrees
      emptyDir: {}

Then in your build script:

bash
# Clone to writable workspace
git clone https://github.com/org/repo /workspace/repo
cd /workspace/repo

# Create worktree in writable volume
git worktree add /worktrees/feature origin/feature
7Use git clone instead of worktree when necessary

If worktrees are problematic in your CI environment, consider using separate clones instead:

bash
# Instead of worktree, use a shallow clone of the specific branch
git clone --depth 1 --branch feature-branch \
  "$(git remote get-url origin)" /tmp/feature-checkout

# Or for local changes, use git archive
git archive feature-branch | tar -x -C /tmp/feature-checkout

# For comparing branches, use git diff directly
git diff main...feature-branch

This avoids worktree creation entirely while achieving similar goals.

8Check for filesystem errors in CI runner

Sometimes the filesystem becomes read-only due to errors. Check the CI runner's system logs:

bash
# Check kernel messages for filesystem errors
dmesg | grep -i "read-only\|error\|ext4\|xfs" | tail -20

# Check if filesystem was remounted due to errors
mount | grep "ro,"

# Attempt to remount as read-write (requires privileges)
# This is typically done by CI admins, not in build scripts
sudo mount -o remount,rw /path/to/mount

If you see filesystem errors, the CI runner may need maintenance. Report to your DevOps team:

bash
EXT4-fs error (device sda1): ext4_lookup:1604: inode #12345: comm git: deleted inode referenced
EXT4-fs (sda1): Remounting filesystem read-only

This indicates disk corruption requiring administrator intervention.

9Alternative: Use sparse checkout instead of worktree

If you're using worktrees to check out a subset of files, sparse checkout might work better in CI:

bash
# Enable sparse checkout
git sparse-checkout init --cone

# Define which directories to include
git sparse-checkout set src/ tests/

# Switch to the branch you need
git checkout feature-branch

This modifies the current checkout rather than creating a new worktree, avoiding the read-only filesystem issue entirely.

For checking out files from another branch without switching:

bash
# Check out specific files from another branch
git checkout feature-branch -- path/to/file.txt

# Or restore from another branch to the working directory
git restore --source=feature-branch -- path/to/file.txt

Advanced notes

Understanding CI filesystem architecture:

Modern CI systems use layered filesystems and containers for isolation:

1. Base image layer (read-only): Contains OS, tools, and dependencies
2. Overlay layer (copy-on-write): Captures changes during build
3. Volume mounts: Can be configured as read-only or read-write

When Git tries to create a worktree, it needs to:
- Create a directory structure
- Write Git metadata files
- Create a HEAD file pointing to the branch

All of these require write permissions to the target directory.

Security considerations:

Read-only filesystems in CI are often intentional security measures:

- Prevents supply chain attacks from modifying build tools
- Ensures reproducible builds
- Limits damage from compromised dependencies

Work within these constraints rather than disabling them. Use designated writable areas like:
- $RUNNER_TEMP (GitHub Actions)
- $CI_BUILDS_DIR (GitLab CI)
- /tmp (usually writable)

Worktree vs. multiple clones performance:

| Approach | Disk Space | Clone Time | Network |
|----------|-----------|------------|---------|
| Worktree | Shared objects | Near instant | None |
| Full clone | Full copy | Long | Full download |
| Shallow clone | Minimal | Fast | Minimal download |

In CI where builds are ephemeral, shallow clones often work as well as worktrees without the filesystem write requirements.

Debugging filesystem issues:

bash
# Get detailed filesystem info
stat -f .

# Check SELinux context (if applicable)
ls -laZ .

# Check AppArmor status
aa-status 2>/dev/null || echo "AppArmor not active"

# Trace system calls to see exact failure
strace -e trace=open,openat,mkdir git worktree add /target branch 2>&1 | grep -i "EROFS\|read-only"

Container runtime considerations:

Different container runtimes handle read-only filesystems differently:

- Docker: Use -v /host/path:/container/path:rw
- Podman: Same as Docker, also supports :U for user namespace mapping
- containerd/CRI-O: Configured via Kubernetes PodSpec

CI-specific environment variables for writable paths:

| CI System | Writable Path Variable |
|-----------|----------------------|
| GitHub Actions | $RUNNER_TEMP, $GITHUB_WORKSPACE |
| GitLab CI | $CI_PROJECT_DIR, $CI_BUILDS_DIR |
| CircleCI | $CIRCLE_WORKING_DIRECTORY, /tmp |
| Jenkins | $WORKSPACE, $JENKINS_HOME |
| Azure Pipelines | $(Agent.TempDirectory), $(Build.SourcesDirectory) |
| Travis CI | $TRAVIS_BUILD_DIR, /tmp |

Frequently asked questions

What does "EROFS" mean?
The "fatal: cannot create worktree: read-only filesystem" error indicates that Git is trying to create a linked worktree on a filesystem that has been mounted in read-only mode. This is particularly common in CI/CD environments where certain directories are intentionally mounted as read-only for security or reproducibility reasons. In CI systems like GitHub Actions, GitLab CI, Jenkins, and CircleCI, the build environment often has a mix of read-only and writable directories: - Read-only areas: The container image, system binaries, and sometimes the checkout directory - Writable areas: Typically /tmp, the workspace directory, and designated cache directories When you run git worktree add and Git cannot write to the target location, you'll encounter this error.
How do I fix "EROFS"?
This error occurs when Git attempts to create a worktree in a CI/CD environment but the filesystem is mounted as read-only. The fix involves using a writable directory, configuring the CI workspace correctly, or adjusting container mount options.
What causes "EROFS"?
CI workspace is mounted as a read-only volume in Docker or Kubernetes The target worktree path is on a read-only filesystem overlay Container image uses a read-only root filesystem for security
Is the "EROFS" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading