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.
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();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();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()
);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);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();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();
}
}
}LocalStack requires path-style access for S3. The getEndpoint() method returns an IP address (not hostname) to ensure path-style access works correctly.
S3 supports two access styles:
http://s3.amazonaws.com/bucket-name/keyhttp://bucket-name.s3.amazonaws.com/keyLocalStack 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.
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();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();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"));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());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();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;
}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;
}// 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 calls return same endpoint (cached)
URI endpoint1 = localstack.getEndpoint();
URI endpoint2 = localstack.getEndpoint();
System.out.println(endpoint1.equals(endpoint2)); // true// 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 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"