I have released a compatible simulator for testing SQS locally, lqs . The name is an abbreviation of local queue service.
It provides an HTTP API that can be accessed from the AWS SDK, allowing you to test locally operations such as sending and receiving messages for both Standard and FIFO queues, visibility timeouts, and moving messages to a DLQ.
Since all the issues I had prepared have been resolved, I decided to make it public.
Wanted to Test SQS Locally
When developing an application that uses SQS, you often want to run everything locally up to the point where a worker receives a message sent to the queue.
It’s not enough to just verify that sending and receiving succeed; you also want to check what happens if a worker crashes during processing, or what occurs when the same message is re‑sent. Setting up an SQS queue on AWS each time was a bit of a hassle.
There are already tools for using SQS locally, such as LocalStack and ElasticMQ .
LocalStack is an environment that lets you run multiple AWS services—including SQS—locally, while ElasticMQ is a message queue with an SQS‑compatible interface. When you look for SQS emulators, these are the ones that come up.
I decided to build something myself that I could use for developing and testing SQS‑based workflows, and that’s how lqs was born.
HTTP API Accessible from the AWS SDK
lqs runs locally as an HTTP server. In your application, you point the SQS client’s endpoint to lqs.
The default endpoint is http://127.0.0.1:9324.
You can perform operations such as creating queues, sending, receiving, and deleting messages via the HTTP API. It supports both JSON format and the SQS Query format.
We have also prepared integration tests that connect from the AWS SDK for Go v2 to the server. In addition to sending and receiving messages, we verify FIFO behavior, DLQ handling, and error responses.
Since signature verification is not performed, the credentials you configure in the SDK can be dummy values intended for local use. In the tests, the access key and secret are set to test, and the region is set to us-east-1.
What You Can Do Now
The main supported features are as follows.
| Feature | What You Can Test |
|---|---|
| Standard / FIFO | Sending and receiving messages, ordering control within FIFO groups |
| Visibility timeout | Temporary invisibility after receipt, re‑receiving after the timeout expires, changing the timeout |
| FIFO deduplication | Preventing duplicate sends using message IDs or bodies |
| Long polling | Waiting for messages to arrive before receiving them |
| DLQ | Moving messages that have exceeded the maximum receive count |
| Delay and retention period | Handling messages that are not delivered immediately and those that expire |
| Batch operations | Sending, deleting, and changing visibility for multiple messages at once |
| Attributes & queue management | Message attributes, queue configuration, listing, tagging, purging, etc. |
Queue configurations and messages are stored locally, so you can restart the service and continue from where you left off.
Redelivery and DLQ When Processing Fails
A message is not deleted simply by being received from the queue; it is deleted only after processing completes.
If a worker crashes before deletion, the message becomes visible again once its visibility timeout expires, allowing it to be received again. This behavior can also be tested locally.
Messages that are repeatedly unable to be processed are moved to the DLQ after they reach the maximum receive count and become eligible for re‑reception.
Since you may not notice such failure paths when only testing successful messages, being able to try them out was exactly what we wanted.
FIFO Ordering and Deduplication
In FIFO mode, you can maintain the order of messages within the same group while processing messages from other groups in parallel.
For example, if you separate messages by order ID, you can process events for a single order sequentially while still handling messages from other orders.
Deduplication is also supported for sends: if a message with the same ID is re‑sent within the five‑minute deduplication window, a new message will not be added. You can also configure deduplication based on the message body.
This is intended for verifying how your application behaves when a re‑send occurs or when a preceding operation is halted.
Running Locally
If you have a build environment that supports Rust/Cargo edition 2024, you can clone the repository and start it.
git clone https://github.com/ieee0824/lqs.git
cd lqs
cargo run --locked
From another terminal, verify that it has started.
curl --fail -i http://127.0.0.1:9324/health
If you receive an HTTP 200 response, the service is running. Then change the endpoint setting in your AWS SDK to http://127.0.0.1:9324 and connect.
The Queue URL used for sending and receiving should be the value returned by CreateQueue or GetQueueUrl. Both the SDK’s endpoint and the target queue URL should be set to the local ones.
The listening address and storage location can be changed via environment variables.
| Environment variable | Purpose | Default value |
|---|---|---|
LQS_BIND_ADDR | Listening address | 127.0.0.1:9324 |
LQS_BASE_URL | Base URL for returned queue URLs | Generated from the listening address |
LQS_DATABASE_PATH | File used to persist state | lqs.sqlite |
Example SDK configurations are also posted in Go SDK integration test .
Can Be Used Directly from Rust Tests
You can also use it as a Rust library without starting the HTTP server. For each test you can create an in‑memory database and try sending, receiving, and deleting messages.
Because you can specify timestamps, you can verify visibility timeouts and deduplication expiration without actually waiting several minutes. However, the long‑polling wait behavior is handled by the HTTP side; the library’s synchronous receive does not block.
Examples of direct usage are posted in Rust library section in the README .
Differences from Real SQS
Although the issues I had prepared have been resolved, this does not mean it fully replicates SQS.
It does not reproduce region‑specific throughput limits or the behavior of a distributed environment. Some timing aspects, such as queue configuration changes and message count updates, differ from those of actual SQS.
DLQ support is implemented, but the asynchronous “move‑task” style operations that re‑inject messages via the HTTP API are not yet available.
Also, while some policy settings and SSE/KMS configurations can be handled, the tool does not fully replicate AWS IAM or actual encryption. Messages are stored in plain text, and authentication is not equivalent to that of a production service, so it is intended for local development and testing only.
Queues without a policy do not restrict access. Do not expose the server to the internet or to untrusted clients.
The scope of support and detailed differences are summarized in SQS compatibility specifications and limitations .
Now you can locally test sending messages, receiving them, and reprocessing them when a failure occurs. I plan to continue using it in practice and address any missing features as they arise.