or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

aws-sdk-integration.mdaws-services.mdcontainer-configuration.mdendpoint-configuration.mdindex.mdnetwork-configuration.md
tile.json

endpoint-configuration.mddocs/

Endpoint Configuration

This document describes how to retrieve endpoint URLs, credentials, and region configuration for connecting AWS SDK clients to LocalStack, including S3 path-style access details, edge cases, and troubleshooting.

Capabilities

Main Endpoint

Retrieves the main LocalStack endpoint URL for configuring AWS SDK clients.

/**
 * Provides the main endpoint to communicate with LocalStack
 * This endpoint uses the single edge port (4566) and is intended for AWS SDK clients
 * running on the test host. The endpoint resolves to an IP address for proper S3 path-style access.
 * Must be called after start() completes.
 *
 * For containers within a Docker network, configure using the network alias instead
 * (e.g., "http://localstack:4566")
 *
 * @return URI endpoint with host IP and mapped port (e.g., "http://192.168.1.100:49153")
 * @throws IllegalStateException if container not started or cannot obtain endpoint URL due to network issues
 */
public URI getEndpoint();

Usage Examples:

import org.testcontainers.containers.localstack.LocalStackContainer;
import java.net.URI;

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withServices(Service.S3, Service.SQS);

localstack.start();

// Get the main endpoint
URI endpoint = localstack.getEndpoint();
System.out.println(endpoint);  // e.g., http://192.168.1.100:49153

// Use with AWS SDK v2
S3Client s3 = S3Client.builder()
    .endpointOverride(endpoint)
    .credentialsProvider(
        StaticCredentialsProvider.create(
            AwsBasicCredentials.create(
                localstack.getAccessKey(),
                localstack.getSecretKey()
            )
        )
    )
    .region(Region.of(localstack.getRegion()))
    .build();

// Use with AWS SDK v1
AmazonS3 s3v1 = AmazonS3ClientBuilder.standard()
    .withEndpointConfiguration(
        new AwsClientBuilder.EndpointConfiguration(
            endpoint.toString(),
            localstack.getRegion()
        )
    )
    .withCredentials(
        new AWSStaticCredentialsProvider(
            new BasicAWSCredentials(
                localstack.getAccessKey(),
                localstack.getSecretKey()
            )
        )
    )
    .build();

Service-Specific Endpoint Override

Retrieves an endpoint override for a specific AWS service. Primarily useful in legacy mode (LocalStack < 0.11) where each service runs on a different port.

/**
 * Provides endpoint override for a specific service
 * In legacy mode (LocalStack < 0.11), each service has its own port
 * In modern mode (LocalStack >= 0.11), returns the same endpoint as getEndpoint()
 * Must be called after start() completes.
 *
 * The endpoint resolves to an IP address for proper S3 path-style access
 *
 * @param service The AWS service to access using EnabledService interface
 * @return URI endpoint override for the service
 * @throws IllegalStateException if container not started or cannot obtain endpoint URL due to network issues
 */
public URI getEndpointOverride(EnabledService service);

/**
 * Convenience overload for Service enum
 * Must be called after start() completes.
 *
 * @param service The AWS service to access using Service enum
 * @return URI endpoint override for the service
 * @throws IllegalStateException if container not started or cannot obtain endpoint URL due to network issues
 */
public URI getEndpointOverride(Service service);

Usage Examples:

import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import java.net.URI;

// Modern mode (LocalStack >= 0.11)
LocalStackContainer localstack = new LocalStackContainer(
    DockerImageName.parse("localstack/localstack:2.0")
)
    .withServices(Service.S3, Service.SQS);

localstack.start();

URI s3Endpoint = localstack.getEndpointOverride(Service.S3);
URI sqsEndpoint = localstack.getEndpointOverride(Service.SQS);
// Both endpoints are the same in modern mode (both use port 4566)
System.out.println(s3Endpoint.equals(sqsEndpoint));  // true

// Legacy mode (LocalStack < 0.11)
LocalStackContainer legacyLocalstack = new LocalStackContainer(
    DockerImageName.parse("localstack/localstack:0.10.7")
)
    .withServices(Service.S3, Service.SQS);

legacyLocalstack.start();

URI s3Endpoint = legacyLocalstack.getEndpointOverride(Service.S3);
URI sqsEndpoint = legacyLocalstack.getEndpointOverride(Service.SQS);
// Different endpoints in legacy mode (S3 on 4572, SQS on 4576)
System.out.println(s3Endpoint.equals(sqsEndpoint));  // false

