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 shutdownView on GitHub (pinned to 482b49bf1a)
Solutions
- Set destination to a path inside the task directory, e.g. local/rendered.conf or NAMESPACE-relative paths
- Use ${NOMAD_TASK_DIR} or ${NOMAD_SECRETS_DIR} interpolations instead of absolute host paths
- If another process needs the file at a host path, use a script/task that copies it rather than the template destination
- 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
- Always use local/, secrets/, or ${NOMAD_TASK_DIR}/${NOMAD_SECRETS_DIR} for destinations
- Avoid absolute host paths in template destination
- Clean/normalize interpolated destination paths before submission
- Test jobs in a sandboxed dev cluster to catch escape paths early
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
- template source path escapes alloc directory
- Invalid change mode. Must be one of the following: noop, sig
- could not find destination path relative to chroot: %w
- failed to sandbox alloc dir %q: %w
- All templates should have same Once value
AI-assisted analysis of hashicorp/nomad@482b49bf1a (2026-09-04).
Data as JSON: /api/errors/773bc68ac875b0bc.
Report an issue: GitHub.