Once proot-distro is installed (hack 1) and the Ubuntu rootfs is in place, actually launching proot requires a long command with many flags. Bind mounts expose host filesystems into the guest. Environment variables configure the guest shell. Special flags handle Android filesystem limitations.
Typing or remembering this command is impractical, especially over SSH on a phone keyboard. The helper script captures the exact invocation that works and makes it a single command. Other scripts (start-openclaw, restart-gw) call run-proot.sh rather than duplicating the proot flags.
This hack is marked LEGACY. The native migration on Feb 15, 2026 eliminated proot entirely. The gateway now runs directly on Termux with node22-icu and an LD_PRELOAD compat shim, removing the 6-layer stack in favor of 4 layers.
#!/data/data/com.termux/files/usr/bin/bash
# run-proot.sh — Launch proot Ubuntu environment for OpenClaw
# Deploy to: $PREFIX/bin/run-proot.sh
set -euo pipefail
PREFIX="/data/data/com.termux/files/usr"
ROOTFS="$PREFIX/var/lib/proot-distro/installed-rootfs/ubuntu"
PROOT="$PREFIX/bin/proot"
# Load environment variables from .openclaw/env if it exists
ENV_FILE="$ROOTFS/root/.openclaw/env"
ENV_ARGS=()
if [ -f "$ENV_FILE" ]; then
while IFS='=' read -r key value; do
# Skip comments and blank lines
[[ "$key" =~ ^#.*$ || -z "$key" ]] && continue
ENV_ARGS+=("-e" "$key=$value")
done < "$ENV_FILE"
fi
exec "$PROOT" \
--link2symlink \
--kill-on-exit \
--root-id \
--rootfs="$ROOTFS" \
--bind=/dev \
--bind=/proc \
--bind=/sys \
--bind="$PREFIX:/termux-prefix" \
--bind=/data/data/com.termux/files/home:/termux-home \
--cwd=/root \
"${ENV_ARGS[@]}" \
/usr/bin/env -i \
HOME=/root \
LANG=C.UTF-8 \
TERM="$TERM" \
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" \
/bin/bash --login "$@"Deploy the script:
cat > $PREFIX/bin/run-proot.sh << 'SCRIPT'
#!/data/data/com.termux/files/usr/bin/bash
# ... (paste full script above)
SCRIPT
chmod +x $PREFIX/bin/run-proot.shUsage from other scripts or directly:
# Interactive login shell
run-proot.sh
# Run a single command inside proot
run-proot.sh -c "node --version"
# Used by start-openclaw to launch the gateway
run-proot.sh -c "cd /root && node /root/.openclaw/gateway/dist/index.js"Key flags explained:
--link2symlink: Translates hard link syscalls to symlink. Required because Android's filesystem does not reliably support hard links from proot userspace. Without it, npm install fails when hardlinking files in node_modules.--kill-on-exit: When the proot parent dies (OOM kill, SSH disconnect), all child processes inside the guest are also killed. Prevents orphaned Node.js processes from consuming RAM silently.--root-id: Makes the process appear as UID 0 inside the guest. Without it, apt and npm complain about permission errors writing to /usr/ and /root/.--bind=/dev, --bind=/proc, --bind=/sys: Expose kernel interfaces. Node.js needs /proc/self/ for process introspection, and some npm packages read /sys/ for hardware info.--bind=$PREFIX:/termux-prefix: Makes Termux binaries (git, curl, etc.) accessible from inside proot at a predictable path.run-proot.sh drops into a bash shell with whoami returning root.ls /termux-prefix/bin/ shows Termux binaries accessible from inside proot.ls /termux-home/ shows the Termux home directory.echo $HOME returns /root, echo $LANG returns C.UTF-8.echo $PATH shows only Linux paths, no Termux paths leaked.run-proot.sh -c "cat /root/.openclaw/env" shows the loaded API keys (confirms env loading works).run-proot.sh -c "ls /proc/self/status" succeeds (confirms /proc is bound).env -i invocation strips any leaked Termux environment variables. Without it, $PATH inside proot might include Termux paths like $PREFIX/bin, causing the wrong binaries to be found (e.g., Termux's old Node v12 instead of proot's Node v22).$ROOTFS/root/.openclaw/env must use KEY=VALUE format with no spaces around =. Quoted values like KEY="value with spaces" are passed literally including the quotes. Do not use export prefixes in the file.--bind=/dev mount is effectively read-only from proot's perspective. Writing to /dev/ inside the guest does not affect the host device nodes.ENV_ARGS array is empty (no env file), the "${ENV_ARGS[@]}" expansion produces nothing, which is correct. But if set -u catches an unbound variable, add a fallback: "${ENV_ARGS[@]+"${ENV_ARGS[@]}"}".--cwd=/root flag sets the initial working directory. If /root does not exist in the rootfs (corrupted install), proot silently falls back to /, which can break scripts that assume they start in $HOME.A single run-proot.sh command launches a correctly configured proot Ubuntu environment with all bind mounts, environment variables, and compatibility flags. Other scripts call it as a building block, avoiding duplicated proot flags across the codebase. The script was the backbone of the proot-based stack until the native migration made it obsolete, reducing the stack from 6 layers to 4 and eliminating the per-syscall proot overhead.