FastWorker

FastWorker vs ARQ

Comparing FastWorker and ARQ — two async-native Python task queues for FastAPI. Both speak asyncio; the difference is whether you run Redis.

Dipankar Sarkar · ·
arqcomparisontask-queuepythonfastapiasync

ARQ and FastWorker are the two Python task queues most often reached for by async-first teams. Both are designed around async/await, both fit FastAPI naturally, and both keep their APIs deliberately small. The dividing line is infrastructure: ARQ is Redis-backed, FastWorker is brokerless.

If you’ve already decided you want an async queue, this is the comparison that matters.

The one-line summary

ARQ is an async-native, Redis-backed job queue with durable persistence and built-in cron scheduling. FastWorker is an async-native, brokerless task queue — the same async ergonomics without Redis.

Feature matrix

CapabilityFastWorkerARQ
Async-native APIYesYes (asyncio-first)
External broker requiredNoneRedis (required)
Services in a minimal deploy2–3 Python processesapp + worker + Redis
Built-in dashboardYes (auto-starts at :8080)No
Priority queues4 levels, built-inNo built-in priority levels
Scheduled / cron jobsNo (use cron / K8s CronJob)Yes (cron jobs)
Deferred / delayed jobsNoYes (_defer_by / _defer_until)
Retries with backoffManual in task bodyYes (max_tries, Retry)
Durable persistenceIn-memory (1h TTL)Yes (Redis-backed)
Result storageIn-memory LRURedis
Automatic worker discoveryYesNo (static config)
FastAPI integrationNative async clientAsync (create_pool)
Multi-language workersNo (Python only)No (Python only)
Recommended scale1K–10K tasks/minRedis-bound
LicenseMITMIT

Code: side by side

Defining a task

# ARQ — async function + a WorkerSettings class
async def send_email(ctx, user_id: int) -> bool:
    return True

class WorkerSettings:
    functions = [send_email]
    redis_settings = RedisSettings(host="redis")
# FastWorker — a decorated function, no settings class, no Redis
from fastworker import task

@task
async def send_email(user_id: int) -> bool:
    return True

ARQ collects its jobs on a WorkerSettings class and needs Redis connection settings. FastWorker’s task is just a decorated function.

Enqueuing from FastAPI

# ARQ
redis = await create_pool(RedisSettings(host="redis"))
await redis.enqueue_job("send_email", user_id)
# FastWorker
await client.delay("send_email", user_id)

Both are await-able and non-blocking — exactly what you want inside a FastAPI handler. The difference is the pool: ARQ’s is a Redis connection pool; FastWorker’s client talks to the control plane over NNG.

Retries

# ARQ — declarative retry ceiling + explicit re-raise
async def call_api(ctx, url):
    try:
        return (await httpx.get(url)).json()
    except Exception:
        raise Retry(defer=ctx["job_try"] * 5)
# FastWorker — explicit loop in the task body
@task
def call_api(url: str):
    for attempt in range(3):
        try:
            return requests.get(url, timeout=5).json()
        except Exception:
            time.sleep(2 ** attempt)
    raise

ARQ’s Retry with defer and max_tries is a genuinely nice built-in. FastWorker keeps retries explicit in the task body.

Where ARQ wins

  • Durable persistence. Jobs live in Redis, so they survive a worker restart. FastWorker’s queue is in-memory and is lost if the control plane dies.
  • Built-in cron. ARQ ships first-class cron jobs and deferred execution. FastWorker expects you to use cron or a Kubernetes CronJob.
  • Retry semantics. max_tries plus the Retry exception give you a clean, declarative retry model.
  • Redis-backed results. Results persist in Redis with a configurable TTL, readable from anywhere that can reach Redis.

Where FastWorker wins

  • No broker. ARQ requires Redis; FastWorker runs on pure Python processes with nothing external to deploy, secure, or back up.
  • Built-in dashboard. ARQ has no official web UI. FastWorker’s dashboard starts with the control plane at :8080.
  • Priority levels. FastWorker has four built-in priority levels with least-loaded dispatch. ARQ has no built-in priority mechanism.
  • Automatic worker discovery. Subworkers auto-register with the control plane; ARQ workers are configured statically.
  • Smaller operational surface. No Redis to run in dev or prod, no RedisSettings to wire up.

Which to choose

Choose ARQ if:

  • You want async tasks and durable, Redis-backed persistence
  • You need built-in cron / scheduled jobs
  • You want declarative retries with max_tries
  • You already run Redis and are happy to keep it

Choose FastWorker if:

  • You want an async-native queue with no broker at all
  • You want a dashboard and priority levels that ship in the box
  • You want workers that auto-register instead of static config
  • You can live without durable persistence and built-in cron

Both are strong async choices for FastAPI. The real question is whether Redis earns its place in your stack.

Next steps

Frequently asked questions

What is ARQ?

ARQ is an asyncio-native job queue for Python, written by Samuel Colvin (the author of Pydantic). It's built around async/await from the ground up and uses Redis as its broker and result store. It's a popular pairing with FastAPI because both are async-first.

Does ARQ need Redis?

Yes. ARQ stores jobs, results, and scheduling state in Redis — Redis is a hard dependency. FastWorker is brokerless: it uses NNG for direct Python-to-Python messaging and needs no Redis, RabbitMQ, or database.

If both are async, why choose one over the other?

Both give you an async-native API that fits FastAPI cleanly. Choose ARQ when you want Redis-backed durability and built-in cron scheduling and already run Redis. Choose FastWorker when you want to avoid running a broker at all and want the dashboard, priority levels, and worker auto-discovery in the box.