hashicorp/nomad · error

volumes are not enabled; cannot use volume driver %q

Error message

volumes are not enabled; cannot use volume driver %q

What it means

containerBinds rejects volume usage when host volumes are globally disabled on the docker driver (client option volumes.enabled = false) but the task specifies a non-empty volume_driver. Bind mounts of the alloc/task/secret dirs are always allowed, but any explicit volume driver implies a host/volume-driver mount that requires volumes to be enabled. This is a client-level security policy guard.

Source

Thrown at drivers/docker/driver.go:768

	if err != nil {
		return fmt.Errorf("the image does not exist: %v", err)
	}
	// LCOW If we are running a Linux Container on Windows, we need to mount it correctly, as c:\ does not exist on unix
	if imageConfig.Os == "linux" {
		a := []rune(task.Env[taskenv.AllocDir])
		task.Env[taskenv.AllocDir] = strings.ReplaceAll(string(a[2:]), "\\", "/")
		l := []rune(task.Env[taskenv.TaskLocalDir])
		task.Env[taskenv.TaskLocalDir] = strings.ReplaceAll(string(l[2:]), "\\", "/")
		s := []rune(task.Env[taskenv.SecretsDir])
		task.Env[taskenv.SecretsDir] = strings.ReplaceAll(string(s[2:]), "\\", "/")
	}
	return nil
}

func (d *Driver) containerBinds(task *drivers.TaskConfig, driverConfig *TaskConfig) ([]string, error) {
	taskLocalBindVolume := driverConfig.VolumeDriver == ""
	if !d.config.Volumes.Enabled && !taskLocalBindVolume {
		return nil, fmt.Errorf("volumes are not enabled; cannot use volume driver %q", driverConfig.VolumeDriver)
	}

	allocDirBind := fmt.Sprintf("%s:%s", task.TaskDir().SharedAllocDir, task.Env[taskenv.AllocDir])
	taskLocalBind := fmt.Sprintf("%s:%s", task.TaskDir().LocalDir, task.Env[taskenv.TaskLocalDir])
	secretDirBind := fmt.Sprintf("%s:%s", task.TaskDir().SecretsDir, task.Env[taskenv.SecretsDir])
	selinuxLabel := d.config.Volumes.SelinuxLabel

	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)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Enable volumes on the client: set plugin 'docker' config volumes { enabled = true } in the client hcl and restart.
  2. Remove the volume_driver field if only default alloc-dir bind mounts are needed.
  3. Switch to Nomad host volumes / CSI volumes, which have their own ACL-gated access model.
  4. If only bind mounts are intended, omit volume_driver so taskLocalBindVolume is true.

Example fix

// before (client config)
plugin "docker" { config { volumes { enabled = false } } }
// after
plugin "docker" { config { volumes { enabled = true } } }
Defensive patterns

Strategy: validation

Validate before calling

// job-side guard before using volume_driver
if cfg.VolumeDriver != "" && !clientVolumesEnabled {
	return fmt.Errorf("job requires volume_driver %q but client volumes are disabled", cfg.VolumeDriver)
}

Prevention

When it happens

Trigger: Task sets config.volume_driver (e.g. "local", "cloudstor:azure", or a named host volume driver) while the Nomad client docker plugin config has volumes.enabled = false. Raised in createContainerConfig before container creation.

Common situations: Jobs migrated from clusters with volumes enabled to locked-down clients; setting volume_driver = "local" for docker named volumes on a security-hardened client; forgetting to enable volumes in the client's plugin configuration.

Related errors


AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04). Data as JSON: /api/errors/0d1bb10c4e0f04e6. Report an issue: GitHub.