Skip to content
Get started
Get started
CLI: Run and Manage bunqueue from the Terminal
View Markdown
server · cli

The queue, from the CLI.

One binary, two roles: bunqueue start runs the server, every other command talks to a running one. Push, pull, ack, DLQ, cron, backups, and monitoring, all scriptable with JSON output.

bunqueue start # defaults: TCP 6789, HTTP 6790
bunqueue start --tcp-port 7000 --http-port 7001 # custom ports
bunqueue start --host 127.0.0.1 -p 6789 # bind to a specific host
bunqueue start --data-path ./data/production.db # persistent storage
bunqueue start --data-path ./data/production.db \
--completed-retention-ms 604800000 # retain completions for 7 days
AUTH_TOKENS=secret-token bunqueue start # with authentication
bunqueue start --config ./bunqueue.config.ts # with a config file

On startup the server prints its ports, data path, and enabled features (TLS, auth, S3 backup, cloud, shard count).

--max-completed-jobs bounds only the completed-job hot cache (default 50,000). --completed-retention-ms separately opts SQLite into durable age-based cleanup; without it, completed rows remain until an explicit clean or obliterate operation. --removed-result-retention-ms (default 60,000) and --max-removed-results (default 1,000) bound how long, and for how many jobs, the result of a job removed on completion stays in memory for a late waitUntilFinished(); 0 turns it off.

