Fair job queue on Redis: REST/WebSocket/TCP API, retries, archive, live dashboard.
1.0K
A job queue with fair load across projects, stored in Redis, with a live web dashboard built in.
xtjobs needs Redis 8+. Redis 8 includes the Query Engine that job listing uses.
docker network create xtjobs
docker run -d --name redis --network xtjobs redis:8-alpine redis-server --appendonly yes
docker run -d --name xtjobs --network xtjobs \
-p 8080:8080 -p 8081:8081 -p 9090:9090 \
-e XTJOBS_TOKEN=change-me \
-v xtjobs-data:/var/lib/xtjobs \
mukovoz/xtjobs
Open http://localhost:8080/: the dashboard asks for the token, and the REST API is under /api/v1.
curl -H "Authorization: Bearer change-me" -H "Content-Type: application/json" \
-d '{"queue":"emails","project":"shop","environment":"production","data":{"to":"[email protected]"}}' \
http://localhost:8080/api/v1/jobs
services:
redis:
image: redis:8-alpine
command: ["redis-server", "--appendonly", "yes"]
volumes: [redis-data:/data]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
xtjobs:
image: mukovoz/xtjobs:latest
environment:
XTJOBS_TOKEN: change-me
ports:
- "8080:8080" # dashboard + REST API
- "8081:8081" # WebSocket (dashboard, workers)
- "9090:9090" # TCP socket (workers, producers)
volumes:
- xtjobs-data:/var/lib/xtjobs
depends_on:
redis: { condition: service_healthy }
restart: unless-stopped
volumes:
redis-data:
xtjobs-data:
The image ships a default config at /etc/xtjobs/config.yaml: Redis at redis:6379, no auth, and the archive in /var/lib/xtjobs. Override it with environment variables, or mount your own file (-v ./config.yaml:/etc/xtjobs/config.yaml:ro).
| Variable | Default | Meaning |
|---|---|---|
XTJOBS_TOKEN | (none: no auth) | token for REST (Authorization: Bearer …), WebSocket and TCP |
XTJOBS_REDIS_ADDR | redis:6379 | Redis address |
XTJOBS_REDIS_PASSWORD | (none) | Redis password |
XTJOBS_REST_ADDR | :8080 | dashboard + REST API listen address |
XTJOBS_WEBSOCKET_ADDR | :8081 | WebSocket listen address |
XTJOBS_SOCKET_ADDR | :9090 | TCP socket listen address |
XTJOBS_UI_WEBSOCKET_URL | (page host, port 8081) | WebSocket address the dashboard connects to behind a reverse proxy: wss://jobs.example.com/ws, or /ws for the page's own host |
XTJOBS_CONFIG | /etc/xtjobs/config.yaml | config file path |
Always set XTJOBS_TOKEN when the ports can be reached from outside. Without it, anyone who can reach them has full access.
Port mapping: the dashboard connects to the WebSocket on port 8081 of the same host. If you change a port, change it on both sides (e.g. -e XTJOBS_WEBSOCKET_ADDR=:9081 -p 9081:9081). Behind a reverse proxy, route /ws to port 8081 and the rest to 8080, then set XTJOBS_UI_WEBSOCKET_URL=/ws.
/var/lib/xtjobs holds archive.db, the SQLite archive of finished jobs. Mount a volume to keep it.--appendonly yes) and maxmemory-policy noeviction.GET /health (no token) returns {"status":"ok","redis":"ok","version":"…"}, or HTTP 503 when Redis is down.docker logs xtjobs shows it.latest: the newest release.X.Y.Z (e.g. 0.3.1): a specific release. A released tag is never overwritten.Each tag covers linux/amd64 and linux/arm64. The image is about 32 MB (Alpine, static Go binary), runs as a non-root user, and has the dashboard built in.
Content type
Image
Digest
sha256:c0cf8cca8…
Size
9 MB
Last updated
1 day ago
docker pull mukovoz/xtjobs