Configuring safe.directory for shared checkouts Jump to heading
Since 2022, Git refuses to operate in a repository owned by a different user, failing with fatal: detected dubious ownership in repository. The check exists because a repository’s configuration can run commands — hooks, core.fsmonitor, core.sshCommand and others — and a repository owned by someone else could make your Git run their commands as you. The error appears most often in CI containers where the checkout is owned by one user and Git runs as another, on build servers with shared checkouts, and on mounted drives with unusual ownership. The common fix found online — safe.directory '*' — switches the protection off entirely. This page explains the check, fixes ownership where possible, adds narrow exceptions where not, and handles CI correctly, within Git configuration management at scale.
When to use this approach Jump to heading
- Git fails with “detected dubious ownership in repository”.
- CI jobs run Git in containers as a different user from the checkout’s owner.
- Several users or services share one checkout on a server.
- Someone has added
safe.directory = *and you want to tighten it.
Step 1 — Understand what is being checked Jump to heading
Git compares the owner of the repository directory (and .git) with the current user. If they differ, and the directory is not listed in safe.directory, Git refuses to read the repository’s configuration.
stat -c '%U %n' . .git
id -un
git status
# fatal: detected dubious ownership in repository at '/srv/build/app'
# To add an exception for this directory, call:
# git config --global --add safe.directory /srv/build/app Step 2 — Prefer fixing ownership Jump to heading
The cleanest fix is to make the repository owned by the user who runs Git. That keeps the protection intact.
sudo chown -R builder:builder /srv/build/app
# In Dockerfiles, create the checkout as the runtime user
# USER builder
# RUN git clone https://git.example.com/org/app.git /home/builder/app Step 3 — Add a narrow exception when ownership must differ Jump to heading
When a directory must be owned by someone else — a checkout mounted into a container, a deploy directory owned by a service account — list that exact path.
git config --global --add safe.directory /srv/build/app
git config --global --get-all safe.directory safe.directory is only honoured in system, global and command-line configuration, never in a repository’s own configuration — otherwise the repository could declare itself safe.
⚠️ SAFETY WARNING:
safe.directory = *disables the check for every repository. On a machine where other users can create repositories you might enter — shared servers, multi-tenant runners — that lets their repository configuration run commands as you. Use specific paths instead.
Step 4 — Handle CI containers Jump to heading
In container-based CI, the workspace is often mounted from the host with a different owner from the container’s user. Add the workspace path at the start of the job, or run the container as the workspace owner.
jobs:
build:
runs-on: ubuntu-latest
container: { image: "node:20" }
steps:
- uses: actions/checkout@v4
- run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- run: git describe --tags Ephemeral containers are single-tenant, so the risk the check guards against is low there; a path-specific entry still avoids training people to reach for the wildcard.
Step 5 — Handle shared checkouts on servers Jump to heading
When several people or services run Git in one checkout, give the checkout a dedicated owner and have everyone act through it, rather than each user listing it as safe.
sudo -u deploy git -C /srv/app pull --ff-only
# Or, if individual accounts must run Git there, list the exact path in each account's global config
sudo -u alice git config --global --add safe.directory /srv/app Step 6 — Mounted drives and network shares Jump to heading
Filesystems such as FAT, exFAT or some network shares report ownership that does not match any local user, triggering the check on every repository there. List the specific repository paths, or the mount’s subdirectories with a trailing /* where your Git version supports it.
git config --global --add safe.directory '/mnt/usb/projects/*' # repositories directly under projects The trailing /* matches repositories under that directory, which is safer than a global wildcard but still broad; keep it to drives only you can write.
Step 7 — Audit for wildcards Jump to heading
Check team machines and runner images for the global wildcard and replace it with specific entries.
for scope in --system --global; do
git config $scope --get-all safe.directory 2>/dev/null | grep -qx '\*' && echo "wildcard safe.directory in $scope config"
done Fleet-wide checks of this kind are covered in auditing local Git configuration drift.
Validation checklist Jump to heading
Frequently Asked Questions Jump to heading
Why did this start failing after a Git upgrade? Jump to heading
The ownership check was added in Git 2.35.2 and related releases as a security fix. Repositories that worked before fail when their owner differs from the user running Git.
Does the check apply to reading repositories with git -C? Jump to heading
Yes. Any Git command that opens the repository performs the check, whatever the working directory.
Is running as root an exception? Jump to heading
Root running Git in a repository owned by another user triggers the check too, with a special case for repositories owned by the user who ran sudo. Avoid running Git as root in other users’ repositories.
Related Jump to heading
- Git Configuration Management at Scale — the parent topic.
- Signing Commits Inside Ephemeral Containers — other container ownership issues.
- Enforcing a Minimum Git Version — keeping security fixes deployed.
- Managing Git Credentials Across Hosts — other global settings.