# goaws: a local SQS and SNS emulator for tests that must not touch AWS

> goaws is an MIT licensed Go implementation of the AWS SQS and SNS APIs that you run on your own machine, so integration tests can create queues, publish topics and consume messages without reaching the cloud or writing to production by mistake. It suits a team with an existing AWS SDK based test suite that needs an offline endpoint, and it is a poor fit if you need authentication, TLS, or the parts of the API it marks as unimplemented.

**Admiral-Piett/goaws** — AWS (SQS/SNS) Clone for Development testing

- Repository: https://github.com/Admiral-Piett/goaws
- Stars: 839 · Forks: 145
- Language: Go
- License: MIT
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/admiral-piett-goaws

## The failure mode it is built to prevent

The README states the problem in one long sentence, and the second half of it is the important part. goaws is a clone of the AWS SQS and SNS systems, designed to emulate SQS and SNS in a local environment so developers can test their interfaces without having to connect to the AWS Cloud and possibly incurring the expense, or even worse actually write to production topics and queues by mistake.

That last clause is the reason the project exists. The expensive mistake is not the AWS bill, it is a test run that publishes to a real SNS topic and fans out to real consumers in a real environment. A local emulator that speaks the same API removes that class of accident entirely, and because it speaks the same API, the code under test does not change.

That last point is the design constraint that explains everything else in the repository. The implementation uses the AWS SDK for Go, both the v1 and v2 lines, alongside the SQS and SNS service clients, and it routes HTTP with gorilla/mux. The service is a server that accepts AWS API calls, not a client library, and the fact that the official AWS CLI is the documented way to test it is the proof.

The audience is developers with an existing AWS integration test suite. If you are building something new, you would design the interfaces differently, and this tool is aimed at the case where the interfaces already exist and the tests are the problem.

The project is MIT licensed, the primary language is Go, the last push to the repository was on 2026-10-05, and there is no homepage set.

## The API checklist shows exactly where the gaps are

The README keeps a running list of implemented APIs, and reading it is the fastest way to judge whether goaws will work for you.

On the SQS side, the implemented calls are ListQueues, CreateQueue, GetQueueAttributes, GetQueueUrl, SendMessage, SendMessageBatch, ReceiveMessage, DeleteMessage, DeleteMessageBatch, PurgeQueue, Delete Queue, ChangeMessageVisibility, and SetQueueAttributes, with the caveat that unsupported attributes are mocked for GetQueueAttributes and only supported attributes are set by SetQueueAttributes.

The unchecked entries are the interesting ones: ChangeMessageVisibilityBatch, ListDeadLetterSourceQueues, ListQueueTags, RemovePermission, TagQueue and UntagQueue.

That list is a reasonable shape for a local test double. The missing calls are all administrative, tagging and permission related, which is the category of API that application code rarely calls in a functional test. ChangeMessageVisibilityBatch is the exception worth checking, because it is a messaging operation rather than an administrative one, and a test that extends the visibility timeout of many messages at once would fail against this emulator.

The SNS side is fuller. Implemented are ListTopics, CreateTopic, Subscribe in both raw and normal forms, ListSubscriptions, Publish, DeleteTopic, Unsubscribe, ListSubscriptionsByTopic, GetSubscriptionAttributes and SetSubscriptionAttributes.

So the honest summary is that the message path is complete in both services, and the gaps are all around the edges of the API.

## Three queue attributes, and exact match filter policies

Two capability limits are called out separately from the API lists, and both are easy to trip over.

The first is queue attributes. The supported set is VisibilityTimeout, ReceiveMessageWaitTimeSeconds and RedrivePolicy. Everything else is either ignored or returned as a mock, since the README says that at the moment you can only get all the attributes rather than individual ones, and that unsupported attributes are mocked.

The mocked part is what to watch. If your code sets a queue attribute such as a delay or a message retention setting and then reads it back to confirm, the emulator will not tell you the truth. A test that verifies configuration round-tripping will pass locally and fail in production, or worse, the other way around.

