hashicorp/nomad · error

opening seccomp profile (%s) failed: %v

Error message

opening seccomp profile (%s) failed: %v

What it means

When a security_opt entry is seccomp=<profile> (other than 'unconfined'), parseSecurityOpts reads the profile file from disk and compacts its JSON before passing it to Docker. This error means os.ReadFile of the seccomp profile path failed — the file is missing, unreadable, or the path is wrong. It surfaces the errno in %v.

Source

Thrown at drivers/docker/driver.go:920

}

// takes a local seccomp daemon, reads the file contents for sending to the daemon
// this code modified slightly from the docker CLI code
// https://github.com/docker/cli/blob/8ef8547eb6934b28497d309d21e280bcd25145f5/cli/command/container/opts.go#L840
func parseSecurityOpts(securityOpts []string) ([]string, error) {
	for key, opt := range securityOpts {
		con := strings.SplitN(opt, "=", 2)
		if len(con) == 1 && con[0] != "no-new-privileges" {
			if strings.Contains(opt, ":") {
				con = strings.SplitN(opt, ":", 2)
			} else {
				return securityOpts, fmt.Errorf("invalid security_opt: %q", opt)
			}
		}
		if con[0] == "seccomp" && con[1] != "unconfined" {
			f, err := os.ReadFile(con[1])
			if err != nil {
				return securityOpts, fmt.Errorf("opening seccomp profile (%s) failed: %v", con[1], err)
			}
			b := bytes.NewBuffer(nil)
			if err := json.Compact(b, f); err != nil {
				return securityOpts, fmt.Errorf("compacting json for seccomp profile (%s) failed: %v", con[1], err)
			}
			securityOpts[key] = fmt.Sprintf("seccomp=%s", b.Bytes())
		}
	}

	return securityOpts, nil
}

const (
	// memoryNoLimit is a sentinel value for memory_max that indicates the
	// driver should not enforce a maximum memory limit
	memoryNoLimit = -1
)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure the seccomp profile file exists on the client host at the exact absolute path given.
  2. Distribute the profile via provisioning (or a template/artifact to a known client path) and reference that path.
  3. Check file permissions so the Nomad agent user can read it.
  4. If no custom profile is needed, use "seccomp=unconfined" (not recommended) or drop the option to use Docker's default.

Example fix

// before
config { security_opt = ["seccomp=./profile.json"] }
// after
config { security_opt = ["seccomp=/etc/nomad.d/seccomp/profile.json"] }
Defensive patterns

Strategy: validation

Validate before calling

for _, opt := range cfg.SecurityOpt {
	if strings.HasPrefix(opt, "seccomp=") && !strings.HasSuffix(opt, "unconfined") {
		path := strings.TrimPrefix(opt, "seccomp=")
		if _, err := os.ReadFile(path); err != nil {
			return fmt.Errorf("seccomp profile %s unreadable: %w", path, err)
		}
	}
}

Try / catch

if _, err := os.ReadFile(path); err != nil {
	if os.IsNotExist(err) {
		return fmt.Errorf("seccomp profile missing on client: %s", path)
	}
	return fmt.Errorf("cannot read seccomp profile: %w", err)
}

Prevention

When it happens

Trigger: config.security_opt has "seccomp=/path/to/profile.json" but the file does not exist on the Nomad client, the task lacks read permission, or a relative path resolves against the wrong working directory. Raised in createContainerConfig.

Common situations: Seccomp profile not shipped to the client node (only present in the job dir, not at the absolute path used), typo in the path, permissions tightened by hardening, or referencing a profile inside the container image (unavailable on host).

Understand the failure class

Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.

Related errors


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