The global --port / -p option takes a whole number from 1 to 65535 for client commands (0 to 65535 with start); an invalid or missing value stops the command instead of falling back to 6789. --port= (empty) means “not given”: the port then comes from TCP_PORT, BUNQUEUE_TCP_PORT or BQ_TCP_PORT, and an env value that is not a port (such as the tcp://10.96.0.12:6789 Kubernetes sets for a Service named bunqueue-tcp) prints a warning and uses 6789.

Port flags follow the rules of their environment variables: ports 0-65535 (0 lets the OS pick a free port; 6789.5 and +6789 are read as 6789). As in earlier releases, a value that is not a port (abc, 70000, -5) prints a warning and starts on the default port (6789 for --tcp-port, 6790 for --http-port), and -p abc with start is ignored with a warning. A value earlier releases misread, such as 1e4 (read as port 1), stops the server with an error that names the flag: Fatal error: Invalid --tcp-port: "1e4" (expected a whole number between 0 and 65535). --max-completed-jobs (at least 1), --completed-retention-ms, --removed-result-retention-ms and --max-removed-results (at least 0) read their value like a JavaScript number (1e5 is 100000); an invalid one is ignored with a warning, Warning: Invalid completed-job retention "-1" (...). Ignoring it., and the config file or environment value applies. An empty value (--tcp-port=, --http-port=, --host=, --config=) means “not given”; --data-path, --auth-tokens, --tls-cert and --tls-key must not be empty.

Client commands default to localhost:6789:

bunqueue stats # local server
bunqueue stats --host 192.168.1.100 --port 6789 # remote server
bunqueue stats --token secret-token # with authentication

Set the token once via environment variable instead of repeating the flag. Priority: --token flag > BQ_TOKEN > BUNQUEUE_TOKEN.

export BQ_TOKEN=my-secret-token

The core loop: add a job, take it, and report the outcome.

bunqueue push emails '{"to":"user@example.com","subject":"Welcome"}'
# Job created: 019ce9d7-6983-7000-946f-48737be2b0f9

Job IDs are UUID v7 strings (time-ordered). Push accepts options for priority, retries, deduplication, and more:

bunqueue push emails '{"to":"vip@example.com"}' --priority 10 # higher = sooner
bunqueue push notifications '{"msg":"hi"}' --delay 5000 # run in 5s
bunqueue push orders '{"orderId":"ORD-123"}' --job-id order-ORD-123 # idempotent ID
bunqueue push emails '{"to":"a@b.c"}' --max-attempts 5 --backoff 2000 # retry config
bunqueue push notifications '{"userId":"1"}' -u user-1-notify # unique key (dedup)
bunqueue push aggregate '{"type":"sum"}' --depends-on job-1,job-2 # wait for other jobs
OptionShortDefaultDescription
--priority-P0Higher = processed first
--delay-d0Delay in ms before processing
--job-id--Custom ID for deduplication
--max-attempts-3Max retry attempts
--backoff-1000Delay between retries (ms)
--ttl--Time-to-live in ms
--timeout--Processing timeout in ms
--unique-key-u-Deduplication key
--depends-on--Comma-separated job IDs to wait for
--tags--Comma-separated tags
--group-id-g-Group identifier
--lifo-falseLast in, first out ordering
--remove-on-complete-falseAuto-delete on completion
--remove-on-fail-falseAuto-delete on failure

Pull the next job (typically a worker’s job, but handy for debugging):

bunqueue pull emails # prints the job, or "No job available"
bunqueue pull emails --timeout 5000 # wait up to 5s for a job

Then acknowledge (mark done) or fail it:

bunqueue ack 019ce9d7-... --result '{"delivered":true}' # result retrievable later
bunqueue fail 019ce9d7-... --error "SMTP connection timeout"

A failed job is retried with backoff while attempts remain, then moved to the DLQ.

bunqueue job get <id> # full details (use --json for the raw object)
bunqueue job state <id> # just the state
bunqueue job result <id> # the stored result
bunqueue job logs <id> # log entries attached to the job
bunqueue job cancel <id> # remove a waiting/delayed or completed job
bunqueue job promote <id> # run a delayed job now
bunqueue job discard <id> # send a job to the DLQ
bunqueue job progress <id> 50 --message "Halfway" # update progress (active jobs)
bunqueue job update <id> '{"to":"new@example.com"}' # replace job data
bunqueue job priority <id> 20 # change priority
bunqueue job delay <id> 60000 # move an active job back to delayed
bunqueue job wait <id> --timeout 30000 # block until completed, print result
bunqueue job log <id> "Checkpoint reached" --level info # append a log entry

Commands print OK on success, or Error: Job not found ... with exit code 1. job wait exits 1 if the job does not complete within the timeout.

bunqueue queue list # list all queues
bunqueue queue count emails # total jobs in a queue
bunqueue queue pause emails # workers stop picking new jobs
bunqueue queue resume emails
bunqueue queue paused emails # prints "Queue is paused" or "Queue is active"
bunqueue queue jobs emails --state waiting --limit 10 # list jobs by state
# states: waiting, delayed, active, completed, failed, prioritized, paused,
# waiting-children (--offset for pagination)
bunqueue queue clean emails --grace 3600000 --state completed # remove old jobs
# default state when omitted: waiting/delayed; --limit caps per call (default 1000)
# --state active is accepted but removes nothing: the server never cleans in-flight jobs
bunqueue queue drain emails # remove all waiting jobs (active ones keep running)
bunqueue queue obliterate emails # remove EVERYTHING for this queue

Inspect and recover permanently failed jobs (see Dead Letter Queue):

bunqueue dlq list emails # entries with error and timestamp (--count 10)
bunqueue dlq retry emails # re-queue all, prints the count moved
bunqueue dlq retry emails --id <job-id> # re-queue one
bunqueue dlq purge emails # delete all entries, prints the count

Schedule recurring jobs (see Cron Jobs):

# Cron expression: daily at 6 AM (optionally --timezone/-z Europe/Rome)
bunqueue cron add daily-report -q reports -d '{"type":"daily"}' -s "0 6 * * *"
# Cron scheduled: daily-report (next run: 2024-01-16T06:00:00.000Z)
# Plain interval: every 30 minutes
bunqueue cron add health-check -q health -d '{"check":"all"}' -e 1800000
bunqueue cron list # name, queue, schedule, executions, next run
bunqueue cron delete daily-report
bunqueue rate-limit set emails 100 # max 100 jobs/second
bunqueue concurrency set emails 10 # max 10 concurrent jobs
bunqueue rate-limit clear emails
bunqueue concurrency clear emails
bunqueue ping # quickest TCP liveness check
bunqueue stats # waiting/active/delayed/completed/failed/DLQ counts, uptime, rates
bunqueue metrics # Prometheus text format, same as GET /prometheus
bunqueue health # alias of stats over TCP
bunqueue version # client + server version, warns on mismatch

For a JSON health payload (status, version, memory, connections), use the HTTP endpoint: curl http://localhost:6790/health.

bunqueue doctor runs a full diagnostic: client and server version, reachability, health status, uptime, connections, queue counts, and memory. It prints a check-by-check report and All checks passed. when healthy. Use --host/--port to check a remote server.

bunqueue worker list # registered workers with status
bunqueue worker register email-worker -q emails,notifications
bunqueue worker unregister w-abc123
bunqueue webhook list
bunqueue webhook add https://example.com/hooks -e job.completed,job.failed -q emails
# Webhook added: <id> (keep the ID for webhook remove)
bunqueue webhook remove <id>

--events (-e) is required; valid events are job.pushed, job.started, job.completed, job.failed, job.progress. Optional: --queue/-q filter and --secret/-s HMAC secret. See Webhooks.

Backup commands run locally, not through the TCP server, and read the same configuration as the server: the config file (bunqueue.config.ts in the working directory, or --config <file> / -c <file>), then the environment. They need a persistent SQLite path (storage.dataPath, else BUNQUEUE_DATA_PATH or its aliases) and S3 credentials from the backup section or the S3_* variables, including temporary S3_SESSION_TOKEN credentials when used (see S3 Backup).

bunqueue backup now # create a backup, prints key/size/duration
bunqueue backup list # list backups in the bucket
bunqueue backup status # show configuration
bunqueue backup restore <key> -f # restore; requires --force, stop the server first
bunqueue backup status -c ./config/production.config.ts # use a specific config file

Stopping is mandatory for restore. The command validates a temporary candidate and quarantines stale SQLite WAL/SHM sidecars, but it cannot invalidate a database handle held by a running server.

OptionShortDescriptionDefault
--host-HServer hostnamelocalhost
--port-pTCP port6789
--token-tAuthentication token (env: BQ_TOKEN, BUNQUEUE_TOKEN)-
--tls-Connect with TLS (verify with system CAs)false
--tls-ca <file>-Trust a custom CA cert (implies --tls)-
--tls-no-verify-TLS without cert verification (self-signed, dev only)false
--json-Output as JSONfalse
--help-Show help-
--version-Show version-

Every command supports --json. Server commands print the raw server response ({ "ok": true, ... }), so nest your jq path under the response field (.stats, .jobs, .job, .counts, …); the local backup commands print { "success", "message", "data" } instead:

bunqueue stats --json | jq '.stats.waiting'
# 234

Process a job manually:

JOB=$(bunqueue pull emails --json) # { "ok": true, "job": { ... } }
JOB_ID=$(echo $JOB | jq -r '.job.id')
echo "Processing job $JOB_ID..." # your logic here
bunqueue ack $JOB_ID --result '{"processed":true}'

Daily maintenance script:

#!/bin/bash
bunqueue queue clean emails --grace 86400000 --state completed
bunqueue dlq purge emails
bunqueue backup now