What is a queue?
A queue holds items in order: the first item added is the first one taken out (FIFO, or First In First Out), like a line of people waiting for service. In software, a queue lets one part of your system hand work to another so the producer doesn’t have to wait for the work to finish. Redis works well for queues. Its list and sorted set types map onto queue operations, and operations are atomic, so multiple producers and consumers can share a queue without stepping on each other. With Upstash Redis you reach it over HTTP, so the same queue works from a serverless function or a long-running worker. This tutorial starts with a plain FIFO queue and builds up to a job queue that supports delays, priorities, and retries.Database Setup
Create a Redis database using the Upstash Console. If this is your first one, the Getting Started guide walks through creating a database and finding its credentials. Then copy the REST URL and token into your.env file:
Installation
Redis.fromEnv() reads the two variables above
automatically:
A basic FIFO queue
A Redis list is all you need for a simple queue. You push items onto one end and pop them off the other. The convention is to enqueue on the left withLPUSH and
dequeue on the right with RPOP, so the oldest item is always the next one out.
| Operation | Command | Description |
|---|---|---|
| Enqueue | LPUSH queue task | Add a task to the head of the list |
| Dequeue | RPOP queue | Remove and return the oldest task |
| Peek | LRANGE queue -1 -1 | Look at the next task without removing it |
| Length | LLEN queue | How many tasks are waiting |
LRANGE. This is
useful for monitoring or for deciding whether to process:
A reliable queue
To avoid losing tasks, don’t fully remove a task until it has been processed. Redis’sLMOVE command (the modern replacement for RPOPLPUSH) atomically moves a
task from the main queue to a “processing” list in a single step:
processing while it is being worked on, a crashed
consumer doesn’t cause data loss. A periodic recovery job can scan processing for
tasks that have been stuck too long and move them back to tasks.
Consuming the queue
A consumer reads from the queue in a loop. With a TCP Redis client you might use a blocking pop (BRPOP) to sleep until a task arrives, but @upstash/redis talks to
Redis over HTTP, so it doesn’t expose blocking commands. Instead, poll with RPOP
and back off with a short sleep when the queue is empty:
Polling works well for a long-running worker, but in short-lived serverless
functions you usually don’t want to run a loop at all. For event-driven delivery,
consider Upstash QStash, which pushes messages to your
HTTP endpoint instead of making you poll. We come back to this
at the end.
Going further: a delayed, prioritized job queue with retries
Plain lists are FIFO and immediate. Real job systems usually need a few more things:- Delays: run a job later, like a reminder in an hour, instead of right away.
- Priorities: let urgent jobs go ahead of routine ones.
- Retries: when a job keeps failing, retry it a few times and then set it aside rather than retrying forever.
The schedule: score = “run at” timestamp
Claiming due jobs atomically
A worker should only pick up jobs whoserunAt is in the past, and when several
workers run at once, no two should claim the same job. A Lua script handles this: it
reads the due jobs and removes them in one step, so there is no race between the
read and the remove.
The worker: process, retry, or dead-letter
When a job fails, we re-schedule it with an exponential back-off delay and increment its attempt count. Once it runs out of retries, it goes to a dead-letter queue: a separate list of jobs to look at later, rather than retrying them again.Inspecting the dead-letter queue
Failed jobs live in a list, so checking on them is straightforward. You can wire this into a dashboard or an alert:Wrapping up
Redis covers queues at several levels, all in the same database:- Lists (
LPUSH/RPOP) for simple FIFO queues, plusLMOVEfor reliability andBRPOPfor blocking consumers. - Sorted sets (
ZADD/ZRANGEBYSCORE) for delayed and prioritized scheduling. - Lua scripts (
EVAL) to claim work atomically across many concurrent workers.