Oct 23, 20269 min read

Celery Alternatives for Python Background Jobs

Task queues, workflow engines, and Pub/Sub options for running background work in Python, compared on setup, local development, and what you operate in production.

Celery is the task queue most Python teams reach for when they need background jobs, and a typical setup has a Redis or RabbitMQ broker, a result backend, worker processes and a beat scheduler, all tied together with a fair amount of configuration. It starts to hurt when testing one task locally means starting all of those pieces, or when a job dies on a visibility timeout or a prefetch setting that nobody on the team fully understands. This article compares the options teams usually look at instead, which are Encore, Dramatiq, RQ, Huey, Taskiq and Temporal.

Reasons teams look for Celery alternatives:

  • Running Redis or RabbitMQ, the workers and beat locally makes the feedback loop slow.
  • The configuration is large, and failure modes around visibility timeouts, prefetch and acks are hard to reason about.
  • asyncio support is not native, which is awkward in FastAPI codebases.
  • Long-running jobs with several steps need state that survives a crash, and plain tasks do not keep it.
  • Someone still has to provision and operate the broker in production.

Celery alternatives at a glance

AlternativeApproachBest fit
EncorePub/Sub topics, subscriptions, and cron jobs declared in code, with infrastructure provisioned for youTeams that want background work with local infra and production wiring handled
DramatiqTask queue on RabbitMQ or Redis with retries by defaultTeams that want a Celery-style queue with simpler defaults
RQSimple job queue backed by RedisSmall and medium apps that already run Redis
HueyLightweight task queue with Redis, SQLite, or in-memory storageSmall projects and single-server apps
Taskiqasyncio-native task queue with pluggable brokersFastAPI and other async codebases
TemporalDurable workflow engine with a Python SDKLong-running, multi-step processes that must survive failures
Compare features across all six alternatives
CapabilityEncoreDramatiqRQHueyTaskiqTemporal
ModelPub/Sub messages and cron jobsActors (tasks) on a queueJobs on a queueTasks on a queueTasks on a brokerWorkflows and activities
Broker / backendRuns locally with encore run; SNS/SQS on AWS, Pub/Sub on GCPRabbitMQ or RedisRedisRedis, SQLite, or memoryRedis, RabbitMQ, NATS, Kafka, and others via pluginsTemporal server (Cloud or self-hosted)
asyncioNative (async def subscription handlers in Python)Sync actors; async support varies by versionSyncSyncNativeNative in the Python SDK
RetriesRetry policy per subscription, dead-letter queue on AWSRetries with backoff by defaultConfigurable retriesConfigurable retriesVia middlewareRetry policies per activity
SchedulingCron jobs declared in codeDelays per message; periodic jobs via a separate schedulerDelayed jobs and a schedulerDelayed and periodic tasksScheduler componentTimers and schedules
Local developmentencore run starts Pub/Sub, databases, and servicesRun a broker and workersRun Redis and workersRun a worker (SQLite or memory needs no server)Run a broker and workerstemporal server start-dev plus workers
Production opsBroker provisioned in your AWS/GCP accountYou run the broker and workersYou run Redis and workersYou run storage and workersYou run the broker and workersTemporal Cloud or a self-hosted cluster

Encore: background work with the infrastructure included

Encore

Encore combines the queue, the worker and the scheduler that a Celery setup runs separately into declarations in application code, where a Pub/Sub topic and its subscription take over from the task queue and the worker while a cron job takes over from beat, so encore run starts all of it locally and a deploy creates SNS/SQS in your own AWS account or Pub/Sub in your GCP account from the same declarations. The declarations live in your Python code next to the FastAPI or Django app that publishes the events, and the same model is available in TypeScript and Go.

Application code passes through Encore and infrastructure guardrails to resources provisioned in your own AWS or GCP account.
Encore provisions the infrastructure your application needs in your own AWS or GCP account.

