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:
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.

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 },
});
async def functions that receive a typed Message, with payloads validated as Pydantic models.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.
DramatiqDramatiq 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)
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 (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)
RQ is easy to understand and debug, but it only supports Redis and its workers are synchronous.

Huey is a small task queue that stores tasks in Redis, SQLite or memory and has periodic tasks built in.
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 is a task queue built around asyncio, with plugins that add brokers for Redis, RabbitMQ, NATS, Kafka and others.
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 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.
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.
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.
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.
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.
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.
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 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.
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.