Fix "fatal: detected dubious ownership in repository" When Deploying Django
Fix git fatal detected dubious ownership in repository during Django deploys, CI jobs, and Docker builds: understand the safe.directory check, run git as the repository owner, fix ownership, or add a narrow safe.directory entry.
Problem statement
A deploy script that has run git pull for months suddenly fails:
fatal: detected dubious ownership in repository at '/srv/app'
To add an exception for this directory, call:
git config --global --add safe.directory /srv/app
Git 2.35.2 and later refuse to operate in a repository owned by a different user than the one running git. The check protects against a malicious repository configuration in a shared directory running hooks or commands as you. Deploy setups trigger it when the code is owned by deploy but the script runs as root (or via sudo), when a CI runner uses a different UID than the checkout, or when a Docker build copies a .git folder owned by the host user.
- Run git as the user who owns the repository:
sudo -u deploy git -C /srv/app pull. - Or make the owner match:
sudo chown -R deploy:deploy /srv/app. - If a different user must run git, trust only that path:
git config --global --add safe.directory /srv/appfor that user. - In CI containers, add the workspace path to
safe.directoryafter checkout. - Avoid
safe.directory '*'; it disables the protection everywhere.
Step-by-step solution
1. Compare owners
stat -c '%U %G' /srv/app /srv/app/.git
whoami
If the repository belongs to deploy and the command runs as root, that is the mismatch. Note that git checks the owner of the working tree and .git directory.
2. Run git as the owner
The cleanest fix keeps ownership simple: one user owns the code and runs every git command.
sudo -u deploy git -C /srv/app fetch --all
sudo -u deploy git -C /srv/app reset --hard origin/main
In a deploy script that runs as root, wrap git calls in sudo -u deploy or runuser -u deploy --. This also keeps file ownership consistent, so Gunicorn running as deploy can read new files.
3. Fix ownership after mixed operations
If someone ran sudo git pull earlier, some files may now be owned by root, which also breaks future pulls as deploy:
sudo chown -R deploy:deploy /srv/app
Then always run git as deploy.
4. Add a narrow safe.directory for CI
GitHub Actions jobs running in a container check out code as one UID and run steps as another. actions/checkout configures safe.directory for its own git commands, but your later steps may need it too:
- uses: actions/checkout@v4
- run: git config --global --add safe.directory "$GITHUB_WORKSPACE"
- run: git describe --tags --always > VERSION
GitLab CI and Jenkins agents may need the same line with their workspace path. Add it in the job, not baked into images.
5. Avoid git inside Docker builds
Running git describe or git rev-parse inside a Dockerfile to stamp a version needs the .git folder in the build context, with host ownership. Pass the value as a build argument instead:
docker build --build-arg GIT_SHA=$(git rev-parse --short HEAD) -t myapp .
ARG GIT_SHA=unknown
ENV GIT_SHA=$GIT_SHA
Exclude .git in .dockerignore, which also makes builds faster and images smaller.
Explanation
The check was added after CVE-2022-24765. In a shared directory such as /tmp or a multi-user server, another user could create a repository with a .git/config that runs commands through options like core.fsmonitor. Running any git command there, even implicitly through a shell prompt, would execute that code as you. Git now trusts only repositories owned by the current user, plus explicit safe.directory entries. Root is not exempt, which is why sudo git in a deploy script started failing after an upgrade.
Edge cases / notes
- Root-owned repositories run by root are fine; the problem is only mismatched owners.
- Network file systems may report unexpected owners (
nobody); fix the mount options or add a specific safe entry. - Windows runners have a similar check based on SIDs.
Next steps
- Set up pulls from private repositories with Deploy Django From a Private Git Repo With Deploy Keys.
- Write a dependable script with Django Deployment Script Bash Guide.
- For SSH key failures during pulls, see Fix SSH Permission Denied (publickey) During Deploy.
FAQ
Is it safe to add safe.directory?
Yes, for a specific path you control, such as your app directory. Avoid *, which disables the check for every repository.
Why did it start after a system update?
The update brought a git version with the ownership check. Your setup was already mixing users; git now refuses.
Does running as root bypass it?
No. Root also gets the error for repositories owned by another user.
Should my deploy run git as root?
No. Run it as the user that owns the code, so file ownership stays consistent for the app.