{"skill":{"slug":"openclaw-tmux-persistent-process","displayName":"OpenClaw Tmux Persistent Process","summary":"Run programs that survive OpenClaw exec session cleanup and gateway restarts via tmux. Use when you need long-running servers, dev servers, tunnels (ngrok/cl...","description":"---\nname: openclaw-tmux-persistent-process\ndescription: >\n  Run programs that survive OpenClaw exec session cleanup and gateway restarts via tmux.\n  Use when you need long-running servers, dev servers, tunnels (ngrok/cloudflared),\n  coding agents, or any process that must outlive the current exec session.\n  Solves: exec background processes getting SIGKILL on session cleanup.\n  Recommended for all OpenClaw users running any long-lived process.\n---\n\n# OpenClaw Tmux Persistent Process\n\nRun programs that survive OpenClaw exec session cleanup and gateway restarts.\n\n## Why every OpenClaw user should have this\n\nOpenClaw runs shell commands via `exec`. But exec has a critical limitation that most users don't realize until something breaks:\n\n- **Background processes are tied to the exec session.** When the session cleans up (idle timeout, context compaction, or gateway restart), all background processes receive SIGKILL and die immediately.\n- **Gateway restarts kill everything.** Updates, config changes, or `openclaw gateway restart` — all running exec processes are gone.\n- **No warning.** Your tunnel, dev server, or build task just silently disappears.\n\nThis means anything you expect to keep running — a tunnel for a client demo, a dev server, a long build — can die at any moment without notice.\n\n**The solution:** Use **tmux** to create virtual terminals completely decoupled from the exec lifecycle. Programs in tmux survive gateway restarts, exec cleanup, and session timeouts. tmux runs independently of OpenClaw — even if the gateway goes down, your processes keep running.\n\n**Rule of thumb:** If a command might run longer than 2 minutes, use tmux.\n\n---\n\n## Socket convention\n\nAll operations use a dedicated socket to avoid interfering with the user's own tmux:\n\n```bash\nSOCK=\"/tmp/openclaw-tmux/openclaw.sock\"\nmkdir -p /tmp/openclaw-tmux\n```\n\n## Start a process\n\n```bash\nSESSION=\"my-server\"\n\n# Check if already running\nif tmux -S \"$SOCK\" has-session -t \"$SESSION\" 2>/dev/null; then\n  echo \"already running\"\nelse\n  tmux -S \"$SOCK\" new -d -s \"$SESSION\" \\\n    \"cd /path/to/project && npm run dev\"\n  echo \"started\"\nfi\n```\n\n## Monitor\n\n```bash\n# List all sessions\ntmux -S \"$SOCK\" list-sessions\n\n# View last 30 lines of output\ntmux -S \"$SOCK\" capture-pane -t \"$SESSION\" -p -S -30\n\n# Check if process is idle (back to shell prompt)\ntmux -S \"$SOCK\" capture-pane -t \"$SESSION\" -p -S -3 \\\n  | grep -qE '\\$\\s*$|❯' && echo \"IDLE\" || echo \"RUNNING\"\n```\n\n## Interact\n\n```bash\n# Send a command\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" \"command\" Enter\n\n# Send literal text (recommended, no special key parsing)\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" -l -- \"literal text\"\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" Enter\n\n# Ctrl+C to interrupt\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" C-c\n```\n\n## Stop / cleanup\n\n```bash\n# Kill one session\ntmux -S \"$SOCK\" kill-session -t \"$SESSION\"\n\n# Kill all\ntmux -S \"$SOCK\" kill-server\n\n# Clean stale socket\nrm -f \"$SOCK\"\n```\n\n## Start interactive programs (wait for ready)\n\nFor programs that need startup time (e.g. coding agents, REPLs):\n\n```bash\nSESSION=\"my-task\"\ntmux -S \"$SOCK\" new -d -s \"$SESSION\"\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" \"cd ~/project && node\" Enter\n\n# Wait for prompt before sending commands\nfor i in $(seq 1 15); do\n  OUTPUT=$(tmux -S \"$SOCK\" capture-pane -t \"$SESSION\" -p -S -5)\n  if echo \"$OUTPUT\" | grep -qE '❯|>\\s*$|ready'; then\n    break\n  fi\n  sleep 1\ndone\n\n# Now safe to send input\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" -l \"your command\"\ntmux -S \"$SOCK\" send-keys -t \"$SESSION\" Enter\n```\n\n## Common use cases\n\n| Use case | Session name | Command example |\n|----------|-------------|-----------------|\n| Dev server | `dev-server` | `npm run dev` |\n| Tunnel (ngrok) | `tunnel-ngrok` | `ngrok http 3000` |\n| Tunnel (localhost.run) | `tunnel-lr` | `ssh -R 80:localhost:3000 nokey@localhost.run` |\n| Background worker | `worker` | `python worker.py` |\n| Build task | `build-app` | `npm run build` |\n\n## Session naming\n\nLowercase + hyphens. No spaces.\n\n```\ndev-server       — development servers\ntunnel-{name}    — tunnels (ngrok, cloudflared, etc.)\nbuild-{project}  — build tasks\nworker-{name}    — background workers\n```\n\n## Important notes\n\n- **Reboot = gone.** tmux doesn't survive system restarts. Re-create sessions after reboot.\n- **Scrollback limit.** Default 2000 lines. Increase with: `tmux set-option -t \"$SESSION\" history-limit 10000`\n- **Socket stale.** If `list-sessions` says \"error connecting\", delete the socket and recreate.\n- **Environment.** tmux inherits env at creation time. Set vars in the command: `tmux new -d -s name \"export KEY=val && cmd\"`\n- **No conflict.** Dedicated socket means zero interference with user's own tmux.\n\n## Requirements\n\n- **tmux 2.6+** (for `send-keys -l` literal mode)\n- Check: `tmux -V`\n- Install: `brew install tmux` (macOS) or `apt install tmux` (Linux)\n","topics":["Coding"],"tags":{"latest":"1.0.0"},"stats":{"comments":0,"downloads":782,"installsAllTime":29,"installsCurrent":0,"stars":0,"versions":1},"createdAt":1773478917282,"updatedAt":1778491902083},"latestVersion":{"version":"1.0.0","createdAt":1773478917282,"changelog":"Initial release: run long-lived processes in tmux that survive OpenClaw exec cleanup and gateway restarts. Explains why every OpenClaw user needs this for tunnels, dev servers, and long tasks.","license":"MIT-0"},"metadata":null,"owner":{"handle":"darwin7381","userId":"s17d6805f0n7wkv9ewv2ayad7h84em8h","displayName":"Joey Luo","image":"https://avatars.githubusercontent.com/u/11506086?v=4"},"moderation":null}