hashicorp/nomad · error

failed to bind mount ns at %s: %v

Error message

failed to bind mount ns at %s: %v

What it means

After unsharing, NewNS bind-mounts the thread's new namespace from /proc/<tid>/ns/net onto a persistent file under /var/run/netns. If that mount fails, this message wraps it and NewNS ultimately returns 'failed to create namespace'. The mount is what keeps the namespace alive after the thread exits.

Source

Thrown at client/lib/nsutil/netns_linux.go:119

		}
		defer origNS.Close()

		// create a new netns on the current thread
		err = unix.Unshare(unix.CLONE_NEWNET)
		if err != nil {
			err = fmt.Errorf("error from unshare: %v", err)
			return
		}

		// Put this thread back to the orig ns, since it might get reused (pre go1.10)
		defer origNS.Set()

		// bind mount the netns from the current thread (from /proc) onto the
		// mount point. This causes the namespace to persist, even when there
		// are no threads in the ns.
		err = unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", unix.MS_BIND, "")
		if err != nil {
			err = fmt.Errorf("failed to bind mount ns at %s: %v", nsPath, err)
		}
	})()
	wg.Wait()

	if err != nil {
		return nil, fmt.Errorf("failed to create namespace: %v", err)
	}

	return GetNS(nsPath)
}

// UnmountNS unmounts the NS held by the netns object
func UnmountNS(nsPath string) error {
	// Only unmount if it's been bind-mounted (don't touch namespaces in /proc...)
	if strings.HasPrefix(nsPath, NetNSRunDir) {
		if err := unix.Unmount(nsPath, unix.MNT_DETACH); err != nil {
			return fmt.Errorf("failed to unmount NS: at %s: %w", nsPath, err)
		}

View on GitHub (pinned to 482b49bf1a)

Solutions

  1. Ensure /var/run/netns is a shared mount: mount --bind /run/netns /run/netns && mount --make-shared /run/netns
  2. Confirm the empty target file exists and is writable before the mount (library creates it; check disk/state)
  3. Grant CAP_SYS_ADMIN to the process
  4. Check /run is writable and has space

Example fix

// before: /run/netns lost its shared mount after container restart
// after (entrypoint):
// mount --make-shared /run/netns || true
ns, err := nsutil.NewNS()
Defensive patterns

Strategy: validation

Validate before calling

if err := unix.Mount("", "/run/netns", "", unix.MS_SHARED|unix.MS_REC, ""); err != nil && err != unix.EINVAL {
    return fmt.Errorf("/run/netns not prepared for ns bind-mount: %v", err)
}
if unix.Access("/run/netns", unix.W_OK) != nil {
    return errors.New("/run/netns is not writable")
}

Try / catch

ns, err := nsutil.NewNS()
if err != nil && strings.Contains(err.Error(), "failed to bind mount ns") {
    return fmt.Errorf("could not persist netns under /run/netns; check it is a shared writable mount: %w", err)
}

Prevention

When it happens

Trigger: unix.Mount(getCurrentThreadNetNSPath(), nsPath, "none", MS_BIND) fails during NewNS — e.g. the target file under /var/run/netns could not be used as a mountpoint (not created, wrong fs) or mount is denied (EPERM).

Common situations: /var/run/netns not a shared mount (propagation lost after container restart); read-only /run; antivirus/security software interfering with mounts; leftover stale files with the target name.

Related errors


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