A Celery task that is queued with .delay() maps to a publish on a topic, and the task body moves into the handler of a subscription to that topic.

# Celery @app.task(bind=True, max_retries=5) def send_welcome_email(self, user_id, email): ... send_welcome_email.delay(user.id, user.email)

In Encore, written in Python, the publish replaces .delay() and Encore retries the subscription handler when it raises:

# Encore (Python) from encore import pubsub from pydantic import BaseModel class Signup(BaseModel): user_id: int email: str signups = pubsub.Topic[Signup]("signups") # Replaces send_welcome_email.delay(user.id, user.email) async def on_signup(user) -> None: await signups.publish(Signup(user_id=user.id, email=user.email)) # Replaces the Celery worker running the task body. async def send_welcome_email(msg: pubsub.Message[Signup]) -> None: # Retried with backoff if this raises. await send_email(msg.data.email) welcome = pubsub.Subscription[Signup]( signups, "send-welcome-email", handler=send_welcome_email, max_retries=5 )

The TypeScript version follows the same shape:

// Encore (TypeScript) import { Topic, Subscription } from "encore.dev/pubsub"; interface Signup { userID: number; email: string; } export const signups = new Topic<Signup>("signups", { deliveryGuarantee: "at-least-once", }); // Replaces send_welcome_email.delay(user.id, user.email) export async function onSignup(user: { id: number; email: string }) { await signups.publish({ userID: user.id, email: user.email }); } // Replaces the Celery worker running the task body. const _ = new Subscription(signups, "send-welcome-email", { handler: async (event) => { // Retried with backoff if this throws. }, retryPolicy: { maxRetries: 5 }, });

Key features

  • Subscriptions have a configurable retry policy, an ack deadline and a concurrency limit, and on AWS messages that run out of retries go to a dead-letter queue.
  • Several subscriptions can listen to the same topic, so one event can trigger more than one job.
  • Cron jobs are declared in code and call an API endpoint on a schedule, and Postgres databases, buckets and secrets are declared in the same way.
  • Python handlers are plain async def functions that receive a typed Message, with payloads validated as Pydantic models.

Good to know

Encore replaces the task queue rather than wrapping it, so tasks that return results to a caller or chain together through Celery canvases need to be reshaped into events and subscriptions, and cron jobs do not run on their schedule locally, which means developers call the endpoint directly to test them. Python support is in beta, sign up for access.

Try Encore →

Dramatiq: a Celery-style queue with simpler defaults

Dramatiq

Dramatiq is a Python task queue that uses RabbitMQ or Redis as its broker, where tasks are called actors and retries with exponential backoff are on by default.

import dramatiq @dramatiq.actor(max_retries=5) def send_welcome_email(user_id: int) -> None: ... send_welcome_email.send(42)

Key features

  • It supports RabbitMQ and Redis brokers.
  • Retries, time limits and delayed messages work without extra setup.
  • Middleware adds features such as rate limiting and result storage.

Good to know

Dramatiq is the closest swap for Celery users who want less configuration, though it is licensed under LGPL-3.0 and periodic jobs need a separate scheduler such as APScheduler.

RQ: a simple job queue on Redis

RQ

RQ (Redis Queue) enqueues plain Python function calls in Redis and runs them in worker processes.

from redis import Redis from rq import Queue from app.emails import send_welcome_email q = Queue(connection=Redis()) q.enqueue(send_welcome_email, 42)

Key features

  • Any importable function can be enqueued without a task decorator.
  • It supports retries, job dependencies and delayed jobs.
  • A web dashboard is available as a separate package.

Good to know

RQ is easy to understand and debug, but it only supports Redis and its workers are synchronous.

Huey: a lightweight queue for small projects

Huey

Huey is a small task queue that stores tasks in Redis, SQLite or memory and has periodic tasks built in.

Key features

  • Tasks can be stored in Redis, SQLite or memory.
  • Periodic tasks use crontab-style schedules.
  • It handles retries, delays and locking, and integrates with Django.

