What Is Amazon SQS? Message Queues in Plain English

Logeshwaran.C

Amazon SQS is a waiting line for work: one program drops a message in the queue, another picks it up when it is ready, and neither has to be awake, fast, or even running at the same moment. Simple — until you meet the rule that breaks almost every beginner's first queue: reading a message does not remove it. When your code receives a message, SQS only hides it, for 30 seconds by default. If your code does not come back and explicitly delete that message after processing it, it reappears in the queue — and gets processed again. And again. Every "my queue keeps redelivering the same job" mystery, and a good share of "why did my app charge this customer twice" disasters, is this one rule, unread. This post is stop 10 of our learn-AWS-free series — SQS in plain English, the rule above, and the handful of settings that decide whether a queue quietly saves your app or quietly duplicates its work.

⚡ Quick Answer

SQS = a managed queue: producers send messages, consumers receive and process them at their own pace. Nothing is pushed; consumers ask.

The golden rule: receive → process → delete. Skip the delete and the message returns after the visibility timeout.

Free: the first 1 million requests every month cost nothing, permanently.

The details — Standard vs FIFO, the duplicate-delivery truth, and the polling setting that wastes money — are all below.

Jake got the panicked call on a Sunday: a student he mentors had built an order system for a family friend's shop — API Gateway in front, Lambda to charge the card, SQS in between, straight from this series — and a customer had been charged twice for one order. The student swore his code had no loop, and he was right. His card-charging step sometimes took 40 seconds on a slow payment API. His queue's visibility timeout was the default 30. At second 31, SQS concluded the first worker had died, put the order back in the queue, and a second worker charged it again — while the first was still mid-charge. Two lessons for the price of one refund: the visibility timeout must outlast your slowest processing time, and a payment step must be written so that running it twice cannot charge twice. Neither lesson appears in the error log, because nothing errored. Everything worked exactly as configured.

Ethan: "It's the ticket rail in a diner kitchen. The waiter clips the order and walks away — that's the send. A cook grabs the ticket and flips it face-down so nobody else cooks it — that's the visibility timeout. When the plate goes out, the cook tears the ticket up — that's the delete. Now: a cook who flips a ticket, cooks the burger, and forgets to tear the ticket up? Thirty seconds later it flips back face-up, and the next cook makes the same burger. The rail did nothing wrong. The rail never does."

What SQS actually is, and why apps need a waiting line

Without a queue, your programs talk like a phone call: the order API calls the payment code and waits, the customer's browser waits on both, and if the payment step is slow or down, everything upstream is slow or down with it. A queue turns the phone call into a note under the door. The API writes "charge order #831" into SQS — that takes milliseconds — and tells the customer "order received." The payment worker reads notes off the queue at whatever pace it can manage. Traffic spike? The queue gets longer; nothing crashes. Worker down for ten minutes? The notes wait. This is decoupling, and it is the single idea behind every queue ever built: the part of your app that accepts work should never be held hostage by the part that does the work.

SQS is AWS's managed version of that: no servers to run, effectively unlimited queue length, messages up to 256 KB each, stored redundantly across machines. It was actually the very first AWS service opened to the public — older than S3 — which tells you how fundamental the waiting-line idea is. One boundary to hold onto from day one: a queue is a conveyor belt, not a shelf. Messages live at most 14 days (4 by default), and each message is meant to be processed and deleted, not kept. Anything you want to store belongs in S3 or DynamoDB; the queue just moves the instruction.

The rule that breaks beginners: receiving is not removing

Here is why SQS hides messages instead of deleting them on read. If receiving a message removed it, a worker that crashed mid-job would take the message to its grave — the order would simply never be processed, and nobody would know. So SQS makes a cautious bargain: when a worker receives a message, the message stays in the queue but turns invisible for the visibility timeout (default 30 seconds). If the worker finishes and calls delete within that window, the message is gone forever — the happy path. If the worker crashes, hangs, or just takes too long, the timeout expires and the message becomes visible again for another worker to pick up. Crash-proof by design: a died-mid-job message is automatically retried.

The full life of a message, concretely:

  1. Send. A producer writes the message to the queue. It is stored redundantly and the producer moves on.
  2. Wait. The message sits in the queue — seconds or days — until a consumer asks for work.
  3. Receive. A consumer polls the queue and gets the message, plus a receipt handle. The message turns invisible; the visibility clock starts.
  4. Process. The consumer does the actual work — charge the card, resize the image, send the email.
  5. Delete. The consumer calls delete with the receipt handle. Only NOW is the message truly gone. Skip this step and go back to step 2 — that is the loop that double-charged Jake's student's customer.

Standard vs FIFO: the two queues, honestly compared

StandardFIFO
DeliveryAt-least-once — rare duplicates are documented, intended behaviorExactly-once processing within the deduplication window
OrderBest effort — usually in order, no promiseStrict, guaranteed, per message group
ThroughputEffectively unlimited300 operations/second — 3,000 with batching, more in high-throughput mode
Namingany-namemust end in .fifo
Price (after free tier)~$0.40 per million requests~$0.50 per million requests