The second limit is on the SNS side, and it is in the filter policy support. The supported subscription attributes are RawMessageDelivery and FilterPolicy, and the note on FilterPolicy says only the simplest exact match filter policy is supported.

That is a bigger limitation than it first appears, because filtered subscriptions are a common way to reduce fan-out noise. A test that publishes a message which should be filtered out, and asserts that no subscriber received it, will behave differently depending on whether the filter policy is an exact match on one key. Exact match is enough for a message with a single type field and a subscription for one type. It is not enough for anything that resembles a real policy.

## YAML declares your queues, topics and subscriptions before the test runs

The configuration section describes a YAML driven setup, and this is the feature that makes the tool useful in a suite that runs repeatedly.

The implemented list covers reading a config file, a -config flag to read a specific configuration file, for example -config=myconfig.yaml, a command line argument to determine the environment to use in the config file, for example Dev, and the ability to create Queues, Topic and Subscription in the config file, with an example config file in the conf directory.

The environment argument is the piece that makes this more than a convenience. A single YAML file can hold a Dev environment and a Test environment, and the same service image can be pointed at either, so a CI job and a laptop run the same declared fixtures.

The Docker example shows how the config gets in.

```shell
docker run \
 --rm \
 --name=goaws \
 -v /path/on/host/goaws-config.yaml:/path/in/container/goaws-config.yaml \
 -p=4100:4100 \
 admiralpiett/goaws:v0.4.4-arm64 -config /path/in/container/goaws-config.yaml
```

The mount is the whole mechanism, a YAML file on the host mounted into the container and passed with the -config flag.

Debug logging is turned on with a command line flag, given as -debug, which matters when a test is failing and you need to know whether the request reached the service at all.

## Building from source, or pulling a container image

There are two ways to get it running, and the README documents both with the commands to paste.

The source route starts with a clone.

```shell
git clone git@github.com:Admiral-Piett/goaws.git
```

Then, from the GoAws directory, a build.

```shell
go build -o goaws app/cmd/goaws.go
```

The goaws executable ends up in the current directory, and the README says to move it somewhere in your $PATH. Running ./goaws starts the service, which by default listens on port 4100, and the port can be changed in the goaws.yaml file.

The container route is a pull and a run. The image name depends on the version, and the README is explicit about the split: for version 0.3.2 and above, docker pull admiralpiett/goaws, and for version 0.3.1 and below, docker pull pafortin/goaws.

```shell
docker run -d --name goaws -p 4100:4100 admiralpiett/goaws
```

That version split is a small sign of a project that changed hands, since the Docker Hub namespace changed. The repository still carries the old name in more than one place, including the docker-compose.yml in the root, which references the pafortin image while the documentation tells you to use admiralpiett for current versions. If you use the compose file as shipped, you will run the older image, so it is worth checking before you trust it.

The container image is built by a Dockerfile with a golang alpine build stage that runs the tests with CGO_ENABLED=0 before building, and an alpine release stage that copies the binary and the default goaws.yaml config, exposes port 4100 and defines a health check that polls the health endpoint on that port every five seconds.

## The AWS CLI drives it, with nothing but an endpoint URL

The testing section answers the question every adopter asks, which is how do I know it works. The README offers two ways.

There is a Postman environment and a Postman collection published by the author, with the collection linked for import and the environment variable set so that URL equals http://localhost:4100/.

The second way is the one that matters more, because it proves the emulator is API compatible rather than merely similar. You use the AWS CLI tools, with the download link to the AWS CLI installation guide and a pointer to the AWS CLI reference, and pass an endpoint URL.

Creating a queue looks like this.

```shell
aws --endpoint-url http://localhost:4100 sqs create-queue --queue-name test1
```

and the response is a QueueUrl pointing at the local service.

Publishing a message uses the same shape as the real thing.

```shell
aws --endpoint-url http://localhost:4100 sqs send-message --queue-url http://localhost:4100/test1 --message-body "this is a test of the GoAws Queue messaging"
```

Receiving and deleting follow, and the README shows that a second receive returns no output, meaning no messages in the queue, and that after delete-queue the list-queues call returns nothing.

