or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdcontainer-lifecycle.mddocker-client.mddocker-compose.mdimage-building.mdimage-management.mdimage-pull-policies.mdindex.mdjunit-jupiter-integration.mdlifecycle.mdmodules-overview.mdnetwork-configuration.mdoutput-handling.mdstartup-checks.mdutility-classes.mdwait-strategies.md
tile.json

tessl/maven-org-testcontainers--testcontainers

Java library for integration testing with Docker containers

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/org.testcontainers/testcontainers@2.0.x

To install, run

npx @tessl/cli install tessl/maven-org-testcontainers--testcontainers@2.0.0

index.mddocs/

Testcontainers Java

Testcontainers is a Java library that provides lightweight, throwaway Docker container instances for integration testing. It enables developers to write tests that use real dependencies (databases, message brokers, web services) running in Docker containers, ensuring tests run in environments that closely mirror production. Testcontainers manages the complete container lifecycle automatically, from pulling images to cleaning up resources after tests complete.

Package Information

  • Package Name: org.testcontainers:testcontainers
  • Package Type: Maven
  • Language: Java
  • Installation: Add to your pom.xml:
<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>testcontainers</artifactId>
    <version>2.0.3</version>
    <scope>test</scope>
</dependency>

Or for Gradle:

testImplementation 'org.testcontainers:testcontainers:2.0.3'

Core Imports

import org.testcontainers.Testcontainers;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.lifecycle.Startables;
import org.testcontainers.utility.DockerImageName;

Basic Usage

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import org.junit.jupiter.api.Test;

public class RedisBackedCacheTest {

    @Test
    public void testSimpleRedisUsage() {
        // Create a Redis container
        try (GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:7.0-alpine"))
                .withExposedPorts(6379)) {

            // Start the container
            redis.start();

            // Get connection details
            String host = redis.getHost();
            Integer port = redis.getMappedPort(6379);

            // Use the Redis instance in your test
            // RedisClient client = RedisClient.create("redis://" + host + ":" + port);
            // ... test code here ...
        } // Container is automatically stopped and removed
    }
}

More complex example with configuration:

import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
import java.time.Duration;

public class PostgresDatabaseTest {

    @Test
    public void testDatabaseConnection() {
        try (GenericContainer<?> postgres = new GenericContainer<>(DockerImageName.parse("postgres:15"))
                .withExposedPorts(5432)
                .withEnv("POSTGRES_PASSWORD", "test")
                .withEnv("POSTGRES_DB", "testdb")
                .waitingFor(Wait.forListeningPort())
                .withStartupTimeout(Duration.ofSeconds(60))) {

            postgres.start();

            String jdbcUrl = String.format("jdbc:postgresql://%s:%d/testdb",
                postgres.getHost(),
                postgres.getMappedPort(5432));

            // Connect and run tests
        }
    }
}

Quick Reference

Common Container Operations

OperationMethodExample
Expose portswithExposedPorts(Integer...).withExposedPorts(8080, 8081)
Set environmentwithEnv(String, String).withEnv("DB_NAME", "test")
Run commandwithCommand(String...).withCommand("sh", "-c", "sleep 10")
Copy fileswithCopyFileToContainer(MountableFile, String).withCopyFileToContainer(file, "/app/config")
Wait for readywaitingFor(WaitStrategy).waitingFor(Wait.forHttp("/health"))
Set timeoutwithStartupTimeout(Duration).withStartupTimeout(Duration.ofMinutes(2))

Common Wait Strategies

StrategyUse CaseExample
Wait.forListeningPort()Service listening on portRedis, PostgreSQL
Wait.forHttp(path)HTTP endpoint availableWeb applications, REST APIs
Wait.forLogMessage(regex, times)Log message appearsApplication startup messages
Wait.forHealthcheck()Docker healthcheck passesImages with HEALTHCHECK

Exception Handling

All container operations can throw the following exceptions:

// Common exceptions to handle
org.testcontainers.containers.ContainerLaunchException  // Container failed to start
org.testcontainers.containers.ContainerFetchException   // Failed to pull image
java.util.concurrent.TimeoutException                   // Startup timeout exceeded

Example with error handling:

try {
    GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
        .withExposedPorts(8080)
        .waitingFor(Wait.forHttp("/health").forStatusCode(200))
        .withStartupTimeout(Duration.ofSeconds(30));

    container.start();
    // Use container

} catch (ContainerLaunchException e) {
    // Container failed to start - check logs
    System.err.println("Container logs: " + container.getLogs());
    throw e;
} catch (TimeoutException e) {
    // Startup took too long - check wait strategy
    System.err.println("Container did not become ready in time");
    throw e;
}

Architecture

Testcontainers follows a layered architecture with clear separation of concerns:

Core Container Management Layer:

  • GenericContainer: The primary class for creating and managing Docker containers. Uses a fluent API with self-referential generics (SELF extends GenericContainer<SELF>) for type-safe method chaining in subclasses.

  • Container Lifecycle: Implements Startable and AutoCloseable interfaces for proper resource management. Provides lifecycle hooks (configure(), containerIsCreated(), containerIsStarting(), containerIsStarted()) for customization in subclasses.

Readiness Detection Layer:

  • Wait Strategies: Strategy pattern implementation for determining container readiness. The WaitStrategy interface allows different approaches (port listening, HTTP endpoints, log messages, healthchecks) to detect when containers are ready for testing. Keeps readiness logic decoupled from container lifecycle.