The honest guidance: default to Standard, and reach for FIFO only when order or exactly-once genuinely matters — a ledger where debits must follow credits, inventory counts, anything where "event 2 before event 1" corrupts state. Most workloads — emails, image processing, notifications, log handling — survive reordering and duplicates just fine if you follow the next section, and Standard's unlimited throughput means one less limit to think about.

Duplicates are a feature: write code that is safe to repeat

Read a Standard queue's fine print and it says something most people refuse to believe until it bites them: occasionally, the same message is delivered more than once. Not a bug — the price of a distributed system that never loses a message is one that sometimes repeats it. Add the visibility-timeout mechanics from earlier and the professional conclusion is unavoidable: your processing code must be idempotent — a fancy word for "running it twice has the same effect as running it once." The standard trick: give every job an ID, record the ID when the job completes (a DynamoDB table is perfect), and make the worker's first move a check — "have I already done #831?" — skipping if so. Twenty lines of code, and duplicate delivery goes from disaster to non-event. This is the second lesson from Jake's Sunday call: the visibility timeout fix stopped most duplicates, but only the idempotency check made a double-charge impossible.

Getting the visibility timeout right

The rule of thumb: set the visibility timeout comfortably above your slowest realistic processing time — not your average. A job that usually takes 5 seconds but can take 40 on a bad day needs a timeout of a minute or more, because the cost of too-short is duplicate processing while the cost of too-long is merely a slower retry after a genuine crash. The ceiling is 12 hours, and long-running workers can extend the clock mid-job if a task turns out slower than expected. If you take one number away from this post, take this pairing: default timeout 30 seconds, and any job that can ever exceed it is a duplicate waiting to happen.

Dead-letter queues: where poison messages go to be studied

Picture a malformed message — a corrupt order, a JSON typo — that makes your worker crash every time it is received. Crash, timeout, reappear, crash: a poison message, looping forever, burning requests and drowning your logs. The cure is a dead-letter queue: a second, ordinary queue you attach to the first with a rule — "if any message gets received more than N times (the maxReceiveCount, commonly 3 to 5) without being deleted, move it over there." The poison message ends up quarantined where you can read it at leisure and fix the bug it exposed, while the main queue flows on. There is no reason to run a production queue without one, and the setup genuinely takes two minutes:

  1. Create a second, plain queue — same type as the main one (a FIFO queue needs a FIFO DLQ), named something honest like orders-dlq. No special settings; a DLQ is just a queue.
  2. On the MAIN queue, open its settings, enable the dead-letter queue option, and point it at the queue from step 1 with a maxReceiveCount of 3 to 5.
  3. Watch it. Set a CloudWatch alarm on the DLQ's message count — a message landing there means a bug you have not met yet. An unwatched DLQ is just a slower way to lose messages, 14 days later.

That third step is the one people skip, and it is the difference between "one bad message stalled the shop all night" and "one bad message is sitting in a drawer for Monday — and Monday-you knows it is there."

What it costs — and the empty-receive trap

SQS pricing is beautifully boring: the first 1 million requests per month are free, permanently (this is always-free, not a 12-month trial), and after that roughly $0.40 per million for Standard in the main US regions. A hobby project will struggle to pay a cent — unless it steps on the one rake hidden in the grass: empty receives are billed requests too. A worker that checks the queue once per second, around the clock, fires about 2.6 million requests a month — past the free tier — even if the queue never contains a single message. Asking "anything for me?" costs the same as receiving actual work.

The fix is one setting: long polling. Set the receive call's wait time to its maximum of 20 seconds, and instead of "no" and hanging up 86,000 times a day, SQS keeps the line open and answers the moment a message arrives (or after 20 quiet seconds). Same responsiveness, roughly one-twentieth the requests, and as a bonus it reduces the false-empty responses Standard queues sometimes return. There is close to no downside; AWS themselves recommend it as the default. It will not surprise regular readers of this series that the polite default and the cheap setting are, once again, not the same thing — our AWS billing guide collects the whole family of these traps, alarms included.

SQS + Lambda: the pairing you will actually use

You may never write a polling loop at all. Connect a queue to a Lambda function as an event source, and AWS runs the polling for you: it watches the queue, invokes your function with batches of messages (up to 10 by default), scales the number of parallel invocations with queue depth, and — a genuine kindness — deletes the messages automatically when your function finishes without error. Throw an error and the batch stays for retry, eventually flowing to your DLQ. The result is the little architecture this series has been quietly assembling stop by stop: API Gateway answers the internet in milliseconds, drops work into SQS, Lambda chews through the queue at its own pace, DynamoDB remembers the results — and every piece of it sits inside the free tier while you learn. Two honest cautions: your function's timeout and the queue's visibility timeout must be set together (the visibility window should exceed the function timeout — same rake, new handle), and the idempotency section above still applies, because "Lambda manages the deletes" does not repeal at-least-once delivery.

