CeleryRedisLLM PipelinesBackend

Celery and Redis for LLM Jobs: Settings That Matter

Which Celery settings decide whether slow LLM tasks run once or twice: late acks, prefetch, time limits and the Redis visibility timeout, with documented defaults.

In Opaeron the user waits on action items, and the narrative meeting summary is handed to a Celery worker so the request can return. LLM calls are exactly the kind of work that stresses a task queue: slow, variable in length, and expensive to repeat. A handful of settings decide whether each job runs once, twice or never.

#The defaults that surprise you

Documented Celery defaults relevant to slow tasks.
SettingDefaultWhy it matters for LLM work
task_acks_lateDisabled (ack just before execution)A worker crash mid-call loses the job
worker_prefetch_multiplier4Workers reserve messages ahead, so long jobs queue behind each other
task_time_limitNoneA hung provider call can hold a worker forever
task_soft_time_limitNoneNo chance to clean up before the hard kill
Redis visibility_timeout1 hourUnacked messages are redelivered after this
1 hourDefault Redis visibility timeout before redeliveryCelery docs
4Default worker_prefetch_multiplierCelery docs

#A reasonable baseline

celery_config.py
task_acks_late = True
worker_prefetch_multiplier = 1
task_soft_time_limit = 240   # raises SoftTimeLimitExceeded inside the task
task_time_limit = 300        # worker is killed and replaced
worker_max_tasks_per_child = 200

The soft limit raises SoftTimeLimitExceeded so the task can record a failure and release resources; the hard limit kills the worker process if it does not. The numbers above are placeholders for you to size against your own p99, not recommendations.

#Idempotency is the real fix

Late acks and the visibility timeout both trade lost jobs for possible duplicates. Duplicates are only safe if running a task twice produces the same state as once. For a summary job that means writing the result keyed by meeting id with an upsert, and checking whether the summary already exists before paying for another model call.

  • Key every write by a natural id, never by a generated one.
  • Check for existing output before calling the model.
  • Record the model, token counts and cost on the run so a duplicate is visible in the data.

#Worker crashes and the reject flag

Even with late acks, Celery acknowledges a task when its process exits abruptly unlesstask_reject_on_worker_lost is enabled, which re-queues it. The docs warn that this can cause message loops, so enable it only for tasks that are safe to repeat and that cannot be the cause of the crash.

#Scheduling far ahead

The Celery docs note that the broker is not a database, and recommend periodic tasks for work scheduled far in the future because those are not affected by the visibility timeout. Nightly batch jobs belong on the beat schedule, not on a long countdown.

Sources

  1. Celery documentation, Configuration and defaultsAccessed 2026-10-28. task_acks_late, worker_prefetch_multiplier, task_time_limit, task_soft_time_limit, task_reject_on_worker_lost, worker_max_tasks_per_child.
  2. Celery documentation, Using RedisAccessed 2026-10-28. Visibility timeout default of 1 hour and redelivery behavior for long ETA and retry tasks.

Frequently asked questions

Why did my Celery task run twice with Redis?

With the Redis broker, a message that is not acknowledged within the visibility timeout is redelivered to another worker. The Celery documentation gives the default as 1 hour, and warns that ETA, countdown or retry tasks that take longer than the timeout can be redelivered repeatedly. Make the task idempotent, and keep the timeout above your longest ETA or retry delay.

Should LLM tasks use late acknowledgement?

Often yes. By default Celery acknowledges a message just before the task runs, so a worker crash mid-task loses the job. With task_acks_late enabled the message is acknowledged after execution, so a crash leads to redelivery. That makes duplicates possible, so late acks only belong with idempotent tasks.

What does worker_prefetch_multiplier do?

It multiplies the worker's concurrency to decide how many messages it reserves ahead of time. The documented default is 4. For tasks that take seconds to minutes, such as LLM calls, a high prefetch lets one worker hoard messages while others sit idle, so setting it to 1 spreads long tasks more fairly.

Related notes

Meeting Transcript to Action Items: an LLM Pipeline

How Opaeron turns a pasted client meeting transcript into action items with owners and due dates: chunking, extraction, dedup, and a text guard.

Read

Supabase to Self-Hosted Postgres: A Production Migration

How I moved Opaeron off Supabase onto self-hosted PostgreSQL 16 with pgvector: a psycopg3 query-builder shim, losing RLS, pooling and additive migrations.

Read

Building something in this space and want another pair of hands on it?

Get in touch