Open-source project
shell-pool/shpool avatar
shell-pool/shpool

shpool and the narrow job of outliving a dropped ssh link

Think tmux, then aim... lower

2,019 stars69 forksRustApache-2.0

At a glance

What is it?
shpool is a Rust service that hands out named remote shell sessions, so a dropped ssh connection no longer takes your work with it. It does persistence and little else, which is both its strength and its boundary.
Who is it for?
shpool fits one specific person: someone who sshs into Linux hosts all day and needs only the session to survive a dropped link. It is the wrong tool for window splitting, tiling or a multiplexed local terminal, because it provides none of that.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 4 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The dropped ssh link that shpool exists to absorb

shpool targets one failure: the ssh connection that dies while you are mid task on a remote host. Its answer is a named shell session owned by a daemon on the far side of the wire, so a compiler, a test run or a half finished patch keeps running after the client vanishes. A local terminal emulator cannot do that, because when the ssh channel closes the remote process group takes the hangup with it. Moving ownership into a separate long lived process removes that coupling, and everything else in the project follows from it.

Installation follows the server rather than your laptop. shpool has to live on the remote host, and the documentation states that no extra software is required on the client. You then create one named session per terminal: the README works through `shpool attach main`, where the name is yours to pick. If the link fails, you ssh back in and run the same command again, and the shell is where you left it. Nothing about that is fast. No local cache is described, no replay of output that scrolled past while you were away, and no state synchronised between two sessions with different names.

A daemon that owns subshells instead of taking over the terminal

The architecture is two processes and a table. Running in daemon mode, shpool listens for incoming connections, opens up subshells and retains ownership of them in a table, and `shpool attach` connects to that daemon instance and passes a name. A name that is new gets a subshell opened under it. Your programs therefore descend from the daemon rather than from the ssh session, and that parent relationship is the reason a forced disconnect costs you nothing.

What it will not do is act as a multiplexer. tmux and GNU screen take over the whole terminal and provide window splitting and tiling, while shpool only provides persistent sessions. The payoff named in the documentation is that shpool does not break native scrollback or copy-paste, which is exactly what you need after an hour of reading a long build log backwards with the mouse. The trade is explicit and one sided. Persistence is what you get; splitting, tiling and window layout are not part of the package at all.

cargo install plus systemd, or autodaemonize and move on

Linux is the primary supported platform, and the install recipe there pairs a crates.io build with a user level systemd unit:

bash
cargo install shpool
curl -fLo "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/shpool.service" --create-dirs https://raw.githubusercontent.com/shell-pool/shpool/master/systemd/shpool.service
sed -i "s|/usr|$HOME/.cargo|" "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/shpool.service"
curl -fLo "${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user/shpool.socket" --create-dirs https://raw.githubusercontent.com/shell-pool/shpool/master/systemd/shpool.socket
systemctl --user enable shpool
systemctl --user start shpool
loginctl enable-linger

The first line builds the binary, the two curl lines fetch unit files out of the master branch, and the sed rewrites the unit's binary path from /usr to the Cargo directory under your home. The final three lines enable the unit, start it, and call `loginctl enable-linger`, which is what keeps a user unit alive once you log out.

Two details get missed in a skim. The sed rewrite is applied to shpool.service alone, and the socket unit fetched on the line after it is never patched, so read a vendored shpool.socket rather than assuming the same path was corrected. And because the curl targets master while cargo installs a published version, the unit file and the binary it launches are not guaranteed to match one another.

Without systemd the install is one command:

bash
cargo install shpool

From there you can port `systemd/shpool.service` to your own init system, or let autodaemonization handle it: shpool forks a daemon on the fly when it notices one is missing, and that behaviour is on by default. The `nodaemonize` config option and the `-d/-D` command line switches are what control it.

Homebrew on macOS, a platform with failing tests of its own

On a Mac the install is a formula plus a login service:

bash
brew install shpool
bash
brew services start shpool

The second command starts the daemon at login, which settles the daemonization question on that platform without touching any config file. What deserves a closer read is what the Platform Support section admits: shpool runs on linux and mac, Linux is the primary supported platform, and mac currently still has a few tests which don't pass. A tidy two command install path therefore sits on the unfinished side of that split. HACKING.md carries more on the state of mac support, and the README points readers at the test tree to see which tests are disabled.

That split matters mainly when the host you ssh into is the Mac itself, since shpool has to be installed on the remote host. For an engineer whose remote machines are all Linux, the formula is beside the point, and the disabled test list is the thing worth reading before trusting a Mac as a session host.

Session names that follow a variable you change from anywhere

shpool supports a template syntax for generating values from a central list of variables, much like the shell environment does, using `{var}` substitution. Four places accept templates: session names, the `attach --dir` flag, the `attach --cmd` flag and the `attach --start-cmd` flag. The purpose is switching several sessions in one move. When you run `shpool var set <var-name> <new-value>`, the daemon broadcasts the complete variable set to all `shpool attach` processes, each one recomputes its templates, and any process whose session name came out different hangs up and reconnects to the new session.

The worked example is worth following literally, because the session name never gets typed twice. Start from `{workspace=shpool}`, run `shpool attach '{workspace}-edit'` in one terminal and `shpool attach '{workspace}-main'` in another, and you sit in shpool-edit and shpool-main. A bug in another codebase calls for `shpool var set workspace yourco`, after which both terminals disconnect and land on yourco-edit and yourco-main; running `shpool var set workspace shpool` puts them back on your patch.

The boundary sits in that list of four. Templates are an attach time feature, and none of the four documented slots is detach, so the broadcast that renames your sessions is not documented to touch `shpool detach main`, the literal command the troubleshooting note gives for a session that will not let go.

