hashicorp/nomad · error · sourceEscapesErr

template source path escapes alloc directory

Error message

template source path escapes alloc directory

What it means

Nomad's template manager rejects a template whose source_path resolves outside the allocation directory when template sandboxing is enabled. taskEnv.ClientPath computes the allocation-relative path and reports whether it escapes; if escapes && sandboxEnabled, parseTemplateConfigs returns this package-level sentinel error. It protects against templates reading arbitrary files on the client host.

Source

Thrown at client/allocrunner/taskrunner/template/template.go:51

	structsc "github.com/hashicorp/nomad/nomad/structs/config"
)

const (
	// consulTemplateSourceName is the source name when using the TaskHooks.
	consulTemplateSourceName = "Template"

	// missingDepEventLimit is the number of missing dependencies that will be
	// logged before we switch to showing just the number of missing
	// dependencies.
	missingDepEventLimit = 3

	// DefaultMaxTemplateEventRate is the default maximum rate at which a
	// template event should be fired.
	DefaultMaxTemplateEventRate = 3 * time.Second
)

var (
	sourceEscapesErr = errors.New("template source path escapes alloc directory")
	destEscapesErr   = errors.New("template destination path escapes alloc directory")
)

// TaskTemplateManager is used to run a set of templates for a given task
type TaskTemplateManager struct {
	// config holds the template managers configuration
	config *TaskTemplateManagerConfig

	// lookup allows looking up the set of Nomad templates by their consul-template ID
	lookup map[string][]*structs.Template

	// runner is the consul-template runner
	runner *manager.Runner

	// signals is a lookup map from the string representation of a signal to its
	// actual signal
	signals map[string]os.Signal

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Move the template source file into the task's alloc dir (e.g. use an artifact block to fetch it) and reference it relatively
  2. Reference files already staged in the task dir, e.g. NAMESPACE/... via ${NOMAD_TASK_DIR} style relative paths
  3. If the host file is truly required, mount/ship it via artifact or template with content inline instead of source
  4. Only as a last resort, disable the template sandbox in client config (understood security trade-off)

Example fix

// before
template {
  source = "/etc/consul.d/config.hcl" // escapes alloc dir
  destination = "local/config.hcl"
}
// after
artifact {
  source      = "https://example.internal/config.hcl"
  destination = "local/config.hcl"
}
template {
  source      = "local/config.hcl"
  destination = "local/rendered.hcl"
}
Defensive patterns

Strategy: validation

Validate before calling

// check the template source stays inside the alloc dir before submit
src := filepath.Clean(tmpl.SourcePath)
if filepath.IsAbs(src) || strings.HasPrefix(src, "..") {
    return fmt.Errorf("template source %q must be inside the alloc dir", src)
}

Try / catch

_, err := mgr.ParseConfigs()
if err != nil {
    if errors.Is(err, sourceEscapesErr) {
        return fmt.Errorf("fix template source to an alloc-dir-relative path: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A task template block sets source to an absolute path or a ../-relative path that resolves outside the alloc dir, and the client has template sandboxing enabled (consul.template.template_sandbox enabled / nomad >= some version default).

Common situations: Migrating jobs from older Nomad where reading host paths via template source was allowed; referencing a shared file like /etc/ssl/certs/ca.pem as template source; accidental ../ in source paths.

Related errors


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