hashicorp/nomad · error
running as user %q is disallowed
Error message
running as user %q is disallowed
What it means
Nomad's validate hook enforces a user denylist for drivers on the checked_drivers list. If the task's User resolves to a user present in the configured denylist and the task's driver is one being checked, task validation fails and the task never starts. This is a security guard against running tasks as privileged or forbidden accounts.
Source
Thrown at client/allocrunner/taskrunner/validate_hook.go:57
}
resp.Done = true
return nil
}
func validateTask(task *structs.Task, taskEnv *taskenv.TaskEnv, conf *config.Config) error {
var mErr multierror.Error
// Validate the user
// COMPAT(1.0) uses inclusive language. blacklist is kept for backward compatilibity.
unallowedUsers := conf.ReadStringListAlternativeToMapDefault(
[]string{"user.denylist", "user.blacklist"},
config.DefaultUserDenylist,
)
checkDrivers := conf.ReadStringListToMapDefault("user.checked_drivers", config.DefaultUserCheckedDrivers)
if _, driverMatch := checkDrivers[task.Driver]; driverMatch {
if _, unallowed := unallowedUsers[task.User]; unallowed {
mErr.Errors = append(mErr.Errors, fmt.Errorf("running as user %q is disallowed", task.User))
}
}
// Validate the Service names once they're interpolated
for _, service := range task.Services {
name := taskEnv.ReplaceEnv(service.Name)
if err := service.ValidateName(name); err != nil {
mErr.Errors = append(mErr.Errors, fmt.Errorf("service (%s) failed validation: %v", name, err))
}
}
if len(mErr.Errors) == 1 {
return mErr.Errors[0]
}
return mErr.ErrorOrNil()
}
View on GitHub (pinned to 482b49bf1a)
Solutions
- Change the task's user field to an allowed non-privileged user
- Adjust user.denylist/user.checked_drivers in the client config if policy legitimately permits the user (with caution)
- Remove the user block so the driver default user applies
- Re-run the job after fixing
Example fix
// before
task "web" {
driver = "docker"
user = "root"
}
// after
task "web" {
driver = "docker"
user = "nobody"
} Defensive patterns
Strategy: validation
Validate before calling
// pre-validate the user against the denylist before submit
unallowedUsers := conf.ReadStringListToMapDefault("user.denylist", config.DefaultUserDenylist)
checkDrivers := conf.ReadStringListToMapDefault("user.checked_drivers", config.DefaultUserCheckedDrivers)
if checkDrivers[task.Driver] && unallowedUsers[task.User] {
return fmt.Errorf("user %q disallowed for driver %q", task.User, task.Driver)
} Try / catch
if err := validateTask(task, conf); err != nil {
// multierror: inspect entries for the user enforcement failure
for _, e := range err.Errors {
if strings.Contains(e.Error(), "running as user") {
return fmt.Errorf("fix task.user in job spec: %w", e)
}
}
return err
} Prevention
- Never set user = "root" unless the denylist policy explicitly permits it
- Review job specs against the client's user.denylist when tightening policy
- Document which drivers are in user.checked_drivers for your fleet
- Run nomad job validate in CI to catch this before deploy
When it happens
Trigger: task.User is set (or defaults) to a user in user.denylist (default includes root) while the task's driver is in user.checked_drivers (default: docker, exec, qemu, etc.), evaluated during validateTask in Prestart.
Common situations: Jobs specifying user = "root" on docker/exec drivers; agent config where user.denylist was tightened after the job was written; legacy jobs predating the enforcement.
Related errors
- missing secret ID
- namespace cannot contain template delimiters or parenthesis
- wait config is nil or empty
- CSI.ControllerAttachVolume: VolumeID is required
- CSI.ControllerAttachVolume: ClientCSINodeID is required
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/66e163f6b2665e85.
Report an issue: GitHub.