Docker Integration Layer:

  • Image Management: Abstraction layer for Docker images supporting remote images, locally built images, and Jib-based builds. The ImagePullPolicy interface controls when images are pulled from registries.

  • Docker Client Abstraction: Strategy pattern for Docker client configuration (DockerClientProviderStrategy) supporting multiple Docker hosts (Unix socket, Windows named pipe, Docker Desktop, Docker Machine, environment variables). Isolates environment-specific Docker connectivity from container logic.

Network & Communication Layer:

  • Network Management: The Network interface represents Docker networks, enabling containers to communicate using network aliases and service discovery patterns.

Resource Management Layer:

  • Resource Cleanup: Singleton ResourceReaper automatically cleans up containers, networks, and images at JVM shutdown, preventing resource leaks even when tests fail. Handles cleanup orthogonally to container lifecycle.

API Stability:

  • API Stability Annotations: The library uses the @UnstableAPI annotation to mark APIs that are subject to change and should not be considered stable. These APIs may change in future versions without following semantic versioning guarantees. When using APIs marked with @UnstableAPI, be prepared to adapt your code during library upgrades.

Capabilities

Host Port Exposure

Utility class for exposing host ports to containers, enabling containers to access services running on the host machine. This is particularly useful when containers need to connect to databases, APIs, or other services running on the Docker host.

/**
 * Utility class for exposing host ports to containers.
 * This is a utility class with static methods only and cannot be instantiated.
 */
public class Testcontainers {
    /**
     * Expose one or more host ports to containers.
     * Makes the specified ports on the host accessible from within containers
     * using the special hostname 'host.testcontainers.internal'.
     *
     * @param ports the host ports to expose
     */
    public static void exposeHostPorts(int... ports);

    /**
     * Expose host ports to containers with custom port mapping.
     * Maps host ports to different container-side ports.
     *
     * @param ports map of host port to container port mappings
     */
    public static void exposeHostPorts(Map<Integer, Integer> ports);
}

Usage Examples:

import org.testcontainers.Testcontainers;
import org.testcontainers.containers.GenericContainer;

// Expose host port for container to access host service
Testcontainers.exposeHostPorts(8080);

// Container can now access host service at host.testcontainers.internal:8080
GenericContainer<?> container = new GenericContainer<>("myapp:latest")
    .withEnv("API_URL", "http://host.testcontainers.internal:8080/api")
    .withExposedPorts(3000);

container.start();

Common Use Cases:

  1. Accessing Host Database:
// Host PostgreSQL running on port 5432
Testcontainers.exposeHostPorts(5432);

GenericContainer<?> app = new GenericContainer<>("myapp:latest")
    .withEnv("DB_HOST", "host.testcontainers.internal")
    .withEnv("DB_PORT", "5432");
  1. Testing Against Local API:
// Local development API running on port 8000
Testcontainers.exposeHostPorts(8000);

GenericContainer<?> testRunner = new GenericContainer<>("test-suite:latest")
    .withEnv("API_BASE_URL", "http://host.testcontainers.internal:8000");
  1. Multiple Ports:
// Expose multiple services
Testcontainers.exposeHostPorts(5432, 6379, 8080);
  1. Custom Port Mapping:
// Map host port 3000 to container-side port 80
Map<Integer, Integer> portMap = Map.of(3000, 80, 5432, 5432);
Testcontainers.exposeHostPorts(portMap);

// Container accesses host:3000 as host.testcontainers.internal:80

Important Notes:

  • The special hostname host.testcontainers.internal resolves to the Docker host from within containers
  • Port exposure is global - once exposed, all containers can access those host ports
  • This feature uses a port forwarding container managed by Testcontainers
  • Works across all platforms (Linux, macOS, Windows)

Parallel Container Management

Utility for starting multiple containers in parallel with automatic dependency resolution. The Startables class enables efficient parallel startup of container groups, significantly reducing test execution time when working with multiple containers.

/**
 * Utilities for managing multiple Startable resources.
 * This is a utility class with static methods only and cannot be instantiated.
 */
public class Startables {
    /**
     * Start multiple resources, respecting their dependencies.
     * Containers are started in parallel where possible, with automatic
     * dependency resolution ensuring correct startup order.
     *
     * @param startables resources to start
     * @return CompletableFuture that completes when all are started
     */
    public static CompletableFuture<Void> deepStart(Startable... startables);

    /**
     * Start a collection of resources, respecting their dependencies.
     *
     * @param startables resources to start
     * @return CompletableFuture that completes when all are started
     */
    public static CompletableFuture<Void> deepStart(Collection<? extends Startable> startables);

    /**
     * Start a stream of resources asynchronously, respecting dependencies.
     *
     * @param startables stream of resources to start
     * @return CompletableFuture that completes when all are started
     */
    public static CompletableFuture<Void> deepStart(Stream<? extends Startable> startables);
}

Usage Example:

import org.testcontainers.lifecycle.Startables;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;

// Create multiple containers with dependencies
Network network = Network.newNetwork();

GenericContainer<?> database = new GenericContainer<>(DockerImageName.parse("postgres:15"))
    .withNetwork(network)
    .withNetworkAliases("db")
    .withExposedPorts(5432);

GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:7.0"))
    .withNetwork(network)
    .withNetworkAliases("cache")
    .withExposedPorts(6379);

GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
    .withNetwork(network)
    .withExposedPorts(8080)
    .dependsOn(database, redis);  // Explicit dependencies

// Start all containers in parallel (database and redis start together, app waits for them)
Startables.deepStart(app).join();

// All containers are now running with correct startup order
String appUrl = String.format("http://%s:%d", app.getHost(), app.getMappedPort(8080));

Performance Benefits:

// Without Startables (sequential startup - slow)
database.start();  // ~5 seconds
redis.start();     // ~3 seconds
app.start();       // ~10 seconds
// Total: ~18 seconds

// With Startables (parallel startup - fast)
Startables.deepStart(database, redis, app).join();
// Total: ~10 seconds (database and redis start in parallel)

Common Use Cases:

  1. Multiple Independent Services:
// Start multiple databases in parallel for testing
GenericContainer<?> postgres = new GenericContainer<>("postgres:15");
GenericContainer<?> mysql = new GenericContainer<>("mysql:8");
GenericContainer<?> mongodb = new GenericContainer<>("mongo:6");

Startables.deepStart(postgres, mysql, mongodb).join();
// All three start simultaneously
  1. Microservices with Dependencies:
GenericContainer<?> messageQueue = new GenericContainer<>("rabbitmq:3");
GenericContainer<?> database = new GenericContainer<>("postgres:15");

GenericContainer<?> userService = new GenericContainer<>("user-service:latest")
    .dependsOn(database, messageQueue);
GenericContainer<?> orderService = new GenericContainer<>("order-service:latest")
    .dependsOn(database, messageQueue);

// Database and message queue start in parallel, then both services start in parallel
Startables.deepStart(userService, orderService).join();

Lifecycle Management

Lifecycle Management

Control test and container lifecycle with interfaces for starting, stopping, and test integration. The Startable interface provides lifecycle management for containers and resources.

/**
 * Interface for lifecycle-managed resources.
 */
public interface Startable {
    void start();
    void stop();
}

/**
 * Interface for test-aware resources.
 */
public interface TestLifecycleAware {
    void beforeTest(TestDescription description);
    void afterTest(TestDescription description, Optional<Throwable> throwable);
}

Key Use Cases:

  • Implementing custom startable resources
  • Test lifecycle hooks for setup/teardown
  • JUnit integration patterns
  • Cross-test container reuse

Lifecycle Management

JUnit Jupiter Integration

Seamless test framework integration with automatic container lifecycle management. The JUnit Jupiter extension eliminates boilerplate by automatically starting and stopping containers based on test annotations.

import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.junit.jupiter.Container;

/**
 * Activates automatic container management for test classes.
 * Containers annotated with @Container are started/stopped automatically.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Testcontainers {
    /**
     * Whether tests should be disabled when Docker is unavailable.
     * Default: false (tests fail if Docker unavailable)
     */
    boolean disabledWithoutDocker() default false;

    /**
     * Whether containers should start in parallel.
     * Default: false (sequential startup)
     */
    boolean parallel() default false;
}

/**
 * Marks container fields for automatic lifecycle management.
 * Static fields: shared across all tests
 * Instance fields: restarted per test
 */
@Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface Container {
}

Usage Example:

@Testcontainers
class DatabaseTest {
    // Shared container (started once)
    @Container
    private static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");

    // Per-test container (restarted for each test)
    @Container
    private GenericContainer<?> redis = new GenericContainer<>("redis:7.0");

    @Test
    void test() {
        // Containers automatically started and will be stopped
    }
}

JUnit Jupiter Integration

Specialized Container Modules

Pre-configured container implementations for popular databases, message queues, and services. Modules provide simplified APIs with service-specific configuration methods and connection helpers.

/**
 * PostgreSQL database container with JDBC support.
 * Default port: 5432, database: test, user: test
 */
public class PostgreSQLContainer<SELF extends PostgreSQLContainer<SELF>>
    extends JdbcDatabaseContainer<SELF> {

    public String getJdbcUrl();      // jdbc:postgresql://host:port/database
    public String getUsername();      // Default: "test"
    public String getPassword();      // Default: "test"
    public String getDatabaseName();  // Default: "test"

    public SELF withDatabaseName(String databaseName);
    public SELF withUsername(String username);
    public SELF withPassword(String password);
    public SELF withInitScript(String initScriptPath);
}

/**
 * MySQL database container with JDBC support.
 * Default port: 3306, database: test, user: test
 */
public class MySQLContainer<SELF extends MySQLContainer<SELF>>
    extends JdbcDatabaseContainer<SELF> {

    public String getJdbcUrl();  // Includes useSSL=false automatically
    public String getDriverClassName();  // com.mysql.cj.jdbc.Driver

    public SELF withConfigurationOverride(String configurationPath);
}

/**
 * MongoDB NoSQL database container.
 * Default port: 27017, no authentication
 */
public class MongoDBContainer extends GenericContainer<MongoDBContainer> {

    public String getConnectionString();  // mongodb://host:port
    public String getReplicaSetUrl();     // For transactions

    public MongoDBContainer withReplicaSet();  // Enable replica set mode
}

Available Modules:

  • Databases: PostgreSQL, MySQL, MongoDB, Oracle, MS SQL Server, Cassandra, Neo4j, Redis
  • Message Queues: Kafka, RabbitMQ, Pulsar, ActiveMQ
  • Search: Elasticsearch, Solr
  • Cloud Services: LocalStack (AWS), Azure, GCloud
  • And 50+ more...

Usage Example:

// PostgreSQL with JDBC
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withDatabaseName("mydb")
    .withInitScript("schema.sql");
postgres.start();

Connection conn = DriverManager.getConnection(
    postgres.getJdbcUrl(),
    postgres.getUsername(),
    postgres.getPassword()
);

// MongoDB
MongoDBContainer mongodb = new MongoDBContainer("mongo:7.0")
    .withReplicaSet();  // For transactions
mongodb.start();

MongoClient client = MongoClients.create(mongodb.getConnectionString());

Specialized Container Modules

Container Lifecycle and Configuration

