Self-hosted service
softwaremill/elasticmq avatar
softwaremill/elasticmq

ElasticMQ: an in-memory SQS-compatible queue for tests and local stacks

In-memory message queue with an Amazon SQS-compatible interface. Runs stand-alone or embedded.

2,942 stars204 forksScalaApache-2.0

At a glance

What is it?
ElasticMQ is a Scala message queue that speaks the Amazon SQS query interface, runs stand-alone, in Docker or embedded, and is meant for testing and for code that has to work both inside and outside AWS. The README documents a subset of the SQS API, not all of it.
Who is it for?
Adopt ElasticMQ if your code already talks to SQS and you want the same calls to run on a laptop, in CI, or on a non-AWS host. Do not adopt it if you need the full SQS API surface, cross-region durability, or a broker with topic fan-out.
Can I use it commercially?
Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 6 days ago.
What is it written in?
Mainly Scala, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The gap ElasticMQ fills: SQS calls that must run without AWS

Most teams that use Amazon SQS end up with the same problem in two places. The first is the test suite: integration tests that hit real SQS need credentials, cost money per request, and produce queues that have to be cleaned up. The second is deployment: code written against SQS is hard to move to a host that has no AWS account. ElasticMQ addresses both by implementing the SQS query (REST) interface in memory. The README is direct about the scope: it implements a subset of the SQS query interface, which makes it useful for testing and for systems that must work both within and outside Amazon infrastructure. The intended user is a developer or platform engineer who already has SQS client code and wants it to run unchanged against a local endpoint. It is not aimed at production message brokering for teams that have no SQS dependency at all.

How the SQS semantics are reproduced: polling, visibility timeouts, dead letters

ElasticMQ follows SQS semantics rather than inventing its own. Messages are received by polling the queue. When a message is received it is blocked for a visibility timeout, and if it is not deleted during that window it becomes available for delivery again. Queues and messages can also be configured to deliver with a delay. The README is explicit that at-least-once delivery is the model, so a message can be delivered twice if a client dies after receiving and processing it but before deleting it, and clients should therefore be idempotent. That is a property of SQS itself, not a defect introduced here, and it matters for anyone tempted to treat the local instance as a stricter queue than the cloud one. The implementation is described as fully asynchronous with no blocking calls, written in Scala on an actor model, with an SQS-compatible REST layer on top. Configuration comes from Typesafe Config, and the README lists the top-level keys: node-address, rest-sqs, generate-node-address, queues, queues-storage and aws. The aws block supplies the region and accountId that appear in resource ids, defaulting to us-west-2 and 000000000000.

Running ElasticMQ with docker-compose and creating a first queue

The README calls Docker Compose the easiest way to run ElasticMQ locally. Clone the repository and start the stack:

bash
docker compose up

According to the README, this starts two services. The elasticmq service exposes port 9324 for the SQS-compatible REST API using the softwaremill/elasticmq-native image, and elasticmq-ui exposes port 3000 for a web UI. The compose file mounts examples/elasticmq.conf into the container at /opt/elasticmq.conf and persists queue data in a named volume called elasticmq-data. Opening http://localhost:3000 gives the UI, where queues can be viewed, messages sent, and queue statistics monitored in real time.

Queues can be declared ahead of time in the configuration file. The README's example creates queue1 with a ten second visibility timeout, a five second delay, a dead letters queue named queue1-dead-letters with maxReceiveCount 3, and both copyTo and moveTo targets:

code
include classpath("application.conf")

queues {
  queue1 {
    defaultVisibilityTimeout = 10 seconds
    delay = 5 seconds
    receiveMessageWait = 0 seconds
    deadLettersQueue {
      name = "queue1-dead-letters"
      maxReceiveCount = 3 // from 1 to 1000
    }
    fifo = false
    contentBasedDeduplication = false
    copyTo = "audit-queue-name"
    moveTo = "redirect-queue-name"
  }
  queue1-dead-letters { }
  audit-queue-name { }
  redirect-queue-name { }
}

Every attribute is optional except name and maxReceiveCount when a deadLettersQueue is defined. copyTo duplicates messages to another queue and moveTo redirects them, behaviour the README presents as useful mainly for integration testing. FIFO queues are created by setting fifo to true, and the README notes that the .fifo suffix is added automatically during queue creation if you do not add it yourself. The README also describes an auto-create mode that creates a queue the first time a request targets it instead of returning a NonExistentQueue error, though the configuration snippet for it is cut off in the README text.

Queue URLs, node-address and the container networking trap

Queue URLs are the part that most often breaks a first Docker setup. Responses from the REST interface include a queue URL, and by default those URLs use http://localhost:9324 as the base. The README says to set protocol, host, port and context in the node-address block to change that. There is a special value worth knowing: setting node-address.host to "*" makes any queue URL created during a request use the path of the incoming request, which the README suggests for containerized deployments. That is the setting to reach for when a client inside a Compose network receives a URL pointing at localhost and cannot resolve it. A second trap is documented explicitly: changing bind-port and bind-hostname does not affect queue URLs at all unless generate-node-address is true. When it is true, the bind host and port are used to build the node address, which is what you want if the port is assigned automatically by using port 0; the chosen port then appears in the logs.