// Configure AWS SDK clients with service-specific endpoints
S3Client s3 = S3Client.builder()
    .endpointOverride(s3Endpoint)
    .credentialsProvider(
        StaticCredentialsProvider.create(
            AwsBasicCredentials.create(
                legacyLocalstack.getAccessKey(),
                legacyLocalstack.getSecretKey()
            )
        )
    )
    .region(Region.of(legacyLocalstack.getRegion()))
    .build();

AWS Access Key

Retrieves the AWS access key ID for authenticating with LocalStack.

/**
 * Provides the default AWS access key ID for LocalStack
 * The access key can be overridden by setting the AWS_ACCESS_KEY_ID environment variable
 * on the container. Must be called after start() completes.
 *
 * @return Access key ID (default: "test", or from AWS_ACCESS_KEY_ID env var)
 */
public String getAccessKey();

Usage Examples:

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withServices(Service.S3);

localstack.start();

// Get default access key
String accessKey = localstack.getAccessKey();
System.out.println(accessKey);  // "test"

// Override with custom access key
LocalStackContainer customLocalstack = new LocalStackContainer(dockerImageName)
    .withEnv("AWS_ACCESS_KEY_ID", "mycustomkey")
    .withServices(Service.S3);

customLocalstack.start();
String customAccessKey = customLocalstack.getAccessKey();
System.out.println(customAccessKey);  // "mycustomkey"

// Use in AWS SDK
AwsBasicCredentials credentials = AwsBasicCredentials.create(
    localstack.getAccessKey(),
    localstack.getSecretKey()
);

AWS Secret Key

Retrieves the AWS secret access key for authenticating with LocalStack.

/**
 * Provides the default AWS secret access key for LocalStack
 * The secret key can be overridden by setting the AWS_SECRET_ACCESS_KEY environment variable
 * on the container. Must be called after start() completes.
 *
 * @return Secret access key (default: "test", or from AWS_SECRET_ACCESS_KEY env var)
 */
public String getSecretKey();

Usage Examples:

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withServices(Service.DYNAMODB);

localstack.start();

// Get default secret key
String secretKey = localstack.getSecretKey();
System.out.println(secretKey);  // "test"

// Override with custom secret key
LocalStackContainer customLocalstack = new LocalStackContainer(dockerImageName)
    .withEnv("AWS_SECRET_ACCESS_KEY", "mycustomsecret")
    .withServices(Service.DYNAMODB);

customLocalstack.start();
String customSecretKey = customLocalstack.getSecretKey();
System.out.println(customSecretKey);  // "mycustomsecret"

// Use with AWS SDK v1
BasicAWSCredentials credentials = new BasicAWSCredentials(
    localstack.getAccessKey(),
    localstack.getSecretKey()
);

AWSStaticCredentialsProvider credentialsProvider =
    new AWSStaticCredentialsProvider(credentials);

AWS Region

Retrieves the AWS region for LocalStack.

/**
 * Provides the default AWS region for LocalStack
 * The region can be overridden by setting the DEFAULT_REGION environment variable
 * on the container. Must be called after start() completes.
 *
 * @return AWS region (default: "us-east-1", or from DEFAULT_REGION env var)
 */
public String getRegion();

Usage Examples:

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withServices(Service.S3);

localstack.start();

// Get default region
String region = localstack.getRegion();
System.out.println(region);  // "us-east-1"

// Override with custom region
LocalStackContainer euLocalstack = new LocalStackContainer(dockerImageName)
    .withEnv("DEFAULT_REGION", "eu-west-1")
    .withServices(Service.S3);

euLocalstack.start();
String euRegion = euLocalstack.getRegion();
System.out.println(euRegion);  // "eu-west-1"

// Use with AWS SDK v2
Region awsRegion = Region.of(localstack.getRegion());

S3Client s3 = S3Client.builder()
    .endpointOverride(localstack.getEndpoint())
    .credentialsProvider(
        StaticCredentialsProvider.create(
            AwsBasicCredentials.create(
                localstack.getAccessKey(),
                localstack.getSecretKey()
            )
        )
    )
    .region(awsRegion)
    .build();

// Use with AWS SDK v1
AmazonS3 s3v1 = AmazonS3ClientBuilder.standard()
    .withEndpointConfiguration(
        new AwsClientBuilder.EndpointConfiguration(
            localstack.getEndpoint().toString(),
            localstack.getRegion()  // Pass region string directly
        )
    )
    .withCredentials(
        new AWSStaticCredentialsProvider(
            new BasicAWSCredentials(
                localstack.getAccessKey(),
                localstack.getSecretKey()
            )
        )
    )
    .build();

