git commit on NFS4.2 fails with "error when closing loose object file: Permission denied" — but only some of the time
Filed 2026-08-12, updated 2026-08-12 with fix-attempt results. Looking
for root-cause ideas, config fixes, or kernel bug references. Everything
below is reproducible and tested — no guesses.
Update: fix attempts tested (2026-08-12)
A detailed diagnosis (NFSv4.2 close-to-open race over mergerfs-FUSE) was
tested with the following results:
❌ Made it deterministic worse — but exposed the exact failing syscall: fatal: fsync error on '.git/objects/8c/tmp_obj_KoDBkc': Permission denied. So the failure is fsync() on git's tmp_obj temp files, not plain close().
server export sync (rw,sync + exportfs -ra)
Server-side
❌ Verified active (/proc/fs/nfsd/exports shows sync,wdelay) but did not fix — commits still fail.
⚠️ Couldn't apply cleanly — mount -o remount on live mergerfs fought existing opts (relatime unknown on remount, needs nonempty + full option set).
client noac
Client-side
❌ Applied via fstab remount (verified noac,acregmin=0... in /proc/mounts) but did not fix — failure moved earlier to unable to write loose object file: Permission denied.
Validated root-cause refinement: the failing syscall is fsync()
(mapped by git to "error when closing loose object file") on the tmp_obj_* temporary object files. Writing succeeds; the post-write fsync over NFS4.2→mergerfs(FUSE) intermittently returns EACCES.
Conclusion after testing: neither client-side (noac, git fsync
tuning) nor server-side (sync export) NFS tuning fixes it. The failure
is in the mergerfs (FUSE) layer itself — it intermittently rejects
writes/fsyncs to newly created files (EACCES), and NFSd faithfully
propagates that. This is consistent with the "Hard Truth" in the
original diagnosis: git repositories do not belong on NFS backed by
mergerfs. All experimental changes were reverted to the original
state (fstab, exports, test dirs removed).
TL;DR
git commit inside a repository that lives on an NFSv4.2 mount
intermittently dies with:
fatal: error when closing loose object file: Permission denied
It's not actually a permission problem: the file is writable, touch
in the same directory works, the owner/mode are right, and the same
commit succeeds on local ext4.
It's intermittent — sometimes the first attempt works, sometimes
only after retries, sometimes git commit prints success but writes
nothing and the ref is unchanged.
It happens on freshly-created git repos on NFS, and also on long-lived
repos after certain operations (git add of a big file, cp -r of a
directory into the working tree, rm -rf .git && git init).
The workaround that reliably works is to redirect object writes to a
local disk (GIT_OBJECT_DIRECTORY=/local/path + GIT_ALTERNATE_OBJECT_DIRECTORIES=<repo>/.git/objects) then cp -rn the objects back.
But that's a band-aid and leaves the refs/index problem unsolved.
GIT_OBJECT_DIRECTORY alone (without GIT_ALTERNATE_OBJECT_DIRECTORIES)
makes it worse: git can no longer read the existing HEAD from the NFS
objects and fails with fatal: could not parse HEAD. Both must be set.
This only fixes object writes. Index/ref writes to .git/index and .git/refs/heads/main still occasionally fail, so the workaround is
partial.
git config core.fsync none on the repo reduces frequency but does not
eliminate it.
What we need help with
Root cause. The failure is at close() (or fsync/rename) of a
loose object file on NFS4.2. The file is writable, local writes work,
but git's close fails with EACCES. Is this a known NFSv4.2 client bug?
Kernel 6.14 client + kernel 7.0 server? Something about async exports + close-to-open consistency? Or is the mergerfs
backing store (fuse) underneath the export the real culprit — does the
NFS server do something at close time that the fuse fs rejects?
Is the local_lock=none mount option relevant? We use local_lock=none (default for nfs4). Should it be local_lock=all?
Would that affect close-time locking of newly created files?
Why do fresh repos fail more? Newly created .git/objects dir on
the NFS export vs long-established ones. NFS attribute caching
(negative dentry / missing post-create attribute refresh)?
Server-side: we export async (we know it's the safe default on
this box). Does async + close-to-open on the client explain the
intermittent EACCES at close? Would sync fix it (we can accept the
perf hit)?
Is the mergerfs-under-NFS export a known-bad stack? The export is a
mergerfs fuse mount of 8 disks. NFSd → fuse (mergerfs) → disks. Any
known issues with NFS close/fsync/rename across that chain?
Has anyone seen git commit print success but write nothing (no
objects, no ref update) on NFS? That one is the scariest — silent
success is worse than an explicit error.
Things we already tried (with results)
Attempt
Result
git config core.fsync none
Reduces frequency, doesn't eliminate
GIT_OBJECT_DIRECTORY local + GIT_ALTERNATE_OBJECT_DIRECTORIES + cp -rn