Container Lifecycle and Configuration

Core container management functionality including creation, configuration, startup, and cleanup. The GenericContainer class provides a fluent API for configuring all aspects of container behavior.

public class GenericContainer<SELF extends GenericContainer<SELF>>
    implements Container<SELF>, AutoCloseable, WaitStrategyTarget, Startable {

    // Configuration methods
    public SELF withImagePullPolicy(ImagePullPolicy policy);
    public SELF withExposedPorts(Integer... ports);
    public SELF withEnv(String key, String value);
    public SELF withCommand(String... command);
    public SELF withNetwork(Network network);
    public SELF withNetworkAliases(String... aliases);
    public SELF waitingFor(WaitStrategy waitStrategy);

    // Lifecycle methods
    public void start();  // Throws: ContainerLaunchException
    public void stop();
    public void close();

    // Runtime access methods
    public String getHost();
    public Integer getMappedPort(int originalPort);  // Throws: IllegalArgumentException if port not exposed
    public boolean isRunning();
}

Key Parameters:

  • withStartupTimeout(Duration) - Default: 60 seconds. Maximum time to wait for container startup.
  • withExposedPorts(Integer...) - No default. Must specify ports to access from host.
  • withEnv(String, String) - Environment variables. String values only; no default.

Common Pitfalls:

  • Forgetting to expose ports before calling getMappedPort() - throws IllegalArgumentException
  • Not setting appropriate startup timeout for slow-starting containers - causes TimeoutException
  • Reusing container instances across tests without proper cleanup - causes port conflicts

Container Lifecycle and Configuration

Wait Strategies

Comprehensive strategies for determining when containers are ready for testing. The Wait factory class provides convenient methods for creating common wait strategies.

public class Wait {
    public static WaitStrategy defaultWaitStrategy();
    public static HostPortWaitStrategy forListeningPort();
    public static HostPortWaitStrategy forListeningPorts(int... ports);
    public static HttpWaitStrategy forHttp(String path);
    public static HttpWaitStrategy forHttps(String path);
    public static LogMessageWaitStrategy forLogMessage(String regex, int times);
    public static DockerHealthcheckWaitStrategy forHealthcheck();
    public static ShellStrategy forSuccessfulCommand(String command);
}

public interface WaitStrategy {
    void waitUntilReady(WaitStrategyTarget target);  // Throws: ContainerLaunchException
    WaitStrategy withStartupTimeout(Duration timeout);
}

Decision Guide - Which Wait Strategy to Use:

Container TypeRecommended StrategyReason
Database (PostgreSQL, MySQL)Wait.forListeningPort()Fast, reliable port check
Web applicationWait.forHttp("/health").forStatusCode(200)Ensures app is responding correctly
Message broker (Kafka, RabbitMQ)Wait.forLogMessage(".*started.*", 1)Waits for specific startup message
Image with HEALTHCHECKWait.forHealthcheck()Uses Docker's built-in health check
Multiple conditionsnew WaitAllStrategy().withStrategy(...)Combines multiple checks

Wait Strategies

Image Management

Image pull policies and programmatic image building for Testcontainers. Supports remote Docker images, building from Dockerfiles, and Jib integration.

public class RemoteDockerImage extends LazyFuture<String> {
    public RemoteDockerImage(DockerImageName imageName);
}

public class ImageFromDockerfile extends LazyFuture<String> {
    public ImageFromDockerfile();
    /**
     * @param imageTag custom tag for the built image
     * @param deleteOnExit true to delete image after JVM exits, false to keep it
     */
    public ImageFromDockerfile(String imageTag, boolean deleteOnExit);
    public ImageFromDockerfile withDockerfilePath(String path);
    public ImageFromDockerfile withFileFromPath(String containerPath, Path hostPath);
    public ImageFromDockerfile withBuildArg(String key, String value);
}

public interface ImagePullPolicy {
    boolean shouldPull(DockerImageName imageName);
}

public class PullPolicy {
    public static ImagePullPolicy defaultPolicy();      // Pulls if missing or untagged
    public static ImagePullPolicy alwaysPull();         // Always pulls latest
    public static ImagePullPolicy ageBased(Duration maxAge);  // Pulls if older than maxAge
}

Performance Tips:

  • Use specific image tags (e.g., postgres:15.2) instead of latest to enable caching
  • Set PullPolicy.defaultPolicy() for CI environments to avoid unnecessary pulls
  • Use ImageFromDockerfile with caching for faster test iterations
  • Consider withReuse(true) for development (requires testcontainers.reuse.enable=true)

Image Management

Image Pull Policies

Image Building

Programmatic Docker image building with fluent Dockerfile construction. Build custom images from Dockerfiles with full control over build context, arguments, and layering.

/**
 * Build images from Dockerfiles with full control.
 */
public class ImageFromDockerfile extends LazyFuture<String> {
    public ImageFromDockerfile();
    public ImageFromDockerfile(String imageTag, boolean deleteOnExit);
    public ImageFromDockerfile withDockerfile(Path dockerfile);
    public ImageFromDockerfile withDockerfileFromBuilder(Consumer<DockerfileBuilder> builderConsumer);
    public ImageFromDockerfile withFileFromPath(String containerPath, Path hostPath);
    public ImageFromDockerfile withFileFromClasspath(String containerPath, String resourcePath);
}

/**
 * Fluent builder for constructing Dockerfiles programmatically.
 */
