hashicorp/nomad · error

failed to parse security_opt configuration: %v

Error message

failed to parse security_opt configuration: %v

What it means

The docker driver's security_opt option is validated by parseSecurityOpts before being handed to the Docker daemon. Entries that are not well-formed (e.g. missing key=value separation or disallowed forms) are rejected locally with this wrapper so jobs fail at config time rather than with an opaque Docker API error.

Source

Thrown at drivers/docker/driver.go:1391

	hostConfig.PidMode = containerapi.PidMode(driverConfig.PidMode)

	if utsErr := d.validateNamespace(d.config.AllowedModes.UTS, "uts_mode", driverConfig.UTSMode); utsErr != nil {
		return c, utsErr
	}
	hostConfig.UTSMode = containerapi.UTSMode(driverConfig.UTSMode)

	if usernsErr := d.validateNamespace(d.config.AllowedModes.Userns, "userns_mode", driverConfig.UsernsMode); usernsErr != nil {
		return c, usernsErr
	}
	hostConfig.UsernsMode = containerapi.UsernsMode(driverConfig.UsernsMode)

	hostConfig.ExtraHosts = driverConfig.ExtraHosts
	hostConfig.SecurityOpt = driverConfig.SecurityOpt
	hostConfig.Sysctls = driverConfig.Sysctl

	hostConfig.SecurityOpt, err = parseSecurityOpts(driverConfig.SecurityOpt)
	if err != nil {
		return c, fmt.Errorf("failed to parse security_opt configuration: %v", err)
	}

	ulimits, err := sliceMergeUlimit(driverConfig.Ulimit)
	if err != nil {
		return c, fmt.Errorf("failed to parse ulimit configuration: %v", err)
	}
	hostConfig.Ulimits = ulimits

	hostConfig.ReadonlyRootfs = driverConfig.ReadonlyRootfs

	// set the docker network mode
	hostConfig.NetworkMode = containerapi.NetworkMode(driverConfig.NetworkMode)

	// if the driver config does not specify a network mode then try to use the
	// shared alloc network
	if hostConfig.NetworkMode == "" {
		if task.NetworkIsolation != nil && task.NetworkIsolation.Path != "" {
			// find the previously created parent container to join networks with

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Use key=value syntax for every security_opt entry, e.g. "seccomp=unconfined", "apparmor=profile-name","no-new-privileges=true"
  2. Remove or fix the offending entry identified by the wrapped %v error
  3. Validate the seccomp JSON file path exists on the host if using seccomp=<path>

Example fix

// before
driver {
  docker {
    security_opt = ["seccomp:unconfined"]
  }
}
// after
driver {
  docker {
    security_opt = ["seccomp=unconfined"]
  }
}
Defensive patterns

Strategy: validation

Validate before calling

for _, opt := range cfg.SecurityOpt {
  if !strings.Contains(opt, "=") {
    return fmt.Errorf("security_opt must be key=value, got %q", opt)
  }
}

Try / catch

err := client.StartTask(task); if err != nil && strings.Contains(err.Error(), "failed to parse security_opt") { logOffendingOpts(cfg.SecurityOpt) }

Prevention

When it happens

Trigger: StartTask -> createContainerConfig calls parseSecurityOpts(driverConfig.SecurityOpt) and the security_opt list contains a malformed entry such as a flag without '=' (e.g. 'seccomp:unconfined' style instead of 'seccomp=unconfined').

Common situations: Users copying --security-opt CLI syntax with colons instead of equals signs; security_opt entries that docker daemon would reject (apparmor profiles that don't exist, malformed seccomp JSON paths); quoting mistakes in HCL splitting a value into separate items.

Understand the failure class

Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.

Related errors


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