What SQS is not

Three boundaries save a lot of misarchitecture. SQS is not a broadcaster: one message goes to one consumer, and when five different systems all need to hear "order placed," you want SQS's sibling SNS — the subject of the next stop in this series, and the two are better together than either alone. It is not a stream: messages are deleted as they are consumed, so if you need to replay yesterday's events or have several readers walk the same history, that is Kinesis territory, not a queue. And it is not a storage service: 256 KB per message, 14 days maximum, no browsing. The classic pattern for big payloads is to put the actual file in S3 and send a message that just says where it is — the queue carries the claim ticket, not the luggage.

The numbers that matter, on one screen

SettingDefaultRange / limitThe advice
Visibility timeout30 seconds0 seconds – 12 hoursAbove your SLOWEST job, not your average
Message retention4 days60 seconds – 14 daysRaise it — retention is free insurance
Message size256 KBBigger payloads: file in S3, pointer in the message
Long polling wait0 (short polling)0 – 20 secondsSet 20. Almost always. It is just cheaper
DLQ maxReceiveCountnone (no DLQ)1 – 1,000Attach a DLQ, count 3–5, before production
Free tier1M requests/monthalways freeLong polling keeps you comfortably inside it

FAQ — Amazon SQS in plain English

What is Amazon SQS in one sentence?

A managed waiting line between programs: one side drops off work as messages, the other side picks work up when ready, and neither has to be running, fast, or awake at the same time.

Why does my queue keep delivering the same message again?

Almost always: your code received and processed it but never called delete, or processing took longer than the visibility timeout. Receive-process-delete is the full ritual; the delete is not optional.

Does SQS push messages to my application?

No. Consumers poll the queue and ask for work — or you attach the queue to Lambda and AWS does the polling for you. If you want push-style fan-out to many listeners, that is SNS, the next stop in this series.

What is the visibility timeout?

The window (default 30 seconds) during which a received message is hidden from other consumers. Finish and delete within it and the message is gone; miss it and the message reappears for someone else to process.

Standard or FIFO — which should a beginner use?

Standard, unless strict ordering or exactly-once genuinely matters to your data (ledgers, inventory). Standard is unlimited-throughput and slightly cheaper; FIFO trades speed for guarantees.

Can SQS really deliver a message twice?

Yes — Standard queues are at-least-once by design, and duplicates are documented behavior. Write idempotent workers (check a done-list before acting) and duplicates become harmless.

What does "idempotent" mean?

Safe to run twice: doing the job a second time changes nothing. Record each job's ID on completion and skip already-seen IDs — twenty lines that turn duplicate delivery into a non-event.

What is a dead-letter queue?

A quarantine queue for messages that keep failing. After maxReceiveCount failed attempts, the message moves there instead of looping forever — your main queue flows on, you debug the bad message on Monday.

How much does SQS cost?

First 1 million requests each month: free, permanently. After that, roughly $0.40 per million for Standard ($0.50 FIFO) in the main US regions as of this writing. For hobby projects, effectively $0 — if you use long polling.

What are empty receives and why do they cost money?

Every "anything for me?" poll is a billed request even when the queue is empty. A once-per-second poller fires ~2.6M requests a month doing nothing. Long polling (wait time 20) cuts that ~20x.

How big can a message be?

256 KB. For anything larger, store the payload in S3 and send a message containing its location — the claim-ticket pattern. The queue moves instructions, not luggage.

How long do messages stay in a queue?

Until processed and deleted, up to the retention limit: 4 days by default, configurable from 60 seconds to 14 days. Set it high — longer retention costs nothing and buys recovery time after an outage.

Can SQS trigger a Lambda function?

Yes — attach the queue as an event source and AWS polls, batches, invokes, scales, and deletes on success automatically. Keep the queue's visibility timeout longer than the function's timeout.

Is SQS the same as Kafka or Kinesis?

No. Queues hand each message to one consumer and forget it; streams keep an ordered, replayable history many readers can walk independently. Background jobs: SQS. Event replay and analytics pipelines: Kinesis.

Do I need to create servers for SQS?

No. It is fully managed — you create a queue (a name and a few settings) and AWS handles storage, redundancy, and scale. There is nothing to patch, size, or reboot.

What comes after SQS in this series?

Amazon SNS — the broadcaster. One event, many listeners, and the classic fan-out pattern where SNS and SQS work as a team. Promised here, delivered next, linked from the series hub the day it lands.

A note on prices and promises. Written August 21, 2026; the numbers here — the permanent 1M-requests-per-month free tier, ~$0.40/$0.50 per million after it, the 256 KB / 14-day / 12-hour limits — were checked against AWS's own pages on that date, and queue prices in particular have been boringly stable for years. This is stop 10 of the learn-AWS-free series: the API Gateway post promised SQS, and here it is; this post promises Amazon SNS next, and the series hub will link it the day it lands. If a queue burned you in a way this page did not cover, tell me through the contact page — the double-charge story above came from a reader too.

Related