public class DockerfileBuilder {
    public DockerfileBuilder from(String baseImage);
    public DockerfileBuilder run(String... command);
    public DockerfileBuilder cmd(String... command);
    public DockerfileBuilder expose(int... ports);
    public DockerfileBuilder env(String key, String value);
    public DockerfileBuilder add(String source, String dest);
    public DockerfileBuilder copy(String source, String dest);
    public DockerfileBuilder workdir(String workdir);
    public String build();
}

Key Features:

  • Programmatic Dockerfile construction
  • Build context management (files, classpath resources)
  • Build arguments and caching
  • Integration with Jib for optimized builds

Image Building

Startup Check Strategies

Container startup validation strategies for one-shot and long-running containers. Different strategies for verifying that containers have started successfully and are ready for testing.

/**
 * Base class for startup validation strategies.
 */
public abstract class StartupCheckStrategy {
    public abstract StartupStatus checkStartupState(DockerClient dockerClient, String containerId);

    public enum StartupStatus {
        SUCCESSFUL,   // Container started successfully
        FAILED,       // Container failed to start
        NOT_YET_KNOWN // Status not yet determined
    }
}

/**
 * Common startup check implementations.
 */
public class IsRunningStartupCheckStrategy extends StartupCheckStrategy;
public class OneShotStartupCheckStrategy extends StartupCheckStrategy;
public class MinimumDurationRunningStartupCheckStrategy extends StartupCheckStrategy;

When to Use:

  • One-shot containers that exit after completing work
  • Containers that need to run for a minimum duration before being considered started
  • Custom validation logic for container startup

Startup Check Strategies

Network Configuration

Docker network creation and management for container communication. Networks enable containers to communicate using network aliases and service discovery.

public interface Network extends AutoCloseable {
    static Network newNetwork();
    static NetworkImpl.NetworkImplBuilder builder();
    String getId();
    void close();

    Network SHARED; // Shared network instance (lifecycle managed by ResourceReaper, cannot be closed by user code)
}

public static class NetworkImpl implements Network {
    /**
     * Builder for creating custom Docker networks with advanced configuration.
     * Generated by Lombok @Builder annotation.
     */
    public static class NetworkImplBuilder {
        /**
         * Set the network driver (e.g., "bridge", "overlay", "host").
         * Default: "bridge"
         */
        public NetworkImplBuilder driver(String driver);

        /**
         * Enable or disable IPv6 networking.
         * null = default (disabled), true = enabled, false = explicitly disabled
         */
        public NetworkImplBuilder enableIpv6(Boolean ipv6);

        /**
         * Add a single modifier to customize the CreateNetworkCmd.
         * Lombok @Singular generates both singular and plural methods.
         */
        public NetworkImplBuilder createNetworkCmdModifier(Consumer<CreateNetworkCmd> modifier);

        /**
         * Add multiple modifiers to customize the CreateNetworkCmd.
         */
        public NetworkImplBuilder createNetworkCmdModifiers(Collection<Consumer<CreateNetworkCmd>> modifiers);

        /**
         * Build the network. Network is created lazily on first getId() call.
         */
        public NetworkImpl build();
    }
}

When to Use Networks:

  • Multiple containers need to communicate by name (service discovery)
  • Testing microservices or distributed systems
  • Isolating test environments from each other

Example:

try (Network network = Network.newNetwork()) {
    GenericContainer<?> db = new GenericContainer<>(DockerImageName.parse("postgres:15"))
        .withNetwork(network)
        .withNetworkAliases("database");  // Other containers can reach at "database:5432"

    GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
        .withNetwork(network)
        .withEnv("DB_HOST", "database")  // Use network alias
        .dependsOn(db);
}

Network Configuration

Output Handling

Log and output stream management for containers. Provides multiple consumer implementations for capturing and processing container output.

public class OutputFrame {
    public String getUtf8String();
    public byte[] getBytes();
    public OutputType getType();

    public enum OutputType { STDOUT, STDERR, END }

    // Static field for END frame marker
    public static final OutputFrame END;
}

public class Slf4jLogConsumer extends BaseConsumer<Slf4jLogConsumer> {
    public Slf4jLogConsumer(Logger logger);
}

public class ToStringConsumer extends BaseConsumer<ToStringConsumer> {
    public String toUtf8String();
}

public class WaitingConsumer extends BaseConsumer<WaitingConsumer> {
    public void waitUntil(Predicate<OutputFrame> predicate, long limit, TimeUnit limitUnit)
        throws TimeoutException;
    public void waitUntilEnd();
}

Output Handling

Docker Compose Support

Launch and manage multi-container Docker Compose environments for integration testing. Supports both legacy and modern Docker Compose APIs.

public class DockerComposeContainer<SELF extends DockerComposeContainer<SELF>>
    implements Startable {

    public SELF withExposedService(String serviceName, int servicePort);
    public SELF withExposedService(String serviceName, int servicePort, WaitStrategy waitStrategy);
    public SELF withScaledService(String serviceName, int instances);
    public SELF withEnv(String key, String value);
    public String getServiceHost(String serviceName, int servicePort);
    public Integer getServicePort(String serviceName, int servicePort);
    public void start();  // Throws: ContainerLaunchException
    public void stop();
}

Docker Compose

Configuration Management

Global configuration and customization of Testcontainers behavior. Configure Docker client strategies, image pull policies, timeout values, container reuse, and more through properties files, environment variables, or programmatic access.

import org.testcontainers.utility.TestcontainersConfiguration;

/**
 * Singleton for accessing Testcontainers configuration.
 * Configuration sources (highest to lowest precedence):
 * 1. Environment variables (prefixed with TESTCONTAINERS_)
 * 2. User properties (~/.testcontainers.properties)
 * 3. Classpath properties (testcontainers.properties)
 * 4. Default values
 */