Good to know

With the SQLite backend, background jobs run without an extra server, which suits single-server apps, but Huey is not designed for large distributed worker fleets.

Taskiq: an async-native task queue

Taskiq

Taskiq is a task queue built around asyncio, with plugins that add brokers for Redis, RabbitMQ, NATS, Kafka and others.

Key features

  • Tasks are native async functions.
  • The FastAPI integration supports dependency injection.
  • Brokers and result backends are pluggable, and a scheduler is included.

Good to know

Taskiq is a good fit for FastAPI teams and the closest replacement for arq, although teams still run the broker and the workers themselves.

Temporal: durable workflows instead of tasks

Temporal

Temporal runs workflows as code, and because the server persists the progress of each step, a workflow can wait for long periods, survive a worker crash and resume where it left off.

~/app
# Start a local Temporal server for development$ temporal server start-dev

Key features

  • Workflows and activities are durable and have retry policies.
  • Timers, signals and schedules are built in.
  • The Python SDK supports async.

Good to know

Temporal solves a bigger problem than a task queue and brings more concepts with it, such as deterministic workflow code, and in production teams either use Temporal Cloud or operate the server and its database themselves.

How to choose

  • Encore if you want background work declared in Python, TypeScript or Go code, with the broker running locally and provisioned in your AWS or GCP account.
  • Dramatiq for a Celery-style queue with less configuration.
  • RQ for simple jobs on Redis you already run.
  • Huey for small apps, especially with the SQLite backend.
  • Taskiq for async FastAPI codebases.
  • Temporal for long-running, multi-step processes.

When most Celery tasks react to an event or run on a schedule, Pub/Sub subscriptions and cron jobs declared in code can cover them without a separate broker to operate. Python support in Encore is in beta, sign up for access to move your Python background jobs over.

Frequently asked questions

What is the best alternative to Celery?

It depends on what you need. RQ and Huey are the simplest drop-in task queues, Dramatiq is a reliable Celery-style queue with fewer moving parts, Taskiq is built for asyncio and FastAPI, Temporal is for long-running workflows that must survive failures, and Encore gives you Pub/Sub and cron jobs declared in Python, TypeScript or Go code with the infrastructure provisioned for you.

What should I use for background tasks with FastAPI?

FastAPI's built-in BackgroundTasks is fine for short work that can be lost if the process restarts. For work that needs retries and has to survive restarts, use an async-friendly queue like Taskiq, or Temporal for multi-step workflows.

Is arq still maintained?

arq's maintainers have put it in maintenance-only mode, so it is not a good choice for new projects. Taskiq is the closest async-native alternative.

Do I need Redis or RabbitMQ for background jobs in Python?

Most task queues need a broker. RQ needs Redis, Dramatiq uses RabbitMQ or Redis, Huey can use Redis, SQLite, or memory, and Celery supports several. Encore declares Pub/Sub in Python, TypeScript or Go code, runs it locally with encore run and provisions SNS/SQS on AWS or Google Pub/Sub on GCP, so there is no broker to set up separately.

Celery vs Temporal: which should I use?

Celery runs individual tasks with retries. Temporal runs durable workflows where each step's progress is persisted, so a workflow can wait for days, survive crashes, and resume. Use a task queue for fire-and-forget jobs and Temporal when the job is really a multi-step process.

Can I schedule periodic jobs without Celery beat?

Yes. Huey has periodic tasks, RQ has a scheduler, Dramatiq is commonly paired with APScheduler, Temporal has schedules, and Encore has cron jobs declared in Python, TypeScript or Go code that call an API endpoint on a schedule.

Deploy to your cloud on AWS and Google Cloud
without managing infrastructure

Encore lets developers and agents define application infrastructure directly in code, then automatically provisions it from local development to production in your AWS or GCP account.

~/orders — claude
Claude Code
Claude Code v2.1.180Opus 5.5~/orders