FastWorker vs RQ
Comparing FastWorker and RQ (Redis Queue) — simplicity, features, FastAPI integration, and when to choose each for Python background jobs.
RQ (Redis Queue) is a deliberately minimalist Python task queue. If Celery feels heavy, RQ is often the next stop. FastWorker keeps RQ’s “just a queue” ethos but removes Redis from the picture — leaving you with a task queue that’s 100% Python processes.
The one-line summary
RQ keeps things simple by building on Redis. FastWorker keeps things simple by not needing Redis.
Both aim at the same target audience: Python teams who find Celery too much. FastWorker just pushes further.
Feature matrix
| Capability | FastWorker | RQ |
|---|---|---|
| External broker | None | Redis required |
| Minimal services | 2–3 Python processes | 3–4 (app, RQ worker, Redis) |
| Setup time | < 5 minutes | ~15 minutes |
| Built-in dashboard | Yes (auto-starts at :8080) | rq-dashboard (separate package) |
| Async FastAPI client | Yes | No (sync enqueue) |
| Priority queues | 4 levels, built-in | Multi-queue workers |
| Worker discovery | Automatic | Manual (workers listen on queue names) |
| Task chains | No | Limited (job dependencies) |
| Scheduled tasks | No | Via rq-scheduler |
| Task persistence | In-memory | Redis-backed (durable) |
| Retries with backoff | Manual | Retry class |
| Job cancellation | Basic | Supported |
| OpenTelemetry | Built-in | Manual instrumentation |
| Recommended scale | 1K–10K tasks/min | Similar |
Deployment: side by side
RQ — minimal setup
# You need Redis running somewhere
docker run -p 6379:6379 redis:7
# In your Python code
from redis import Redis
from rq import Queue
q = Queue(connection=Redis())
def send_email(user_id):
...
q.enqueue(send_email, user_id=42)
# Worker process
rq worker
Three things to keep running: your app, an RQ worker, and Redis.
FastWorker — minimal setup
# mytasks.py
from fastworker import task
@task
def send_email(user_id: int) -> bool:
...
return True
# One terminal
fastworker control-plane --task-modules mytasks
# In your app
await client.delay("send_email", 42)
Two things to keep running: your app and the control plane. No Redis.
Code: side by side
Enqueueing from FastAPI
# RQ
from fastapi import FastAPI
from redis import Redis
from rq import Queue
app = FastAPI()
q = Queue(connection=Redis(host="redis"))
@app.post("/signup")
async def signup(user_id: int):
q.enqueue("tasks.send_email", user_id) # sync call in async handler
return {"ok": True}
# FastWorker
from fastapi import FastAPI
from fastworker import Client
app = FastAPI()
client = Client()
@app.on_event("startup")
async def _start():
await client.start()
@app.post("/signup")
async def signup(user_id: int):
await client.delay("send_email", user_id) # async-native
return {"ok": True}
Priority
# RQ — use separate queues
q_hi = Queue("high", connection=redis)
q_lo = Queue("low", connection=redis)
q_hi.enqueue(send_receipt, order_id)
# Worker listens to queues in order
# rq worker high normal low
# FastWorker — one queue, enum priorities
from fastworker.tasks.models import TaskPriority
await client.delay("send_receipt", order_id,
priority=TaskPriority.HIGH)
RQ leans on multi-queue workers for prioritization. FastWorker gives you four enum levels on a single queue.
Where RQ wins
- Durable persistence. Jobs sit in Redis. If the worker crashes, they’re still there. FastWorker keeps the queue in memory.
- rq-scheduler. For recurring / scheduled jobs, RQ has a good story via
rq-scheduler. - Job cancellation. First-class.
- Simpler mental model for Redis shops. If you already run Redis, RQ piggybacks on it for free.
- Battle tested. RQ is mature and ships a lot of small conveniences FastWorker hasn’t built yet.
Where FastWorker wins
- No Redis. The whole point of this comparison. If you don’t already run Redis, RQ adds one more service to your stack. FastWorker doesn’t.
- Async FastAPI client. RQ’s
enqueueis synchronous. It’s fast, but it still blocks your event loop briefly. FastWorker’sClientis async-native. - Built-in dashboard.
rq-dashboardis a separate install. FastWorker’s GUI ships with the control plane and starts automatically. - Enum priorities. Using RQ’s multi-queue model for priorities is fine but requires you to think about queue names; FastWorker gives you four enum levels and a single interface.
- Automatic worker discovery. Subworkers find the control plane on startup — no manual queue name list.
Which to choose
Choose RQ if:
- You already run Redis and are happy to use it
- You need durable queues (tasks must survive a worker crash)
- You want
rq-schedulerfor recurring jobs - You need first-class job cancellation
Choose FastWorker if:
- You don’t want to run Redis just for a task queue
- You’re using FastAPI and want an async-native client
- You want a web dashboard without installing
rq-dashboard - You want enum priorities instead of multi-queue routing
Next steps
Frequently asked questions
Is RQ simpler than Celery?
Yes — RQ is explicitly designed to be a simple, Redis-backed alternative to Celery. FastWorker takes that same instinct one step further by dropping Redis entirely.
Does RQ work with FastAPI?
Yes, but RQ is synchronous by design. You call q.enqueue(...) from an async handler and it blocks the event loop briefly. FastWorker's client is async-native.
Can I keep using RQ and add FastWorker for specific jobs?
Yes. They don't conflict. A common pattern is running FastWorker for user-facing, latency-sensitive background tasks while keeping RQ for simpler long-tail jobs.