Filesystem driver on Windows: blob uploads fail with 500

Author: gesellixCreated Jun 20, 2026Updated Aug 15, 2026

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

  1. Run the registry 3.1.x on Windows with the filesystem storage driver.
  2. 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 with go1.26.4 for 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+unknown

This 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/rename helpers (POSIX vs Windows; directory fsync is a no-op on Windows, and rename falls back to remove-then-rename when the destination already exists),
  • normalizes path handling at the filesystem boundary, and
  • adds a windows-latest CI job so this is covered going forward.

I am happy to adjust the approach based on maintainer feedback.

Source: distribution/distribution