DocsAWS 101BlogServices

Testcontainers (Java)

The official Testcontainers module ships a dedicated MiniStackContainer class. It starts MiniStack, waits for /_ministack/health, and exposes the mapped endpoint and credentials for wiring any AWS SDK for Java v2 client.

JUnit 5 Testcontainers AWS SDK for Java v2 Maven Central

Install

Maven

<dependency>
  <groupId>org.ministack</groupId>
  <artifactId>testcontainers-ministack</artifactId>
  <version>0.1.5</version>
  <scope>test</scope>
</dependency>

Gradle

testImplementation 'org.ministack:testcontainers-ministack:0.1.5'

The module pulls the ministackorg/ministack image and requires a running Docker daemon, like any Testcontainers module. Check Maven Central for the latest release.

Quick start

Start the container, read getEndpoint(), and point an SDK client at it. The default credentials are test / test and the default region is us-east-1.

try (MiniStackContainer ministack = new MiniStackContainer()) {
    ministack.start();
    String endpoint = ministack.getEndpoint();

    S3Client s3 = S3Client.builder()
            .endpointOverride(URI.create(endpoint))
            .region(Region.of(ministack.getRegion()))
            .credentialsProvider(StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(ministack.getAccessKey(), ministack.getSecretKey())))
            .forcePathStyle(true)
            .build();

    s3.createBucket(b -> b.bucket("my-bucket"));
}

The accessor methods available on the container are getEndpoint(), getPort(), getRegion(), getAccessKey(), getSecretKey(), and getMiniStackVersion(). Use forcePathStyle(true) for S3 — MiniStack serves path-style URLs by default.

Configuration

The builder methods set the corresponding MiniStack environment variables on the container:

MiniStackContainer ministack = new MiniStackContainer("1.3.42")
    .withRegion("eu-west-1")                            // MINISTACK_REGION
    .withCredentials("AKIAEXAMPLE000000000", "secret")  // AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
    .withPersistence()                                  // PERSIST_STATE=1 + S3_PERSIST=1
    .withImdsV2Required();                              // MINISTACK_IMDS_V2_REQUIRED=1

Version pinning

Pass a tag to pin a specific MiniStack release instead of the default image tag:

MiniStackContainer ministack = new MiniStackContainer("1.4.5");

Gate tests on capability with getMiniStackVersion(), which returns the image tag verbatim:

assumeTrue(ministack.getMiniStackVersion().compareTo("1.3.42") >= 0,
    "test requires MiniStack 1.3.42+");
Note: semver tags like "1.3.42" sort lexicographically as expected, but "latest" and "nightly" do not. Pin a specific version tag when you need precise capability gating.

Private registry

The module automatically forwards Testcontainers' hub.image.name.prefix into the container as MINISTACK_IMAGE_PREFIX. This means nested real-infrastructure images (RDS postgres/mysql/mariadb, ElastiCache redis/memcached, EKS k3s, Lambda runtimes) route through the same private registry or proxy as the MiniStack image itself — important in air-gapped or proxy-only environments where a bare docker.io / public.ecr.aws pull would fail. No configuration is needed; set the standard Testcontainers prefix and it propagates.

Real infrastructure

Call withRealInfrastructure() to let MiniStack spin up real backend containers (RDS, ElastiCache, ECS, EKS) by bind-mounting the host Docker socket into the MiniStack container:

try (MiniStackContainer ministack = new MiniStackContainer()) {
    ministack.withRealInfrastructure();
    ministack.start();

    RdsClient rds = RdsClient.builder()
            .endpointOverride(URI.create(ministack.getEndpoint()))
            .region(Region.of(ministack.getRegion()))
            .credentialsProvider(StaticCredentialsProvider.create(
                    AwsBasicCredentials.create(ministack.getAccessKey(), ministack.getSecretKey())))
            .build();

    rds.createDBInstance(b -> b
            .dbInstanceIdentifier("postgres")
            .dbInstanceClass("db.t3.micro")
            .engine("postgres")
            .masterUsername("admin")
            .masterUserPassword("password")
            .dbName("postgresdb")
            .allocatedStorage(20));

    // MiniStack spawns a real Postgres container. Poll DescribeDBInstances
    // until it reports `available` before opening a JDBC connection.
    Awaitility.await()
        .atMost(Duration.ofMinutes(2))
        .pollInterval(Duration.ofSeconds(2))
        .until(() -> "available".equals(
            rds.describeDBInstances(b -> b.dbInstanceIdentifier("postgres"))
               .dbInstances().get(0).dbInstanceStatus()));
}
Security warning: withRealInfrastructure() bind-mounts the host Docker socket into the MiniStack container. Anything running inside MiniStack — including arbitrary code in Lambda handlers or RDS init scripts — gains root-equivalent control of the host's container engine. Use only on trusted developer machines or isolated CI runners.