hashicorp/nomad · error

failed to sandbox alloc dir %q: %w

Error message

failed to sandbox alloc dir %q: %w

What it means

The template render subprocess (z_template_render.go init/readTemplate) invokes the platform-specific sandbox() before opening the template source file; when sandbox() fails (e.g. the chroot-relative destination error above), this wrapper reports that the alloc dir could not be sandboxed. It means the renderer could not securely map the source path into the alloc-dir sandbox, so it aborts rather than reading an unsandboxed path.

Source

Thrown at client/allocrunner/taskrunner/template/renderer/z_template_render.go:84

			os.Exit(ExitError)
		}
	}
}

func readTemplate() error {
	var (
		sandboxPath, sourcePath string
		err                     error
	)

	flags := flag.NewFlagSet("template-render", flag.ExitOnError)
	flags.StringVar(&sandboxPath, "sandbox-path", "", "")
	flags.StringVar(&sourcePath, "source-path", "", "")
	flags.Parse(os.Args[3:])

	sourcePath, err = sandbox(sandboxPath, sourcePath) // platform-specific sandboxing
	if err != nil {
		return fmt.Errorf("failed to sandbox alloc dir %q: %w", sandboxPath, err)
	}

	f, err := os.Open(sourcePath)
	if err != nil {
		return fmt.Errorf("failed to open source file %q: %w", sourcePath, err)
	}
	defer f.Close()

	_, err = io.Copy(os.Stdout, f)
	return err
}

func writeTemplate() (*renderer.RenderResult, error) {

	var (
		sandboxPath, destPath, perms, user, group string
	)

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Inspect the wrapped inner error (often 'could not find destination path relative to chroot') and align template source/destination paths within the alloc dir
  2. Rewrite template block source/destination to use alloc-dir-relative paths (local/, secrets/)
  3. Check client alloc_dir configuration for symlinks or non-standard mounts and normalize them
  4. Review Nomad version changelog for template sandbox behavior changes and adjust the job spec accordingly
  5. Restart the allocation after fixing paths so the renderer subprocess re-runs with correct flags

Example fix

// before: template source outside alloc dir sandbox
template {
  source      = "/opt/templates/app.conf.tpl"
  destination = "local/app.conf"
}

// after: source staged inside the alloc dir (or inlined via data)
template {
  data        = file("templates/app.conf.tpl") // or embed the template text
  destination = "local/app.conf"
}
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check that template paths live under the sandbox/alloc dir before render
func pathUnderSandbox(sandboxPath, p string) error {
    absSandbox, err := filepath.Abs(sandboxPath)
    if err != nil { return err }
    absP, err := filepath.Abs(p)
    if err != nil { return err }
    rel, err := filepath.Rel(absSandbox, absP)
    if err != nil || strings.HasPrefix(rel, "..") {
        return fmt.Errorf("path %q is not under sandbox %q", p, sandboxPath)
    }
    return nil
}

Prevention

When it happens

Trigger: readTemplate is called during subprocess init; it parses -sandbox-path and -source-path flags and calls sandbox(sandboxPath, sourcePath). Any error returned by sandbox (such as filepath.Rel failure when the source path isn't under the sandbox path) is wrapped with this message and returned, aborting the render.

Common situations: Consul Template render events where the source/destination paths don't sit inside the alloc dir; clients where chroot behaves differently than expected and path mapping fails; alloc dirs with symlinks or unusual mount points breaking path relativization; upgrades changing template sandbox behavior causing previously working templates to fail.

Related errors


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