windmill-labs/windmill · error

ENABLE_UNSHARE_PID is set but unshare test failed. Error: {}

Error message

ENABLE_UNSHARE_PID is set but unshare test failed.
Error: {}
Flags: {}

Solutions:
• Check if user namespaces are enabled: 'sysctl kernel.unprivileged_userns_clone'
• Check max user namespaces limit: 'cat /proc/sys/user/max_user_namespaces'
  (Some AMIs like Bottlerocket have max_user_namespaces=0 which disables user namespaces entirely)
• For Docker: Requires 'privileged: true' in docker-compose for --mount-proc flag
• For Kubernetes: Requires 'privileged: true' in securityContext for --mount-proc flag
• Try different flags via UNSHARE_ISOLATION_FLAGS env var (remove --mount-proc if privileged mode not possible)
• Alternative: Use NSJAIL instead
• Disable: Set ENABLE_UNSHARE_PID=false (or disableUnsharePid=true in Helm chart)

What it means

When ENABLE_UNSHARE_PID is enabled, Windmill workers isolate job processes into a PID namespace by invoking the `unshare` binary. At startup it runs a probe test; if the probe spawns but fails (non-success exit, checked via stderr output), and the setting is required, the worker panics with a diagnostic listing kernel userns settings, container privilege requirements, and alternatives (NSJAIL, disabling the feature).

Source

Thrown at backend/windmill-worker/src/worker.rs:474

        let test_result = std::process::Command::new("unshare")
            .args(&test_cmd_args)
            .output();

        match test_result {
            Ok(output) if output.status.success() => {
                if TINI_AVAILABLE.is_some() {
                    tracing::info!("PID namespace isolation enabled with tini. Flags: {}", flags);
                } else {
                    tracing::info!("PID namespace isolation enabled. Flags: {}", flags);
                }
                Some("unshare".to_string())
            },
            Ok(output) => {
                let stderr = String::from_utf8_lossy(&output.stderr);

                if *ENABLE_UNSHARE_PID {
                    panic!(
                        "ENABLE_UNSHARE_PID is set but unshare test failed.\n\
                        Error: {}\n\
                        Flags: {}\n\
                        \n\
                        Solutions:\n\
                        • Check if user namespaces are enabled: 'sysctl kernel.unprivileged_userns_clone'\n\
                        • Check max user namespaces limit: 'cat /proc/sys/user/max_user_namespaces'\n\
                          (Some AMIs like Bottlerocket have max_user_namespaces=0 which disables user namespaces entirely)\n\
                        • For Docker: Requires 'privileged: true' in docker-compose for --mount-proc flag\n\
                        • For Kubernetes: Requires 'privileged: true' in securityContext for --mount-proc flag\n\
                        • Try different flags via UNSHARE_ISOLATION_FLAGS env var (remove --mount-proc if privileged mode not possible)\n\
                        • Alternative: Use NSJAIL instead\n\
                        • Disable: Set ENABLE_UNSHARE_PID=false (or disableUnsharePid=true in Helm chart)",
                        stderr.trim(),
                        flags
                    );
                }

View on GitHub (pinned to e474e8803c)

Solutions

  1. Set ENABLE_UNSHARE_PID=false (or disableUnsharePid=true in Helm) if PID isolation is not strictly needed
  2. Check `sysctl kernel.unprivileged_userns_clone` and `cat /proc/sys/user/max_user_namespaces`; raise max_user_namespaces (e.g. sysctl -w user.max_user_namespaces=15000)
  3. Run the container with `privileged: true` (docker-compose / k8s securityContext) so --mount-proc works
  4. Adjust UNSHARE_ISOLATION_FLAGS env var to drop --mount-proc if privilege cannot be granted
  5. Switch to NSJAIL isolation as an alternative

Example fix

// before (k8s, non-privileged)
enabledUnsharePid: true
// after: either grant privileges
securityContext:
  privileged: true
// or disable the feature
enabledUnsharePid: false
Defensive patterns

Strategy: validation

Validate before calling

#!/bin/sh
# preflight check before enabling ENABLE_UNSHARE_PID
sysctl kernel.unprivileged_userns_clone 2>/dev/null || echo 'unprivileged_userns_clone: unknown'
ns=$(cat /proc/sys/user/max_user_namespaces 2>/dev/null || echo 0)
[ "$ns" -gt 0 ] || { echo 'max_user_namespaces=0 — unshare will fail'; exit 1; }
unshare --mount-proc true 2>/dev/null || { echo 'unshare probe failed (need privileged/cap_sys_admin)'; exit 1; }

Prevention

When it happens

Trigger: ENABLE_UNSHARE_PID=true (or disableUnsharePid=false in Helm) while the probe `unshare --mount-proc ...` fails: user namespaces disabled (max_user_namespaces=0, userns_clone=0), unprivileged userns restricted, or --mount-proc rejected because the container lacks CAP_SYS_ADMIN (non-privileged Docker/K8s).

Common situations: Bottlerocket/Amazon Linux AMIs with max_user_namespaces=0; Kubernetes pod without `privileged: true` securityContext; Docker compose without privileged/required capabilities; hardened kernels with unprivileged_userns_clone=0.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/4a5df6053b7cd864. Report an issue: GitHub.