public class TestcontainersConfiguration {
    public static TestcontainersConfiguration getInstance();
    public String getEnvVarOrProperty(String propertyName, String defaultValue);
    public String getEnvVarOrUserProperty(String propertyName, String defaultValue);
    public String getUserProperty(String propertyName, String defaultValue);
    public boolean updateUserConfig(String prop, String value);

    // Feature flags
    public boolean isDisableChecks();
    public boolean environmentSupportsReuse();

    // Docker client configuration
    public String getDockerClientStrategyClassName();
    public String getTransportType();

    // Image management
    public String getImageSubstitutorClassName();
    public String getImagePullPolicy();
    public Integer getImagePullTimeout();

    // Ryuk configuration
    public boolean isRyukPrivileged();
    public Integer getRyukTimeout();
}

Common Configuration:

# ~/.testcontainers.properties

# Enable container reuse (development mode)
testcontainers.reuse.enable=true

# Docker client strategy
docker.client.strategy=org.testcontainers.dockerclient.UnixSocketClientProviderStrategy

# Image pull policy
pull.policy=default
pull.timeout=300

# Ryuk (resource cleanup)
ryuk.container.privileged=false
ryuk.container.timeout=60

Usage Example:

import org.testcontainers.utility.TestcontainersConfiguration;

TestcontainersConfiguration config = TestcontainersConfiguration.getInstance();

// Check if reuse is enabled
if (config.environmentSupportsReuse()) {
    System.out.println("Container reuse is enabled");
}

// Update configuration
config.updateUserConfig("testcontainers.reuse.enable", "true");

Configuration Management

Specialized Container Types

Beyond the core GenericContainer and DockerComposeContainer, Testcontainers provides specialized container implementations for advanced testing scenarios:

DockerMcpGatewayContainer

Container for running Docker MCP (Model Context Protocol) Gateway, enabling AI model integration and testing scenarios.

import org.testcontainers.containers.DockerMcpGatewayContainer;
import org.testcontainers.utility.DockerImageName;

/**
 * Docker MCP Gateway container for AI model testing.
 * Supported image: docker/mcp-gateway
 * Exposed port: 8811
 */
public class DockerMcpGatewayContainer extends GenericContainer<DockerMcpGatewayContainer> {
    public DockerMcpGatewayContainer(String dockerImageName);
    public DockerMcpGatewayContainer(DockerImageName dockerImageName);

    /**
     * Add an MCP server with its tools.
     * @param server the MCP server URL
     * @param tools list of tool names to enable
     * @return this container for method chaining
     */
    public DockerMcpGatewayContainer withServer(String server, List<String> tools);
    public DockerMcpGatewayContainer withServer(String server, String... tools);

    /**
     * Add secrets for MCP server authentication.
     * @param secrets map of secret key-value pairs
     * @return this container for method chaining
     */
    public DockerMcpGatewayContainer withSecrets(Map<String, String> secrets);
    public DockerMcpGatewayContainer withSecret(String secretKey, String secretValue);

    /**
     * Get the HTTP endpoint for the MCP Gateway.
     * @return the gateway endpoint URL
     */
    public String getEndpoint();
}

Usage Example:

import org.testcontainers.containers.DockerMcpGatewayContainer;
import org.testcontainers.utility.DockerImageName;
import java.util.Map;

DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer(
    DockerImageName.parse("docker/mcp-gateway:latest")
)
    .withServer("https://mcp-server.example.com", "tool1", "tool2")
    .withSecret("API_KEY", "your-api-key")
    .withSecrets(Map.of(
        "SECRET_1", "value1",
        "SECRET_2", "value2"
    ));

gateway.start();

String endpoint = gateway.getEndpoint();
// Use endpoint to connect to MCP Gateway: http://host:8811

DockerModelRunnerContainer

Proxy container for Docker Model Runner service, enabling local AI model testing with OpenAI-compatible API.

import org.testcontainers.containers.DockerModelRunnerContainer;
import org.testcontainers.utility.DockerImageName;

/**
 * Proxy container for Docker Model Runner (Docker Desktop).
 * Extends SocatContainer to proxy to model-runner.docker.internal.
 * Supported image: alpine/socat
 * Exposed port: 80
 */
public class DockerModelRunnerContainer extends SocatContainer {
    public DockerModelRunnerContainer(String image);
    public DockerModelRunnerContainer(DockerImageName image);

    /**
     * Specify which AI model to pull and use.
     * Model is pulled automatically after container starts.
     * @param model model name (e.g., "llama3.2:1b", "phi3")
     * @return this container for method chaining
     */
    public DockerModelRunnerContainer withModel(String model);

    /**
     * Get the base endpoint for model runner API.
     * @return the base URL
     */
    public String getBaseEndpoint();

    /**
     * Get the OpenAI-compatible endpoint.
     * @return the OpenAI API endpoint URL
     */
    public String getOpenAIEndpoint();
}

Usage Example:

import org.testcontainers.containers.DockerModelRunnerContainer;
import org.testcontainers.utility.DockerImageName;

// Create model runner proxy for local AI testing
DockerModelRunnerContainer modelRunner = new DockerModelRunnerContainer(
    DockerImageName.parse("alpine/socat:latest")
)
    .withModel("llama3.2:1b");  // Automatically pulls model on startup

modelRunner.start();

String baseUrl = modelRunner.getBaseEndpoint();
String openAIUrl = modelRunner.getOpenAIEndpoint();

// Use OpenAI-compatible API
// POST to openAIUrl + "/chat/completions"

SocatContainer

Network proxy/relay container for advanced networking scenarios and port forwarding.