Complete Configuration Example

Here's a complete example showing how to retrieve all configuration values and use them with AWS SDK clients:

import org.testcontainers.containers.localstack.LocalStackContainer;
import org.testcontainers.containers.localstack.LocalStackContainer.Service;
import org.testcontainers.utility.DockerImageName;
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.services.sqs.SqsClient;
import java.net.URI;

public class LocalStackExample {
    public static void main(String[] args) {
        // Create and start LocalStack
        LocalStackContainer localstack = new LocalStackContainer(
            DockerImageName.parse("localstack/localstack:2.0")
        )
            .withServices(Service.S3, Service.SQS)
            .withEnv("DEFAULT_REGION", "us-west-2");

        localstack.start();

        try {
            // Retrieve all configuration
            URI endpoint = localstack.getEndpoint();
            String accessKey = localstack.getAccessKey();
            String secretKey = localstack.getSecretKey();
            String region = localstack.getRegion();

            // Print configuration
            System.out.println("LocalStack Configuration:");
            System.out.println("Endpoint: " + endpoint);
            System.out.println("Access Key: " + accessKey);
            System.out.println("Secret Key: " + secretKey);
            System.out.println("Region: " + region);

            // Create credentials provider
            StaticCredentialsProvider credentialsProvider =
                StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(accessKey, secretKey)
                );

            // Configure S3 client
            S3Client s3 = S3Client.builder()
                .endpointOverride(endpoint)
                .credentialsProvider(credentialsProvider)
                .region(Region.of(region))
                .forcePathStyle(true)
                .build();

            // Configure SQS client
            SqsClient sqs = SqsClient.builder()
                .endpointOverride(endpoint)
                .credentialsProvider(credentialsProvider)
                .region(Region.of(region))
                .build();

            // Use the clients
            s3.createBucket(b -> b.bucket("test-bucket"));
            System.out.println("Created S3 bucket");

            sqs.createQueue(q -> q.queueName("test-queue"));
            System.out.println("Created SQS queue");

        } catch (Exception e) {
            System.err.println("Error: " + e.getMessage());
            e.printStackTrace();
        } finally {
            localstack.stop();
        }
    }
}

S3 Path-Style Access

LocalStack requires path-style access for S3. The getEndpoint() method returns an IP address (not hostname) to ensure path-style access works correctly.

Why IP Address?

S3 supports two access styles:

  • Path-style: http://s3.amazonaws.com/bucket-name/key
  • Virtual-hosted style: http://bucket-name.s3.amazonaws.com/key

LocalStack requires path-style access. When using a hostname like localhost, some AWS SDK configurations may attempt virtual-hosted style access, which fails. Using an IP address forces path-style access.

SDK v2 Configuration

import software.amazon.awssdk.services.s3.S3Client;

LocalStackContainer localstack = // ... configured
localstack.start();

URI endpoint = localstack.getEndpoint();
// Returns: http://192.168.1.100:49153 (IP address)

S3Client s3 = S3Client.builder()
    .endpointOverride(endpoint)  // IP address ensures path-style
    .forcePathStyle(true)  // Explicitly enable path-style (recommended)
    .credentialsProvider(/* ... */)
    .region(Region.of(localstack.getRegion()))
    .build();

SDK v1 Configuration

import com.amazonaws.services.s3.AmazonS3;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

LocalStackContainer localstack = // ... configured
localstack.start();

AmazonS3 s3 = AmazonS3ClientBuilder.standard()
    .withEndpointConfiguration(
        new AwsClientBuilder.EndpointConfiguration(
            localstack.getEndpoint().toString(),  // IP address
            localstack.getRegion()
        )
    )
    .withPathStyleAccessEnabled(true)  // Required for LocalStack
    .withCredentials(/* ... */)
    .build();

Common S3 Path-Style Issues

Problem: S3 operations fail with "Invalid endpoint" or "Bucket name contains invalid characters".

Solution: Ensure forcePathStyle(true) is set and endpoint is IP address:

S3Client s3 = S3Client.builder()
    .endpointOverride(localstack.getEndpoint())  // IP address
    .forcePathStyle(true)  // Required
    .build();

Problem: Bucket names with dots cause issues.

Solution: Use path-style access (already handled by getEndpoint() returning IP):

