spoolctl and nq
Research date: 2026-09-21
spoolctl source baseline: main, commit f95600b
nq source baseline: 1.0 (2024-07-03), commit 8bad1d0
Conclusion
nq is the closest relative of spoolctl in spirit. Neither tool has a daemon. Both use the filesystem to coordinate. Both are small.
Use nq to run commands one after
another, in the background, with no setup. It is a single C file and a good
nohup replacement.
Use spoolctl when jobs must run in parallel, retry on failure, or survive a reboot. nq runs exactly one job at a time per queue directory. A queued nq job is a waiting process, so a reboot or kill of that process drops it. nq never retries.
Scope and architecture
Each nq call creates a log file named ,TIMESTAMP.PID and locks it with
flock(2). The job waits until every earlier lock file in the directory is
unlocked, then runs. The lock is shared with the job process, so it releases
when the job ends. The README says: “Exclusive execution is maintained
strictly.”
When the job ends, nq appends [exited with status N.] or
[killed by signal N.] to the log. With $NQDONEDIR set, it moves a
successful log there. With $NQFAILDIR set, it moves a log with a nonzero
exit there. A job killed by a signal stays in place. See the pinned
exit handling code.
spoolctl also has no daemon, but it keeps job state as rows in one SQLite file, not as processes. A queued job is a row. It waits for any worker, now or after a reboot. See Guarantees.
Capability comparison
| Area | nq 1.0 | spoolctl at f95600b |
|---|---|---|
| Primary job | Run command lines in sequence, in the background. | Run commands that must survive the death of any runner. |
| Coordinator | None. flock(2) on log files in $NQDIR. |
None. One SQLite file. |
| Concurrency | One job at a time per directory. | Many workers; lanes with optional --slots ceilings. |
| Queued job | A waiting nq process. |
A database row. |
| Queued jobs after a reboot | Never start. Logs remain. | Claimed by the next worker. |
| Running job after its runner dies | Not run again. | Reclaimed and run again after the owner is confirmed dead. |
| Retry | No. Resubmit by hand with sh $jobid. |
Automatic, exponential backoff, then dead. |
| Failure record | Exit status in the log; optional move to $NQFAILDIR. |
failed and dead states, attempt history, error codes. |
| Per-job timeout | No. | Yes. Default 300 s. Kills the process group. |
| Output | One log file per job; nqtail follows it. |
Files per attempt; output verb from any process. |
| Machine interface | Log files and job IDs. | JSON envelope on every verb, stable error codes, schemas. |
| Install | One C binary. A shell version, nq.sh, also exists. |
Python 3 standard library only. |
| Platforms | POSIX.1-2008 with working flock(2). |
macOS, Linux. |
What nq does well
- It needs almost nothing. One small binary, no config, no database.
- Its order is strict and needs no polling.
- Any directory can be a queue. The README encourages wrappers that set
$NQDIRfor different purposes. - A log file doubles as a resubmit script.
sh ,TIMESTAMP.PIDruns the job again. - Its README has a fair comparison with
at,batch, and task-spooler.
What spoolctl does differently
nq ties a queued job to a live process. That is why it needs no daemon, and also why a reboot drops the queue. spoolctl ties a job to a row. Any worker started later can run it.
spoolctl runs jobs in parallel. Several workers can drain one queue. Lanes and slot ceilings limit how many run at once.
spoolctl retries without help and records each attempt. nq records one exit status and stops.
Composition
The tools do not integrate. For strictly serial work that must survive a
reboot, run one spoolctl worker per lane with --slots 1.
Limits and claims not to make
- Do not say nq needs a daemon. It needs none.
- Do not say nq has no failure record. The log states the exit status, and
$NQFAILDIRseparates failed jobs. - Do not say nq loses running output on a crash. The log file stays on disk.
- Do not claim spoolctl is smaller or simpler to install than nq.