{"record":{"id":"5dd563038c0031bf","repo":"docker/cli","slug":"option-bind-recursive-readonly-requires-bind-pr","errorCode":null,"errorMessage":"option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction","messagePattern":"option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"opts/mount_utils.go","lineNumber":32,"sourceCode":"func validateMountOptions(m *mount.Mount) error {\n\tif err := validateExclusiveOptions(m); err != nil {\n\t\treturn err\n\t}\n\n\tif m.BindOptions != nil {\n\t\tif m.BindOptions.ReadOnlyNonRecursive && !m.ReadOnly {\n\t\t\treturn errors.New(\"option 'bind-recursive=writable' requires 'readonly' to be specified in conjunction\")\n\t\t}\n\t\tif m.BindOptions.ReadOnlyForceRecursive {\n\t\t\tif !m.ReadOnly {\n\t\t\t\treturn errors.New(\"option 'bind-recursive=readonly' requires 'readonly' to be specified in conjunction\")\n\t\t\t}\n\t\t\tif m.BindOptions.Propagation != mount.PropagationRPrivate {\n\t\t\t\t// FIXME(thaJeztah): this is missing daemon-side validation\n\t\t\t\t//\n\t\t\t\t//\tdocker run --rm --mount type=bind,src=/var/run,target=/foo,bind-recursive=readonly,readonly alpine\n\t\t\t\t//\t# no error\n\t\t\t\treturn errors.New(\"option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction\")\n\t\t\t}\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// validateExclusiveOptions checks if the given mount config only contains\n// options for the given mount-type.\n//\n// This is the client-side equivalent of [mounts.validateExclusiveOptions] in\n// the daemon, but with error-messages matching client-side flags / options.\n//\n// [mounts.validateExclusiveOptions]: https://github.com/moby/moby/blob/v2.0.0-beta.6/daemon/volume/mounts/validate.go#L31-L50\nfunc validateExclusiveOptions(m *mount.Mount) error {\n\tif m.Type == \"\" {\n\t\treturn errors.New(\"type is required\")\n\t}","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/docker/cli/blob/4f84911bfe8811e9b028e4b1fee8e7510be79387/opts/mount_utils.go#L14-L50","documentation":"Returned by validateMountOptions (opts/mount_utils.go:32) when BindOptions.ReadOnlyForceRecursive is true ('bind-recursive=readonly') and m.ReadOnly is true, but bind propagation is not set to 'rprivate'. Recursively read-only bind mounts require rprivate propagation to prevent mount events from propagating into the read-only subtree. Note: there is a FIXME in the code indicating daemon-side validation for this constraint is currently missing.","triggerScenarios":"A --mount value includes 'bind-recursive=readonly' and 'readonly' but either omits 'bind-propagation=rprivate' or sets a different propagation. For example: '--mount type=bind,src=/x,dst=/y,readonly,bind-recursive=readonly' (propagation defaults to something other than rprivate).","commonSituations":"Using 'bind-recursive=readonly' without understanding that the kernel's recursively-read-only feature requires rprivate propagation to be safe, or setting a different propagation value that conflicts with the requirement.","solutions":["Add 'bind-propagation=rprivate' to the --mount spec when using 'bind-recursive=readonly'.","Ensure no conflicting 'bind-propagation' value is set; remove it if present so rprivate can be used."],"exampleFix":"// before: missing rprivate propagation\n// docker run --mount type=bind,src=/x,dst=/y,readonly,bind-recursive=readonly nginx\n\n// after: add rprivate propagation\n// docker run --mount type=bind,src=/x,dst=/y,readonly,bind-recursive=readonly,bind-propagation=rprivate nginx","handlingStrategy":"validation","validationCode":"func validateRecursiveReadonlyPropagation(spec string) error {\n    if strings.Contains(spec, \"bind-recursive=readonly\") {\n        if !strings.Contains(spec, \"bind-propagation=rprivate\") {\n            return fmt.Errorf(\"bind-recursive=readonly requires bind-propagation=rprivate\")\n        }\n    }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"if err := mountOpt.Set(value); err != nil {\n    if strings.Contains(err.Error(), \"bind-recursive=readonly' requires 'bind-propagation=rprivate'\") {\n        return fmt.Errorf(\"add 'bind-propagation=rprivate' to the mount spec when using bind-recursive=readonly\")\n    }\n    return err\n}","preventionTips":["Always include 'bind-propagation=rprivate' when using 'bind-recursive=readonly'.","Ensure no conflicting 'bind-propagation' value is set alongside bind-recursive=readonly.","Be aware that daemon-side validation for this constraint is currently missing (per FIXME in source)."],"tags":["mount","bind","validation","propagation","readonly"],"backgroundTag":null,"analyzedSha":"4f84911bfe8811e9b028e4b1fee8e7510be79387","analyzedAt":"2026-08-07T12:15:29.814Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}