hashicorp/nomad · error
invalid docker volume %q: %v
Error message
invalid docker volume %q: %v
What it means
containerBinds parses each entry of driverConfig.Volumes with parseVolumeSpec; this error means one of the user-specified volume bind strings is malformed. A valid spec is src:dst[:mode]; parse failures include empty paths, too many/few colon-separated parts, or an invalid mode.
Source
Thrown at drivers/docker/driver.go:794
binds := []string{allocDirBind, taskLocalBind, secretDirBind}
logsROFlag := "ro"
if selinuxLabel != "" {
// Apply SELinux Label to each built-in bind
for i := range binds {
binds[i] = fmt.Sprintf("%s:%s", binds[i], selinuxLabel)
}
logsROFlag = "ro," + selinuxLabel
}
allocLogsDirBind := fmt.Sprintf("%s/logs:%s/logs:%s", task.TaskDir().SharedAllocDir, task.Env[taskenv.AllocDir], logsROFlag)
binds = append(binds, allocLogsDirBind)
for _, userbind := range driverConfig.Volumes {
// This assumes host OS = docker container OS.
// Not true, when we support Linux containers on Windows
src, dst, mode, err := parseVolumeSpec(userbind, runtime.GOOS)
if err != nil {
return nil, fmt.Errorf("invalid docker volume %q: %v", userbind, err)
}
// Paths inside task dir are always allowed when using the default driver,
// Relative paths are always allowed as they mount within a container
// When a VolumeDriver is set, we assume we receive a binding in the format
// volume-name:container-dest
// Otherwise, we assume we receive a relative path binding in the format
// relative/to/task:/also/in/container
if taskLocalBindVolume {
src = expandPath(task.TaskDir().Dir, src)
} else {
// Resolve dotted path segments
src = filepath.Clean(src)
}
if !d.config.Volumes.Enabled {
if err := escapingfs.ChildEscapesParentDir(task.AllocDir, src); err != nil {
return nil, fmt.Errorf("volumes are not enabled; cannot mount host path: %q", userbind)View on GitHub (pinned to 482b49bf1a)
Solutions
- Use the full three-part form: "host-src:container-dest:mode", at minimum "src:dst".
- Ensure both src and dst are non-empty absolute or relative paths.
- Limit mode to valid values ("ro", "rw", selinux labels like "z", "Z").
- On Windows, be careful with drive letters (C:\...) interacting with the spec parser.
Example fix
// before
config { volumes = ["/data"] }
// after
config { volumes = ["/host/data:/data:rw"] } Defensive patterns
Strategy: validation
Validate before calling
func validBindSpec(spec string) bool {
parts := strings.Split(spec, ":")
if len(parts) < 2 || len(parts) > 3 { return false }
if parts[0] == "" || parts[1] == "" { return false }
if len(parts) == 3 {
switch parts[2] { case "ro", "rw", "z", "Z", "": default: return false }
}
return true
} Prevention
- Always write volumes as src:dst or src:dst:mode
- Double-check specs on Windows where drive letters contain colons
- Validate all entries with nomad job validate before submission
When it happens
Trigger: A config.volumes entry fails parseVolumeSpec — e.g. "only-one-path", "src:dst:mode:extra", empty source/destination, or a mode string that isn't ro/rw/z etc., for the host GOOS.
Common situations: Missing destination path ("/data" instead of "/data:/data"), Windows drive letters colliding with colon splitting, typos like "host/path:ctr/path:read-only", or quoting issues in HCL producing wrong strings.
Related errors
- volumes are not enabled; cannot use volume driver %q
- volumes are not enabled; cannot mount host path: %q
- invalid security_opt: %q
- volumes are not enabled; cannot mount volume: %q
- not <src>:<destination> format
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/c95bd2feb599da39.
Report an issue: GitHub.