Where ElasticMQ is the wrong tool

The most consequential limitation is stated in the README rather than hidden: ElasticMQ implements a subset of the SQS query interface. Anything your client calls that falls outside that subset will not work, and the README does not publish a complete list of what is and is not covered. ElasticMQ is also in-memory by design. Persistence is optional and the compose file persists queue data to a Docker volume, but the README does not document rollback, replication, or what happens to in-flight messages during an abrupt restart. Treat it as a development and testing dependency, not as the system of record for messages you cannot afford to lose. The strict limits mode is another sharp edge: the README lists sqs-limits with possible values relaxed and strict, and does not explain what each value changes, so a team that needs to push oversized payloads through a local instance has to discover the difference themselves. Finally, the README's own guidance is that clients should be idempotent because duplicate delivery is possible. If your consumers are not idempotent, a local queue that behaves exactly like SQS will reproduce the duplicate-delivery bug you were hoping to avoid.

ElasticMQ against RabbitMQ and against real SQS

The comparison people search for most is ElasticMQ versus RabbitMQ, and the difference is architectural rather than a matter of features. RabbitMQ is a general-purpose broker built around exchanges, bindings and routing keys, with its own AMQP protocol and its own client libraries. ElasticMQ implements the SQS query interface instead, so an existing SQS client can be pointed at it by changing an endpoint URL. That is the whole point: there is no new client library to learn and no message-routing model to translate. The cost is that you inherit SQS semantics, including polling rather than push delivery, visibility timeouts, and at-least-once delivery. Against real SQS the trade is the mirror image. Real SQS gives you a managed service with durability and the full API; ElasticMQ gives you a process you can start in CI, inspect through a UI on port 3000, and tear down without leaving queues behind. It is a substitute for the API, not for the service level.

Maintenance, licensing and upgrade cost

The repository is not archived and the last push was on 2026-09-19, days before this article, so the codebase is being touched. The most recent release listed is v1.7.1 from 2026-04-05, following v1.7.0 and a v1.7.0-rc2 in the days before it. That is a modest release cadence, and the presence of a release candidate suggests the maintainers stage larger changes rather than shipping continuously. The dependency surface is worth noting for upgrade planning: the project is written in Scala, builds with sbt, and publishes artifacts under the org.elasticmq group on Maven Central, with the README's badge pointing at elasticmq-rest-sqs_2.12. Scala version cross-building means an upgrade can pull in a Scala version decision alongside the ElasticMQ version. The licence is Apache-2.0, a permissive licence that permits commercial use and modification, with the usual obligations around notices and attribution; the repository ships LICENSE.txt and NOTICE.txt. That is a description of the licence, not legal advice, and anyone embedding the library in a distributed product should read those two files directly.

Editorial conclusion

Adopt ElasticMQ if your code already talks to SQS and you want the same calls to run on a laptop, in CI, or on a non-AWS host. Do not adopt it if you need the full SQS API surface, cross-region durability, or a broker with topic fan-out. Before wiring it into a pipeline, verify the exact subset your client uses against the strict limits mode, confirm how queue data survives a container restart through the elasticmq-data volume, and check whether you need sqs-limits set to relaxed for oversized messages.

Frequently asked questions

What is ElasticMQ?

ElasticMQ is an in-memory message queue system created by SoftwareMill. It offers an SQS-compatible REST (query) interface and can run stand-alone, via Docker, or embedded in an application.

What are some open-source alternatives to Amazon SQS?

ElasticMQ is one: the README describes it as a great SQS alternative for testing purposes and for systems that work both inside and outside Amazon infrastructure, because it implements a subset of the SQS query interface.

How do I run ElasticMQ with Docker?

The README says the easiest way is to clone the repository and run docker compose up, which starts the elasticmq service on port 9324 and the elasticmq-ui service on port 3000.

Does ElasticMQ implement all of Amazon SQS?

No. The README states that ElasticMQ implements a subset of the SQS query (REST) interface, and it does not publish a complete list of the operations that are covered.

How do I create a queue in ElasticMQ?

Queues can be declared in the configuration file under the queues block with attributes such as defaultVisibilityTimeout, delay and deadLettersQueue, or ElasticMQ can auto-create a queue the first time a request targets it instead of returning a NonExistentQueue error.

Can ElasticMQ deliver the same message twice?

Yes. The README says a message may be delivered twice, for example if a client dies after receiving and processing it but before deleting it, which is why clients of ElasticMQ and Amazon SQS should be idempotent.

Official sources

  1. License: Apache-2.0
  2. Project website
  3. README
  4. Releases
  5. softwaremill/elasticmq on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/softwaremill-elasticmq.svg)](https://hysenlabs.com/projects/softwaremill-elasticmq)