Filesystem driver on Windows: blob uploads fail with 500
Description
On Windows, with the filesystem storage backend, every blob upload fails with HTTP 500. The registry cannot rename the per-upload startedat temp file into place because the file handle is still open, and Windows (unlike POSIX) does not allow renaming a file while a handle to it is open. Because startedat is written at the start of every upload (POST /v2/<name>/blobs/uploads/), the registry is effectively unusable with the filesystem driver on Windows.
Reproduce
- Run the registry 3.1.x on Windows with the
filesystemstorage driver. - Push any image to it (
docker push localhost:5000/...).
Expected behavior
The upload session is created and the push succeeds, as it does on Linux.
registry version
v3.1.1, built on/for Windows with Golang 1.26.4.
I also checked with 3.0.0 (succeeds) and 3.1.0 (fails).
Additional Info
Environment
- Registry version:
v3.1.1(built withgo1.26.4for Windows) - Registry host OS: Windows
- Storage backend:
filesystem - Client:
docker/29.5.3 go/go1.26.4 git-commit/285b471 os/windows arch/amd64 - No load balancer / proxy in front of the registry (direct,
localhost).
(Registry was launched as a normal local instance with the default filesystem storage; no Nginx/enterprise proxy involved.)
Actual behavior
POST /v2/<name>/blobs/uploads/ returns 500 and the push fails. The registry log shows a rename failure on the startedat temp file:
level=error msg="response completed with error" err.code=unknown
err.detail="filesystem: rename \\registry\\docker\\registry\\v2\\repositories\\gesellix\\test\\_uploads\\<id>\\startedat.<id>.tmp \\registry\\docker\\registry\\v2\\repositories\\gesellix\\test\\_uploads\\<id>\\startedat: The process cannot access the file because it is being used by another process.
unlinkat \\registry\\docker\\registry\\v2\\repositories\\gesellix\\test\\_uploads\\<id>\\startedat.<id>.tmp: The process cannot access the file because it is being used by another process."
err.message="unknown error" http.request.method=POST
http.request.uri=/v2/gesellix/test/blobs/uploads/
http.response.status=500 http.response.written=708
service=registry version=v3.1.1+unknownThis repeats for every upload attempt (the client retries, every retry returns 500).
Root cause
In registry/storage/driver/filesystem/driver.go, PutContent writes the content to a temporary file through a fileWriter and then calls Move (os.Rename) to atomically replace the target, but it does not close the writer's file handle before renaming. On POSIX this is fine; on Windows the rename (and the subsequent cleanup unlinkat) fails because the handle is still open.
There are a couple of related Windows path issues in the same driver (mixing OS-specific filepath.Join with slash-separated storage keys), but the file handle not being closed before rename is what produces the 500 above.
Proposed fix
I have a fix and have verified it locally on a Windows host (uploads succeed again). I can open a PR that:
- closes the writer before renaming in
PutContent, - adds build-tagged
syncDir/renamehelpers (POSIX vs Windows; directory fsync is a no-op on Windows, andrenamefalls back to remove-then-rename when the destination already exists), - normalizes path handling at the filesystem boundary, and
- adds a
windows-latestCI job so this is covered going forward.
I am happy to adjust the approach based on maintainer feedback.
Source: distribution/distribution