import org.testcontainers.containers.SocatContainer;

/**
 * Socat container for network proxying and port forwarding.
 * Useful for connecting containers to external services.
 */
public class SocatContainer extends GenericContainer<SocatContainer> {
    public SocatContainer withTarget(int port, String target);
}

VncRecordingContainer

Captures screen recordings during test execution for GUI testing and debugging.

import org.testcontainers.containers.VncRecordingContainer;

/**
 * VNC recording container for capturing GUI test sessions.
 * Useful for debugging Selenium tests and visual regression testing.
 */
public class VncRecordingContainer extends GenericContainer<VncRecordingContainer> {
    // Start/stop recording methods
}

PortForwardingContainer

Singleton enum for port forwarding between containers and host. Used internally by Testcontainers.exposeHostPorts().

Note: These specialized containers are available in the core Testcontainers library. For database-specific containers (MySQL, PostgreSQL, MongoDB, etc.), check the Testcontainers modules documentation for dedicated module implementations with database-specific APIs.

Docker Client Management

Docker client factory and provider strategies for connecting to Docker hosts. Automatically detects and configures the appropriate Docker client based on the environment.

public class DockerClientFactory {
    public static DockerClientFactory instance();
    public DockerClient client();
    public boolean isDockerAvailable();
    public String dockerHostIpAddress();
    public String getActiveApiVersion();

    public static final String TESTCONTAINERS_LABEL;
    public static final String SESSION_ID;
}

Troubleshooting:

  • If isDockerAvailable() returns false, ensure Docker daemon is running
  • Check ~/.testcontainers.properties for custom Docker host configuration
  • Verify DOCKER_HOST environment variable if using remote Docker

Docker Client Management

Utility Classes

Essential utility classes for working with Docker images, files, and configuration. These classes provide type-safe abstractions for common Docker operations.

public class DockerImageName {
    public static DockerImageName parse(String fullImageName);  // Throws: IllegalArgumentException
    public DockerImageName withTag(String tag);
    public DockerImageName withRegistry(String registry);
    public String asCanonicalNameString();
    public boolean isCompatibleWith(DockerImageName other);
}

public class MountableFile implements Transferable {
    public static MountableFile forHostPath(String path);
    public static MountableFile forHostPath(Path path);
    public static MountableFile forClasspathResource(String resourcePath);
    public String getResolvedPath();
}

// Import: org.testcontainers.images.builder.Transferable
public interface Transferable {
    static Transferable of(byte[] bytes);
    static Transferable of(byte[], int fileMode);
    static Transferable of(String content);
    static Transferable of(String content, int fileMode);
    void transferTo(TarArchiveOutputStream outputStream, String destinationPath);
}

Utility Classes

Common Types

Container State Interface

public interface ContainerState {
    boolean isRunning();
    boolean isCreated();
    boolean isHealthy();
    String getHost();
    String getContainerId();
    Integer getMappedPort(int originalPort);  // Throws: IllegalArgumentException if port not exposed
    Integer getFirstMappedPort();
    List<Integer> getExposedPorts();
    String getLogs();
    String getLogs(OutputFrame.OutputType... types);
    Container.ExecResult execInContainer(String... command) throws Exception;
    void copyFileToContainer(Transferable transferable, String containerPath);
    void copyFileFromContainer(String containerPath, String destinationPath);
}

Exec Result

Result of executing a command in a container. Nested class within Container interface.

/**
 * Result of executing a command inside a container.
 * Nested class: Container.ExecResult
 */
public static class Container.ExecResult {
    public String getStdout();
    public String getStderr();
    public int getExitCode();
}

Lifecycle Interface

public interface Startable extends AutoCloseable {
    void start();  // Throws: ContainerLaunchException
    void stop();
    default Set<Startable> getDependencies() {
        return Collections.emptySet();  // Default: no dependencies
    }
    void close(); // Alias for stop()
}

Enums

public enum BindMode {
    READ_ONLY,
    READ_WRITE
}

/**
 * IP protocols supported by Docker for port bindings.
 * Used when specifying port protocol in container configuration.
 */
public enum InternetProtocol {
    TCP,  // Transmission Control Protocol
    UDP   // User Datagram Protocol
}

/**
 * SELinux context modes for volume binds.
 * Used with bind mounts to control SELinux labeling.
 */
public enum SelinuxContext {
    SHARED,  // :z - Content is shared among multiple containers
    SINGLE,  // :Z - Content is private and unshared (relabeled for single container)
    NONE     // No SELinux labeling
}

Annotations

/**
 * Marks that the annotated API is subject to change and should not be
 * considered a stable API. Methods, classes, or fields marked with this
 * annotation may change in future versions without following semantic
 * versioning guarantees.
 *
 * When using APIs marked with @UnstableAPI, be prepared to adapt your
 * code during library upgrades.
 */
@Retention(RetentionPolicy.SOURCE)
@Target({ ElementType.TYPE, ElementType.METHOD, ElementType.FIELD })
@Documented
public @interface UnstableAPI {
}

Exceptions

Common exceptions thrown by Testcontainers operations.

/**
 * Exception thrown when a container fails to start or become ready.
 * This is the primary exception for container lifecycle failures.
 */
public class ContainerLaunchException extends RuntimeException {
    public ContainerLaunchException(String message);
    public ContainerLaunchException(String message, Throwable cause);
}

/**
 * Exception thrown when Docker image fetch/pull operations fail.
 * Typically indicates network issues or invalid image names.
 */
public class ContainerFetchException extends RuntimeException {
    public ContainerFetchException(String message);
    public ContainerFetchException(String message, Throwable cause);
}

