Skip to content

serve docker: fix volume path being lost when the plugin restarts - #9870

Merged
ncw merged 1 commit into
rclone:masterfrom
hsdfat:docker-restore-volume-path
Sep 8, 2026
Merged

ncw merged 1 commit into
rclone:masterfrom
hsdfat:docker-restore-volume-path

Conversation

@hsdfat

@hsdfat hsdfat commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

What does this change do?

rclone serve docker persists each volume's path option in docker-plugin.state, but never fed it back when
reloading. applyOptions consumes "path" into vol.Path rather than leaving it in vol.Options, and
restoreState rebuilt the options with only fs and type, so the rebuilt fsString lost the on-remote path
and the volume was remounted at the root of the remote.

Two volume shapes were affected:

volume options fsString after a restart, before after
type + path :local:/tmp/path → :local: path kept
remote + path :local:/tmp/path → :local:/tmp/remote path kept

The second one is not in the issue. An explicit path overrides the connection string's path when the volume is
created, but on restore it silently reverted to the connection string's path. Feeding the persisted path back
unconditionally lets applyOptions apply the same precedence on restore that it applies at create time, and is a
no-op for volumes that have no path.

For a backend whose credentials are scoped to the subpath — the reporter's case, S3 on Cloudflare R2 with a
bucket-scoped token — the restored mount does not merely serve the wrong directory: every operation at the new
root needs ListBuckets, which is denied, so all I/O through the mount returns EIO after each reboot.

The issue also reports that stale mounts entries can block plugin start. That one is already fixed on master by
the deferred pendingMounts restore, so this PR only addresses the path.

Testing

cmd/serve/docker/volume_test.go adds:

  • TestRestoreStatePath — a table test over the four option combinations that can carry a path (type+path,
    remote only, remote+path, type only), round-tripping the volume through its JSON persisted form and
    asserting fsString comes back unchanged.
  • TestDriverRestoreStatePath — the same through a real docker-plugin.state file and Driver.restoreState.

I checked the tests actually pin the behaviour by mutating the fix:

  • removing it entirely: type_and_path, remote_and_path and TestDriverRestoreStatePath fail, while the two
    shapes that were never broken still pass;
  • restricting it to vol.Path != "" && vol.Fs == "": only remote_and_path fails, which is the case that would
    otherwise have been missed.

go build ./..., make quicktest and golangci-lint run ./cmd/serve/docker/... all pass.

No documentation change: the "Restarting or upgrading the plugin" section of docker.md already describes volumes
being restored across a restart, which this fixes rather than changes.

Linked issue

Fixes #9853

Checklist

  • This change is trivial OR it has been discussed and agreed in the linked issue.
  • I have read the contribution guidelines.
  • (If I used AI tools to help write this code) I have read and understood the AI-assisted contributions guidance, and I have tested and take ownership of this change myself.
  • I have added tests for all changes in this PR if appropriate.
  • I have added documentation for the changes if appropriate. (No doc change needed — see above.)
  • All commit messages are in house style.
  • (Backend changes only) test_all passes for this backend — not a backend change.
  • This Pull Request is ready for review.

applyOptions consumes the "path" option into vol.Path rather than leaving
it in vol.Options, but restoreState rebuilt the options with only fs and
type. The explicit path was therefore dropped when the plugin restarted,
and since fsString is rebuilt from those options the volume was remounted
at the root of the remote instead of at its subpath.

Before this change a volume created with type + path lost its path
completely, and one created with remote + path silently fell back to the
path of the connection string. With a backend whose credentials are
scoped to the subpath the restored mount then failed every operation
rather than serving the wrong directory.

Feed the persisted path back like fs and type, so applyOptions applies
the same precedence on restore that it applies when the volume is
first created.

Fixes rclone#9853
@hsdfat
hsdfat requested review from albertony and ncw as code owners September 5, 2026 15:15

@ncw ncw left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you - nice fix :-)

@ncw
ncw merged commit 9ac29e3 into rclone:master Sep 8, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

docker volume plugin: state restore after reboot loses volume path (mounts at remote root); stale mounts state also blocks plugin start

2 participants