How to Solve LocalStack Always Connecting To LocalHost

I am using LocalStack to emulate AWS services in my local machine. I believe you already know what LocalStack is. And that’s why you are reading this post. Still let me share a one-liner on how LocalStack works. It will help us understand the problem.
LocalStack runs a single docker container & the application running inside the container is responsible to imitate different AWS services. We define the services that need to be emulated (e.g. DynamoDB, SQS etc.) when we set up the docker-compose file for LocalStack.

Now let’s come to the problem. I have set up SQS as part of LocalStack & it is running successfully. I can access it using AWS cli & private IP address. Here is my docker-compose.yml file configuration:

version: '3.0'

services:

  localstack:
    image: localstack/localstack:latest
    ports:
      - "4566-4599:4566-4599"
    environment:
      - AWS_DEFAULT_REGION=ap-southeast-1
      - SERVICES=sqs,dynamodb,lambda
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"%

You can see below the QueueUrl for the SQS queue that I have created. It adds localhost as the hostname in the url. That is the problem. By the way, 10.5.3.62 is the private IP address of my local machine.

➜  local-stack aws --endpoint-url=http://10.5.3.62:4566 sqs list-queues
{
    "QueueUrls": [
        "http://localhost:4566/000000000000/dev_sqs"
    ]
}
➜  local-stack

Now let’s consider the scenario where we are running our own Java application in another docker container. We have added an SQSClient using AWS Java SDK & configured it with the private IP address 10.5.3.62 as endpoint. Let’s run below code:

        String queueUrl = sqsClient.createQueue("dev_sqs").getQueueUrl();
        logger.info("Print queue url: {}", queueUrl);

We will see that the printed value is:

Print queue url: http://localhost:4566/000000000000/dev_sqs

We are still getting localhost though we have set sqsClient with proper private IP as endpoint. Now let’s use this queueUrl & try to push a message in SQS:

SendMessageRequest sendMessageRequest = new SendMessageRequest(queueUrl, message);
sqsClient.sendMessage(sendMessageRequest);

We will see error similar to below:

"exception":"com.amazonaws.SdkClientException: Unable to execute HTTP request: Connect to localhost:4566 [localhost/127.0.0.1] failed: Connection refused (Connection refused)
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleRetryableException(AmazonHttpClient.java:1207) ~[aws-java-sdk-core-1.11.822.jar:?]
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1153) ~[aws-java-sdk-core-1.11.822.jar:?]
at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:802) ~[aws-java-sdk-core-1.11.822.jar:?]

Queue url is still having localhost as the hostname. We are in a docker environment. Our Java application container will try to find http://localhost:4566/000000000000/dev_sqs within its own network. It would fail as SQS is running in a different docker container where LocalStack is running. The problem is that LocalStack is not using the correct hostname or IP address. That’s why it is failing in docker environment, but working in AWS cli.

We can fix it while starting LocalStack & provide the correct hostname or IP address in the HOSTNAME_EXTERNAL field. That will expose the services externally using the hostname. We need to change our docker-compose file as below:

version: '3.0'

services:

  localstack:
    image: localstack/localstack:latest
    ports:
      - "4566-4599:4566-4599"
    environment:
      - AWS_DEFAULT_REGION=ap-southeast-1
      - SERVICES=sqs,dynamodb,lambda
      - HOSTNAME_EXTERNAL=10.5.3.62
    volumes:
      - "${TMPDIR:-/tmp/localstack}:/tmp/localstack"
      - "/var/run/docker.sock:/var/run/docker.sock"%  

Now if I create the same SQS, it will show private IP address in the QueueUrl:

➜  local-stack aws --endpoint-url=http://10.5.3.62:4566 sqs list-queues
{
    "QueueUrls": [
        "http://10.5.3.62:4566/000000000000/dev_sqs"
    ]
}
➜  local-stack

Now if we use sqsClient in our Java application as mentioned above, it will work. Application would be able to send the message to SQS successfully. Docker container will try to access the queue using private IP http://10.5.3.62:4566/000000000000/dev_sqs. In my case, that is the IP of docker host machine which the container has access to. So the connection is successfully established. This way we can mitigate the connection refused error.

Leave a Comment