Common Causes and Solutions:

ContainerLaunchException:

  • Cause: Wait strategy timeout - container started but didn't become ready within timeout

    • Solution: Increase timeout or adjust wait strategy
    • Example: container.withStartupTimeout(Duration.ofMinutes(2))
  • Cause: Container crashed immediately after starting

    • Solution: Check logs with container.getLogs(), verify command and configuration
  • Cause: Port not exposed before calling getMappedPort()

    • Solution: Add .withExposedPorts(port) before starting container
  • Cause: Invalid wait strategy configuration

    • Solution: Ensure wait strategy matches container capabilities

ContainerFetchException:

  • Cause: Image not found in registry

    • Solution: Verify image name and tag are correct
  • Cause: Network connectivity issues

    • Solution: Check Docker daemon connectivity and network settings
  • Cause: Authentication required for private registry

    • Solution: Configure Docker with appropriate credentials

Exception Handling Example:

import org.testcontainers.containers.ContainerLaunchException;
import org.testcontainers.containers.ContainerFetchException;

try {
    GenericContainer<?> container = new GenericContainer<>("myapp:latest")
        .withExposedPorts(8080)
        .waitingFor(Wait.forHttp("/health").forStatusCode(200));

    container.start();

} catch (ContainerFetchException e) {
    // Image pull failed
    System.err.println("Failed to fetch image: " + e.getMessage());
    // Check image name, registry availability, credentials

} catch (ContainerLaunchException e) {
    // Container failed to start or become ready
    System.err.println("Container failed to launch: " + e.getMessage());

    if (e.getCause() instanceof TimeoutException) {
        // Wait strategy timeout
        System.err.println("Container didn't become ready in time");
        System.err.println("Logs: " + container.getLogs());
    } else {
        // Other launch failure
        System.err.println("Check container configuration and logs");
    }
}

Common Patterns

Pattern: Test with Database

@Test
public void testWithDatabase() {
    try (GenericContainer<?> postgres = new GenericContainer<>(DockerImageName.parse("postgres:15"))
            .withExposedPorts(5432)
            .withEnv("POSTGRES_PASSWORD", "test")
            .withEnv("POSTGRES_DB", "testdb")
            .waitingFor(Wait.forListeningPort())
            .withStartupTimeout(Duration.ofSeconds(60))) {

        postgres.start();

        String jdbcUrl = String.format("jdbc:postgresql://%s:%d/testdb",
            postgres.getHost(), postgres.getMappedPort(5432));

        // Run database tests
    }
}

Pattern: Multi-Container with Network

@Test
public void testMultiContainer() {
    try (Network network = Network.newNetwork()) {
        GenericContainer<?> db = new GenericContainer<>(DockerImageName.parse("postgres:15"))
            .withNetwork(network)
            .withNetworkAliases("db")
            .withEnv("POSTGRES_PASSWORD", "secret")
            .withExposedPorts(5432)
            .waitingFor(Wait.forListeningPort());

        GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
            .withNetwork(network)
            .withEnv("DB_URL", "jdbc:postgresql://db:5432/postgres")
            .withExposedPorts(8080)
            .dependsOn(db)
            .waitingFor(Wait.forHttp("/health").forStatusCode(200));

        app.start();  // Starts db first due to dependsOn()

        String appUrl = String.format("http://%s:%d",
            app.getHost(), app.getMappedPort(8080));
        // Run tests
    }
}

Pattern: Error Handling and Debugging

@Test
public void testWithErrorHandling() {
    GenericContainer<?> container = null;
    try {
        container = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
            .withExposedPorts(8080)
            .withLogConsumer(new Slf4jLogConsumer(LoggerFactory.getLogger("container")))
            .waitingFor(Wait.forHttp("/health").forStatusCode(200))
            .withStartupTimeout(Duration.ofMinutes(2));

        container.start();

        // Run tests

    } catch (ContainerLaunchException e) {
        if (container != null) {
            System.err.println("Container failed to start. Logs:");
            System.err.println(container.getLogs());
        }
        throw e;
    } catch (TimeoutException e) {
        System.err.println("Container startup timed out. Check wait strategy.");
        throw e;
    } finally {
        if (container != null) {
            container.stop();
        }
    }
}

Troubleshooting

Common Issues

IssueCauseSolution
ContainerLaunchExceptionContainer failed to startCheck container.getLogs() for error messages
TimeoutExceptionContainer didn't become readyIncrease timeout or adjust wait strategy
IllegalArgumentException on getMappedPort()Port not exposedCall .withExposedPorts() before starting
Port conflictContainer using fixed port already in useUse dynamic ports (default) instead of withFixedExposedPort()
Image pull failsNetwork issue or invalid image nameCheck Docker connectivity and image name
Container stops immediatelyIncorrect command or missing dependenciesCheck logs and ensure image is configured correctly

Debug Mode

Enable debug logging to troubleshoot issues:

# In ~/.testcontainers.properties
testcontainers.diagnostics.enabled=true

Or programmatically:

// Capture all container output
container.withLogConsumer(new Slf4jLogConsumer(logger).withSeparateOutputStreams());

Performance Tips

  1. Use specific image tags instead of latest to enable Docker layer caching
  2. Enable container reuse in development (testcontainers.reuse.enable=true + .withReuse(true))
  3. Use shared networks when possible to reduce resource overhead
  4. Set appropriate timeouts - balance between fast failure and reliable startup
  5. Clean up resources - always use try-with-resources or proper cleanup
  6. Parallel test execution - containers are isolated, tests can run in parallel
  7. Pull images once - use PullPolicy.defaultPolicy() to cache pulled images