spoolctl and pueue
Research date: 2026-09-21
spoolctl source baseline: main, commit f95600b
pueue source baseline: v4.0.4 (released 2026-03-02); main, commit 193ed22
Conclusion
Use pueue when a person manages long-running commands and wants to see and steer them. pueue can pause and resume tasks, send input to a running task, reorder the queue, set task dependencies, and run on Windows. spoolctl does none of these.
Use spoolctl when no person watches the queue. If a pueue task is running
when the daemon crashes, pueue marks it Killed on restart and does not run it
again. If a spoolctl worker dies, another worker confirms that the owner
process is gone and runs the job again. spoolctl also retries failed jobs with
backoff. pueue retries only when a person runs pueue restart.
Scope and architecture
pueue has a client, pueue, and a daemon, pueued. The daemon owns all task
processes. Clients talk to it over a socket. The daemon writes its state to
state.json after each change. It writes a temporary file first and renames
it, so a crash during a write does not corrupt the state. See the pinned
state save and restore code.
On restart, the daemon reads state.json. It changes every task that was
Running or Paused to Done with result Killed. It pauses every group
that has queued tasks, “to prevent unwanted execution of tasks due to a system
crash.” A person must start those groups again.
spoolctl has no daemon. Each spoolctl work process is a peer. All workers
coordinate through one SQLite file. A worker claims a job in one
BEGIN IMMEDIATE transaction and writes a heartbeat while the job runs. See
Guarantees.
Capability comparison
| Area | pueue v4.0.4 | spoolctl at f95600b |
|---|---|---|
| Primary job | Manage long-running commands for a person. | Run commands that must survive the death of any runner. |
| Coordinator | The pueued daemon. |
None. The SQLite file. |
| Queue state after a crash | Restored from state.json. |
Durable in SQLite. |
| Running task after a crash | Marked Killed. Not run again. |
Reclaimed and run again after the owner is confirmed dead. |
| Queued tasks after a crash | Group paused until a person resumes it. | Claimed by the next worker. |
| Retry of a failed task | Manual: pueue restart. |
Automatic, exponential backoff, default budget 3, then dead. |
| Per-task timeout | Not in the reviewed source. | Yes. Default 300 s. Kills the process group. |
| Parallelism | Groups, each with a parallel-task limit. | Lanes (--queue), each with an optional fleet-wide --slots ceiling. |
| Dependencies | Yes. | No. |
| Delayed start | Yes. | Yes: --after, --at. |
| Priorities | add --priority, plus manual reorder with switch. |
--priority at submit time. |
| Interaction | Pause, resume, send stdin, edit, reorder. | Cancel, retry, prune. No stdin, no pause. |
| Machine interface | JSON for status and log. |
JSON envelope on every verb, stable error codes, schemas, capabilities. |
| Install | Rust binaries. | Python 3 standard library only. |
| Platforms | Linux, macOS, Windows. | macOS, Linux. |
What pueue does well
- It is a mature, polished tool for people. Its status view, log view, and process controls are richer than anything in spoolctl.
- It persists queue state safely. The temp-file-then-rename write means a crash during a save does not leave a broken state file.
- Pausing groups after a crash is a careful choice for its audience. A person can check what happened before anything runs.
- It supports task dependencies and Windows. spoolctl supports neither.
The pinned README feature list documents these features.
What spoolctl does differently
pueue’s crash behavior is correct for a person at a keyboard. It is wrong for an unattended operator. After a crash, pueue waits for a person. spoolctl continues without one.
spoolctl never trusts a stale heartbeat alone. It reclaims a job only after it confirms that the owner process is dead. If the check is inconclusive, it leaves the job alone. Recovery can be slow, but two workers never run the same attempt at the same time.
spoolctl keeps output from each attempt. After three retries, you can read all three outputs, from any process.
Composition
The tools do not integrate. They can share a machine. Use pueue for commands you watch. Use spoolctl for commands that agents or scripts submit and nobody watches.
Limits and claims not to make
- Do not say pueue loses its queue when the daemon dies. It restores
state.json. - Do not say pueue has no failure state. Tasks end as
Success,Failed,Killed, and other results. - Do not call spoolctl exactly-once. A job can run again after an ambiguous crash.
- Do not say spoolctl replaces pueue for interactive work. It has no pause, no stdin, and no dependencies.