// Bucket name with dots works with path-style
s3.createBucket(b -> b.bucket("my.bucket.name"));

Docker Network Considerations

When using LocalStack with Docker networks, containers within the network should use the network alias instead of the host-exposed endpoint:

import org.testcontainers.containers.Network;
import org.testcontainers.containers.GenericContainer;

Network network = Network.newNetwork();

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withNetwork(network)
    .withNetworkAliases("localstack")
    .withServices(Service.S3);

localstack.start();

// For test code running on host, use getEndpoint()
URI hostEndpoint = localstack.getEndpoint();
// Returns: http://192.168.1.100:49153

// For containers within the network, use the network alias
GenericContainer<?> appContainer = new GenericContainer<>("myapp:latest")
    .withNetwork(network)
    .withEnv("AWS_ENDPOINT", "http://localstack:4566")  // Use alias, not getEndpoint()
    .withEnv("AWS_ACCESS_KEY_ID", localstack.getAccessKey())
    .withEnv("AWS_SECRET_ACCESS_KEY", localstack.getSecretKey())
    .withEnv("AWS_REGION", localstack.getRegion());

IP Address Resolution

The getEndpoint() and getEndpointOverride() methods automatically resolve hostnames to IP addresses. This is important for S3 path-style access, as some AWS SDK configurations require IP addresses rather than hostnames:

URI endpoint = localstack.getEndpoint();
// Returns: http://192.168.1.100:49153 (IP address, not hostname)

// This ensures S3 path-style access works correctly
S3Client s3 = S3Client.builder()
    .endpointOverride(endpoint)  // IP-based endpoint
    // ... other configuration
    .build();

Error Handling

Handling IllegalStateException

getEndpoint() and getEndpointOverride() throw IllegalStateException if called before container is started or if network configuration fails:

LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withServices(Service.S3);

try {
    // WRONG - container not started
    URI endpoint = localstack.getEndpoint();  // IllegalStateException!
} catch (IllegalStateException e) {
    System.err.println("Container not started: " + e.getMessage());
}

// CORRECT
localstack.start();
try {
    URI endpoint = localstack.getEndpoint();  // Works
} catch (IllegalStateException e) {
    System.err.println("Failed to get endpoint: " + e.getMessage());
    throw e;
}

Handling Network Issues

If endpoint resolution fails due to network issues:

try {
    URI endpoint = localstack.getEndpoint();
} catch (IllegalStateException e) {
    // Check container is running
    if (!localstack.isRunning()) {
        System.err.println("Container is not running");
    }
    // Check network configuration
    System.err.println("Network error: " + e.getMessage());
    throw e;
}

Edge Cases

Endpoint Before Container Start

// WRONG
LocalStackContainer localstack = new LocalStackContainer(dockerImageName);
URI endpoint = localstack.getEndpoint();  // IllegalStateException

// CORRECT
LocalStackContainer localstack = new LocalStackContainer(dockerImageName);
localstack.start();
URI endpoint = localstack.getEndpoint();  // Works

Multiple Endpoint Calls

// Multiple calls return same endpoint (cached)
URI endpoint1 = localstack.getEndpoint();
URI endpoint2 = localstack.getEndpoint();
System.out.println(endpoint1.equals(endpoint2));  // true

Legacy Mode Endpoint Differences

// Legacy mode - different endpoints per service
LocalStackContainer legacy = new LocalStackContainer(
    DockerImageName.parse("localstack/localstack:0.10.7")
)
    .withServices(Service.S3, Service.SQS);

legacy.start();

URI mainEndpoint = legacy.getEndpoint();  // Port 4566
URI s3Endpoint = legacy.getEndpointOverride(Service.S3);  // Port 4572
URI sqsEndpoint = legacy.getEndpointOverride(Service.SQS);  // Port 4576

System.out.println(mainEndpoint.equals(s3Endpoint));  // false
System.out.println(s3Endpoint.equals(sqsEndpoint));  // false

Custom Credentials

// Custom credentials override defaults
LocalStackContainer localstack = new LocalStackContainer(dockerImageName)
    .withEnv("AWS_ACCESS_KEY_ID", "custom-key")
    .withEnv("AWS_SECRET_ACCESS_KEY", "custom-secret")
    .withEnv("DEFAULT_REGION", "eu-west-1")
    .withServices(Service.S3);

localstack.start();

String accessKey = localstack.getAccessKey();  // "custom-key"
String secretKey = localstack.getSecretKey();  // "custom-secret"
String region = localstack.getRegion();  // "eu-west-1"