Skip to main content

serialq

ci

One-at-a-time execution. A cross-process serial gate plus a persistent FIFO job queue, for CLIs and APIs that must never run concurrently.

Some backends are strictly serial: one in-flight call at a time, across every model, key, or client — overlap and you get rate-limited, corrupted, or billed twice. Cron jobs don't know about each other, shell scripts don't coordinate, and "just be careful" stops working at 3am. serialq is the bouncer: whoever holds the gate is the only thing running.

pip install git+https://github.com/intertermux-code/serialq.git

# ad-hoc: blocks until the gate is free, then runs
serialq run --gate qwen -- my-llm-cli ask "summarize this thread"

# fire-and-forget: queue it, a worker runs jobs one by one
serialq enqueue --gate qwen -- my-llm-cli batch jobs/42.json
serialq worker --gate qwen            # drain forever (systemd, tmux, ...)
serialq worker --gate qwen --once     # drain once, then exit (cron-friendly)

serialq list --gate qwen
serialq log 20261005-a3f9c1
serialq status --gate qwen

Zero dependencies, standard library only. POSIX only (Linux, macOS) — it relies on fcntl locks.

Why not just a lock file?

Because lock files go stale. serialq uses fcntl advisory locks, which the kernel releases when the holder process dies — a crashed job can never wedge the gate. The queue goes further:

  • Crash recovery. If a worker is kill -9'd mid-job, the next worker re-queues the orphaned job instead of losing it. Only one worker per gate can run at a time, so the recovery is unambiguous.
  • Timeouts that actually kill. --kill-after terminates the whole process group (SIGTERM, then SIGKILL), not just the parent.
  • Retries with backoff. enqueue --retries 3 re-queues failures with exponential backoff (5s, 10s, 20s … capped at 5 minutes).
  • Atomic queue writes. The job store is rewritten under an exclusive lock with fsync; a corrupt store is quarantined, never silently dropped.
  • Ctrl-C behaves. serialq run forwards SIGINT to the child, so interactive commands interrupt the way you'd expect.

Use cases

  • Serial-only LLM backends. Some model gateways allow exactly one in-flight request across all models. Prefix every call and stop thinking about it:
    serialq run --gate qwen --timeout 3600 -- llm chat model-a "prompt one" &
    serialq run --gate qwen --timeout 3600 -- llm chat model-b "prompt two" &
    wait  # they ran sequentially, in order
    
  • Overnight batch pipelines. Enqueue a hundred jobs, let one worker chew through them; check serialq list --all in the morning.
  • License-limited tools. One floating license, many cron jobs — put the tool behind a gate named after the license.
  • Flaky deploys. enqueue --retries 5 on the deploy script; transient failures retry themselves with backoff.

Reference

Command What it does
run [-g GATE] [--timeout S] [--kill-after S] -- CMD… Run CMD under the gate, blocking until free. Exits with CMD's exit code.
enqueue [-g GATE] [--name N] [--retries N] [--kill-after S] -- CMD… Queue CMD, print the job id.
worker [-g GATE] [--once] [--idle-timeout S] Run queued jobs FIFO. SIGTERM/SIGINT finish the current job, then stop.
list [-g GATE] [--all] Show queued/running jobs (--all includes finished).
log [-g GATE] ID Print a job's captured output.
cancel [-g GATE] ID Cancel a queued job.
status [-g GATE] Gate busy/free plus queue counts.

Gates are just names ([A-Za-z0-9_-], --gate or SERIALQ_GATE env). State lives in SERIALQ_DIR (default ~/.local/share/serialq), one directory per gate: the lock files, jobs.json, and per-job logs.

A systemd user unit template ships in contrib/:

cp contrib/serialq-worker@.service ~/.config/systemd/user/
systemctl --user enable --now serialq-worker@qwen

Design notes

  • One module, ~500 lines, no dependencies. The whole thing fits in your head.
  • The gate and the queue are separate locks: enqueue/list never block behind a long-running job.
  • Job ids are YYYYMMDD- plus 6 hex chars — sortable, greppable, unique.
  • Exit code 124-style semantics aren't faked: timeouts are reported in the job log and on stderr.

Limitations

  • POSIX only. Windows would need a different locking primitive.
  • FIFO, no priorities — deliberate. If you need priorities you probably need a real queue.
  • The worker is single-threaded by design: one gate, one job at a time. That's the point.

License

MIT.

Metadata

Release files for serialq 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for serialq 0.1.0
File Size Uploaded
serialq-0.1.0.tar.gz 14.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for serialq 0.1.0
File Interpreter ABI Platform
serialq-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 25.3 kB

Release files / serialq-0.1.0.tar.gz

Download URL serialq-0.1.0.tar.gz
Size 14.5 kB
Tags Source
SHA-256 checksum
How to use checksums
79d8b608178a1239e1d127ebf4561bca91a950b8e0b68d401f337ed10ccdbd7c
BLAKE2b-256 checksum
How to use checksums
2b2c8d0394b7ff16ec124b3a54c7539250d7250a0e4c53e4daf8f7ad42ab5979
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release files / serialq-0.1.0-py3-none-any.whl

Download URL serialq-0.1.0-py3-none-any.whl
Size 10.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e623f9a2a92218b5297d627e72ab2089fcf5ea0477472410dfc8d35142aa38d2
BLAKE2b-256 checksum
How to use checksums
00dc6e94e2309b44d736f5d910ed0ce02e83882ccf90b93f29af55b018441244
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 5, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page