hashicorp/nomad · error

opening seccomp profile

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.

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.

Appendix: 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)