A wedged connection and the single detach keybinding

Reconnection is the easy case. The hard case is a terminal that hangs and a window you then close by force, because shpool may still think a terminal is attached to that session. The documented cause is an ssh proxy holding the connection open in the vain hope that it will get some traffic again. The remedy is to ssh back in and run `shpool detach main`, which forces the session to detach and lets you attach again.

Keybindings are the other way out, and they are thin. The README says shpool supports keybindings, then concedes that for the moment it supports keybinding. The single binding is `Ctrl-Space Ctrl-q`, which detaches from the current session, and CONFIG.md documents how to point it at something else through the detach-keybinding setting. No list of binding actions appears in the prose; they are defined by the `Action` enum in `keybindings.rs` under `libshpool/src/daemon/keybindings.rs`. Changing the key is a config edit, while adding an action means writing Rust.

Put the two escape routes together and the gap is plain. The keybinding needs a terminal still passing keystrokes, and the detach command needs a second working connection, so the failure mode that strands a session in the first place is the one both routes fight. Anyone running this behind an ssh jump host should learn that before an incident rather than during one.

Orphaned jobs accumulate inside the daemon process tree

Because the daemon owns the process tree, your shell's exit semantics decide how much memory that tree holds. For bash the documentation gives a single line for your startup file:

bash
shopt -s huponexit

Without `huponexit` set, background processes spawned during the session stick around in the shpool daemon's process tree and eat up memory. The leak belongs to the shell rather than to shpool, but shpool is what keeps the tree alive, so nothing reaps it for you. A long running session that launches many detached helpers is the case where this bites, and no setting inside `~/.config/shpool/config.toml` is offered against it.

The silence around the fix matters just as much. This is documented for bash alone, with no equivalent note for zsh, fish or any other shell, even though the same daemon process tree ends up holding their children. If your fleet standard is zsh, working out the equivalent behaviour is on you before shpool points at a build box.

One workspace, three crates, and no documented upgrade path

The repository is a Cargo workspace with `resolver = "2"` and three members: `libshpool`, `shpool` and `shpool-protocol`. That split explains an otherwise odd detail in the keybinding docs, which point at a file inside the library crate rather than the binary. The root also carries `debian/`, `deny.toml`, `rust-toolchain.toml`, `rustfmt.toml`, `EVENTS.md`, `HACKING.md` and `systemd/`, that last one being the directory both install paths pull unit files from. Packaging, dependency policy and the pinned toolchain are all handled at the workspace level.

Cadence here is fast. The last push was on 2026-10-05, and three versions landed in three weeks: 0.11.3 on 2026-08-26, 0.11.4 on 2026-08-28 and 0.11.5 on 2026-09-17. The code is published under Apache-2.0, with a `LICENSE` file at the root and a `deny.toml` policy file beside `Cargo.lock`.

Upgrade guidance is where the writing stops. No page describes replacing an existing install, pinning a fleet to a known good version, or carrying live session state across an upgrade, and CONFIG.md is cited only as a place to find options rather than as a migration guide. If those sessions carry real work, rehearse the upgrade on a host you can afford to lose.

Editorial conclusion

shpool fits one specific person: someone who sshs into Linux hosts all day and needs only the session to survive a dropped link. It is the wrong tool for window splitting, tiling or a multiplexed local terminal, because it provides none of that. Check two things before rolling it out, whether `huponexit` is set in your shell startup file so orphan jobs do not pile up inside the daemon process tree, and whether any host in the fleet is a Mac, since the mac test suite is documented as incomplete. Then read the disabled test list for that platform rather than assuming parity.

Frequently asked questions

Does shpool replace tmux on a remote host?

No. tmux and GNU screen take over the whole terminal and add window splitting and tiling, while shpool only provides persistent sessions, and its stated advantage is that it does not break native scrollback or copy-paste. If you rely on panes, it is a different tool from the one you already have.

How do I detach a shpool session when my terminal is frozen?

If a forced close left shpool believing a terminal is still attached, ssh back in and run `shpool detach main` to force the session to detach. The documented cause of the stuck state is an ssh proxy holding the connection open, and the detach keybinding `Ctrl-Space Ctrl-q` is the other route out when the terminal still passes keystrokes.

What do I have to install on my laptop to use shpool?

Nothing. shpool has to be installed on the remote host where the sessions live, and no extra software is required on the client. The macOS Homebrew instructions apply when the host you ssh into is itself the Mac.

Can I switch all of my shpool sessions at once?

Yes. Templates use `{var}` substitution in session names and in the `attach --dir`, `attach --cmd` and `attach --start-cmd` flags, and `shpool var set <var-name> <new-value>` makes the daemon broadcast the whole variable set to every `shpool attach` process, which hang up and reconnect when their recomputed session name changes.

Is shpool usable on macOS?

It runs on linux and mac, but Linux is the primary supported platform and mac still has a few tests which don't pass. HACKING.md covers the state of mac support, and the test tree shows which tests are currently disabled, so read those before depending on a Mac as a session host.

Where does shpool keep its configuration?

Behaviour is customized by editing `~/.config/shpool/config.toml`, with CONFIG.md holding the in depth discussion of the options. Autodaemonization is on by default, and the `nodaemonize` config option together with the `-d/-D` command line switches is what controls it.

Official sources

  1. Issues
  2. License: Apache-2.0
  3. README
  4. Releases
  5. shell-pool/shpool on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/shell-pool-shpool.svg)](https://hysenlabs.com/projects/shell-pool-shpool)

Was this useful?