SNS works the same way, with sns list-topics returning TopicArn values in the arn:aws:sns:local:000000000000 form.

That local account id in the ARN is a small detail with a practical consequence. Code that parses ARNs, or that compares the account in the ARN against an expected value, will see a different account in tests than in production, so any assertion on the ARN needs to account for it.

## No authentication and no TLS, which sets the boundary

There is one line in the README that decides where this tool can be deployed, and it is worth reading before anything else.

The note says the system does not authenticate or presently use https.

That is a deliberate simplification for a local development tool, and it is the right default for a laptop. It also means the service must not be reachable by anything you do not control, because anything that can reach port 4100 can read and write every queue and topic in it, and can publish messages to the subscribers you configured.

That rules out running it on a shared host, exposing it on a network segment with other tenants, or wiring it into a CI environment where the runner is shared. For a developer machine, a container on a local Docker network, or a dedicated CI container, the constraint is easy to satisfy.

The health endpoint deserves a mention as well, since the Dockerfile relies on it. The health check polls localhost:4100/health with wget every five seconds, with a three second timeout and three retries, which is how an orchestrator can tell whether the emulator is up.

Beyond that, the repository has a smoke_tests directory, a .goreleaser.yml and a goreleaser.dockerfile for building release artifacts, and a makefile.txt. The go.mod pins Go 1.18 as the language version, and the dependencies are the AWS SDK for Go in both its v1 and v2 forms, httpexpect and testify for testing, ghodss/yaml for the configuration, gorilla/mux for routing and logrus for logging.

## Conclusion

goaws suits a team whose services talk to SQS and SNS through the AWS SDK and whose integration tests need to run offline, on a laptop or in CI without credentials, because it answers the same endpoints and the README shows the stock AWS CLI driving it with nothing more than an endpoint URL. It does not suit a deployment where anyone untrusted can reach the port, because the README states the system does not authenticate and does not presently use https, and it does not suit a test suite that depends on the unimplemented calls. Before you adopt it, check the API list against what your code actually calls, since ChangeMessageVisibilityBatch, TagQueue, UntagQueue, ListQueueTags, ListDeadLetterSourceQueues and RemovePermission are marked as not implemented, decide whether the three supported queue attributes are enough, and note the last push to the repository was on 2026-10-05 while the newest release tag is v0.5.4 from 2025-12-19.

## FAQ

### What is goaws used for?

It is a Go implementation of the AWS SQS and SNS systems, designed to emulate them in a local environment so developers can test their interfaces without connecting to the AWS Cloud, incurring the expense, or writing to production topics and queues by mistake.

### Which SQS and SNS APIs does goaws not implement?

The README marks ChangeMessageVisibilityBatch, ListDeadLetterSourceQueues, ListQueueTags, RemovePermission, TagQueue and UntagQueue as not implemented. GetQueueAttributes returns all attributes with unsupported ones mocked, and SetQueueAttributes only sets the supported attributes.

### How do I run goaws and test it?

Build it with go build -o goaws app/cmd/goaws.go and run ./goaws, which listens on port 4100 by default, or pull the admiralpiett/goaws image. You can then test it with the AWS CLI using the aws --endpoint-url http://localhost:4100 commands in the README, or with the published Postman collection.

### Does goaws support authentication and https?

No. The README states plainly that the system does not authenticate or presently use https, so it should only be run somewhere you control and where nothing untrusted can reach the port.

### Can I pre-create queues and topics for my tests?

Yes. The YAML configuration can create Queues, Topic and Subscription, and there is an example config file in the conf directory. You point at it with the -config flag and can select an environment within the file with a command line argument, for example Dev.

## Sources

- [Admiral-Piett/goaws on GitHub](https://github.com/Admiral-Piett/goaws)
- [Issues](https://github.com/Admiral-Piett/goaws/issues)
- [License: MIT](https://github.com/Admiral-Piett/goaws/blob/master/LICENSE)
- [README](https://github.com/Admiral-Piett/goaws/blob/master/README.md)
- [Releases](https://github.com/Admiral-Piett/goaws/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/admiral-piett-goaws
