hashicorp/nomad · error · destEscapesErr

template destination path escapes alloc directory

Error message

template destination path escapes alloc directory

What it means

Companion to the source sandbox check: Nomad's template manager rejects a template whose destination path resolves outside the allocation directory when sandboxing is enabled. taskEnv.ClientPath(tmpl.DestPath, true) computes the path; if it escapes and sandboxEnabled, parseTemplateConfigs returns this sentinel error. It prevents templates from writing to arbitrary host locations.

Source

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

)

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

	// shutdownCh is used to signal and started goroutine to shutdown

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Set destination to a path inside the task directory, e.g. local/rendered.conf or NAMESPACE-relative paths
  2. Use ${NOMAD_TASK_DIR} or ${NOMAD_SECRETS_DIR} interpolations instead of absolute host paths
  3. If another process needs the file at a host path, use a script/task that copies it rather than the template destination
  4. As a last resort, disable the template sandbox in client config (security trade-off)

Example fix

// before
template {
  data        = "..."
  destination = "/etc/nginx/nginx.conf" // escapes alloc dir
}
// after
template {
  data        = "..."
  destination = "local/nginx.conf"
}
Defensive patterns

Strategy: validation

Validate before calling

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

Try / catch

_, err := mgr.ParseConfigs()
if err != nil {
    if errors.Is(err, destEscapesErr) {
        return fmt.Errorf("use local/ or NAMESPACE-relative destination: %w", err)
    }
    return err
}

Prevention

When it happens

Trigger: A task template block sets destination to an absolute path (e.g. /etc/nginx/nginx.conf) or a ../-relative path that resolves outside the alloc dir while the client's template sandbox is enabled.

Common situations: Jobs carried over from pre-sandbox Nomad that wrote rendered files to host paths; using ${NOMAD_META...} interpolation accidentally producing an absolute path; typos like destination = "local/../local/../out" escaping the dir.

Related errors


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