Java library for integration testing with Docker containers
Core container management functionality for creating, configuring, starting, and managing Docker containers in tests. The GenericContainer class serves as the foundation of Testcontainers, providing a fluent API for all container operations.
The main workhorse class providing complete container lifecycle management with a fluent configuration API.
/**
* Base class for all Testcontainers. Provides fluent API for container configuration
* and lifecycle management.
*
* @param <SELF> Self-referential generic type for type-safe method chaining in subclasses
*/
public class GenericContainer<SELF extends GenericContainer<SELF>>
implements Container<SELF>, AutoCloseable, WaitStrategyTarget, Startable {
// Constructors
public GenericContainer(DockerImageName dockerImageName);
public GenericContainer(RemoteDockerImage image);
public GenericContainer(String dockerImageName);
public GenericContainer(Future<String> image);
@Deprecated
public GenericContainer(); // No-args constructor (deprecated)
// Constants
/** Default timeout in seconds for waiting for container to be in running state */
public static final int CONTAINER_RUNNING_TIMEOUT_SEC = 30;
/** Hostname used to access host machine from within containers */
public static final String INTERNAL_HOST_HOSTNAME = "host.testcontainers.internal";
// Image configuration
public SELF withImagePullPolicy(ImagePullPolicy policy);
public void setDockerImageName(@NonNull String dockerImageName);
public void setImage(Future<String> image);
// Command configuration
public SELF withCommand(String... command);
public void setCommand(@NonNull String command);
public void setCommand(@NonNull String... commandParts);
public String[] getCommandParts();
public void setCommandParts(String[] commandParts);
public SELF withWorkingDirectory(String workingDirectory);
public void setWorkingDirectory(String workingDirectory);
public String getWorkingDirectory();
// Environment configuration
public SELF withEnv(String key, String value);
public SELF withEnv(Map<String, String> env);
public void addEnv(String key, String value);
public void setEnv(List<String> env);
public Map<String, String> getEnvMap();
@Deprecated
public List<String> getEnv();
public SELF withLabel(String key, String value);
public SELF withLabels(Map<String, String> labels);
public Map<String, String> getLabels();
public void setLabels(Map<String, String> labels);
// Port configuration
public SELF withExposedPorts(Integer... ports);
public void addExposedPort(Integer port);
public void addExposedPorts(int... ports);
public void setExposedPorts(List<Integer> exposedPorts);
public void setPortBindings(List<String> portBindings);
// Network configuration
public SELF withNetwork(Network network);
public Network getNetwork();
public void setNetwork(Network network);
public SELF withNetworkMode(String networkMode);
public String getNetworkMode();
public void setNetworkMode(String networkMode);
public SELF withNetworkAliases(String... aliases);
public List<String> getNetworkAliases();
public void setNetworkAliases(List<String> aliases);
public SELF withExtraHost(String hostname, String ipAddress);
public List<String> getExtraHosts();
public void setExtraHosts(List<String> extraHosts);
// Volume and file configuration
@Deprecated
public SELF withFileSystemBind(String hostPath, String containerPath);
@Deprecated
public SELF withFileSystemBind(String hostPath, String containerPath, BindMode mode);
@Deprecated
public void addFileSystemBind(String hostPath, String containerPath, BindMode mode);
@Deprecated
public void addFileSystemBind(String hostPath, String containerPath, BindMode mode, SelinuxContext selinuxContext);
public List<Bind> getBinds();
public void setBinds(List<Bind> binds);
public SELF withVolumesFrom(Container container, BindMode mode);
public SELF withCopyToContainer(Transferable transferable, String containerPath);
@Deprecated
public SELF withCopyFileToContainer(MountableFile mountableFile, String containerPath);
// Startup and wait configuration
public SELF waitingFor(WaitStrategy waitStrategy);
protected WaitStrategy getWaitStrategy();
public void setWaitStrategy(WaitStrategy waitStrategy);
public SELF withStartupTimeout(Duration timeout);
public SELF withStartupCheckStrategy(StartupCheckStrategy strategy);
public SELF withStartupAttempts(int attempts);
// Log configuration
public SELF withLogConsumer(Consumer<OutputFrame> logConsumer);
// Advanced configuration
public SELF withPrivilegedMode(boolean privilegedMode);
public void setPrivilegedMode(boolean mode);
public boolean isPrivilegedMode();
public SELF withAccessToHost(boolean accessToHost);
@UnstableAPI
public SELF withReuse(boolean reuse);
public SELF withCreateContainerCmdModifier(Consumer<CreateContainerCmd> modifier);
/**
* Configure the shared memory size for the container.
*
* @param bytes the size of shared memory in bytes
* (e.g., 512 * 1024 * 1024L for 512 MB)
* @return this container for method chaining
*/
public SELF withSharedMemorySize(Long bytes);
/**
* Configure temporary filesystems (tmpfs) mounted in the container.
*
* @param mapping a map where keys are mount paths and values are tmpfs options
* (e.g., "rw,noexec,nosuid,size=100m")
* Example: Map.of("/tmp", "rw,size=100m")
* @return this container for method chaining
*/
public SELF withTmpFs(Map<String, String> mapping);
public SELF withMinimumRunningDuration(Duration minimumRunningDuration);
@Deprecated
public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode);
@Deprecated
public SELF withClasspathResourceMapping(String resourcePath, String containerPath, BindMode mode, SelinuxContext selinuxContext);
// Dependencies
public SELF dependsOn(Startable... dependencies);
public SELF dependsOn(List<? extends Startable> dependencies);
public SELF dependsOn(Iterable<? extends Startable> dependencies);
// Lifecycle methods
public void start();
public void stop();
public void close(); // AutoCloseable
// Runtime access methods
public String getHost();
public Integer getMappedPort(int originalPort);
public Integer getFirstMappedPort();
public List<Integer> getExposedPorts();
public List<String> getPortBindings();
public List<Integer> getBoundPortNumbers();
public String getContainerId();
public InspectContainerResponse getContainerInfo();
public InspectContainerResponse getCurrentContainerInfo();
public String getDockerImageName();
public Future<String> getImage();
@Deprecated
public Map<String, LinkableContainer> getLinkedContainers();
@Deprecated
public void setLinkedContainers(Map<String, LinkableContainer> linkedContainers);
public DockerClient getDockerClient();
@Deprecated
public String getTestHostIpAddress();
public String getContainerName();
public boolean isRunning();
public boolean isCreated();
public boolean isHealthy();
public Set<Startable> getDependencies();
// Object methods
public boolean equals(Object o);
public int hashCode();
// Command execution
public Container.ExecResult execInContainer(String... command) throws Exception;
public Container.ExecResult execInContainer(Charset charset, String... command) throws Exception;
public Container.ExecResult execInContainer(ExecConfig execConfig) throws Exception;
public Container.ExecResult execInContainer(Charset charset, ExecConfig execConfig) throws Exception;
// File operations
public void copyFileToContainer(Transferable transferable, String containerPath);
public void copyFileFromContainer(String containerPath, String destinationPath);
// Log methods
public String getLogs();
public String getLogs(OutputFrame.OutputType... types);
public void followOutput(Consumer<OutputFrame> consumer);
public void followOutput(Consumer<OutputFrame> consumer, OutputFrame.OutputType... types);
// Lifecycle hooks (protected, for subclasses)
protected void configure();
protected void containerIsCreated(String containerId);
protected void containerIsStarting(InspectContainerResponse containerInfo);
protected void containerIsStarted(InspectContainerResponse containerInfo);
}Usage Examples:
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.images.RemoteDockerImage;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;
import java.time.Duration;
// Basic container
GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:7.0"))
.withExposedPorts(6379);
// Container with environment variables
GenericContainer<?> postgres = new GenericContainer<>(DockerImageName.parse("postgres:15"))
.withExposedPorts(5432)
.withEnv("POSTGRES_PASSWORD", "secret")
.withEnv("POSTGRES_DB", "testdb")
.withEnv("POSTGRES_USER", "testuser");
// Container with volume mounts
GenericContainer<?> nginx = new GenericContainer<>(DockerImageName.parse("nginx:alpine"))
.withExposedPorts(80)
.withFileSystemBind("/host/path/nginx.conf", "/etc/nginx/nginx.conf", BindMode.READ_ONLY)
.withCopyFileToContainer(
MountableFile.forClasspathResource("static/index.html"),
"/usr/share/nginx/html/index.html"
);
// Container with custom command and wait strategy
GenericContainer<?> alpine = new GenericContainer<>(DockerImageName.parse("alpine:latest"))
.withCommand("sh", "-c", "while true; do echo 'Hello'; sleep 5; done")
.waitingFor(Wait.forLogMessage(".*Hello.*", 1))
.withStartupTimeout(Duration.ofSeconds(30));
// Container with network
Network network = Network.newNetwork();
GenericContainer<?> database = new GenericContainer<>(DockerImageName.parse("postgres:15"))
.withNetwork(network)
.withNetworkAliases("db")
.withEnv("POSTGRES_PASSWORD", "secret");
GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
.withNetwork(network)
.withEnv("DB_HOST", "db")
.dependsOn(database);
// Container with advanced configuration
GenericContainer<?> advanced = new GenericContainer<>(DockerImageName.parse("memcached:alpine"))
.withExposedPorts(11211)
.withSharedMemorySize(512 * 1024 * 1024L) // 512 MB shared memory
.withTmpFs(Map.of("/tmp", "rw,noexec,nosuid,size=100m"))
.withExtraHost("myhost.local", "192.168.1.100")
.withMinimumRunningDuration(Duration.ofSeconds(5));
// Container with classpath resource mapping
GenericContainer<?> withConfig = new GenericContainer<>(DockerImageName.parse("nginx:alpine"))
.withExposedPorts(80)
.withClasspathResourceMapping("nginx.conf", "/etc/nginx/nginx.conf", BindMode.READ_ONLY)
.withClasspathResourceMapping("static/", "/usr/share/nginx/html/");
// Using container
try (GenericContainer<?> container = new GenericContainer<>("redis:7.0")
.withExposedPorts(6379)) {
container.start();
String host = container.getHost();
Integer port = container.getMappedPort(6379);
// Use container in tests
} // Automatically stopped and removedDeprecated interface for containers that can be linked. Use Network features instead.
/**
* A container which can be linked to by other containers.
*
* @deprecated Links are deprecated. Please use Network features instead.
*/
@Deprecated
public interface LinkableContainer {
/**
* @return the container name used for linking
*/
String getContainerName();
}Core interface defining the contract for all containers. Extends multiple interfaces for complete functionality.
/**
* Core container interface defining configuration and lifecycle methods.
*
* @param <SELF> Self-referential generic type for fluent API
*/
public interface Container<SELF extends Container<SELF>>
extends LinkableContainer, ContainerState {
// All configuration methods return SELF for method chaining
// See GenericContainer for full list of methods
/**
* Result of executing a command inside a container.
* Immutable value object containing command execution results.
* Generated by Lombok @Value annotation.
*/
@Value
@AllArgsConstructor(access = AccessLevel.MODULE)
public static class ExecResult {
/**
* Get the exit code from the command execution.
* Generated by Lombok @Value annotation from 'exitCode' field.
*
* @return exit code (0 indicates success, non-zero indicates failure)
*/
public int getExitCode();
/**
* Get the standard output from the command execution.
* Generated by Lombok @Value annotation from 'stdout' field.
*
* @return stdout as a string (decoded using UTF-8 or specified charset)
*/
public String getStdout();
/**
* Get the standard error from the command execution.
* Generated by Lombok @Value annotation from 'stderr' field.
*
* @return stderr as a string (decoded using UTF-8 or specified charset)
*/
public String getStderr();
}
}Interface representing the state and runtime information of a running container.
/**
* Provides access to container state and runtime information.
*/
public interface ContainerState {
/**
* @return true if the container is currently running
*/
boolean isRunning();
/**
* @return true if the container has been created
*/
boolean isCreated();
/**
* @return true if the container is healthy (based on Docker healthcheck)
*/
boolean isHealthy();
/**
* @return the host that this container is accessible from
*/
String getHost();
/**
* Get the IP address that this container may be reached on.
*
* @return an IP address
* @deprecated use {@link #getHost()} instead
*/
@Deprecated
String getContainerIpAddress();
/**
* @return the container ID
*/
String getContainerId();
/**
* @return container inspection information
*/
InspectContainerResponse getContainerInfo();
/**
* @return current container inspection information (refreshed)
*/
InspectContainerResponse getCurrentContainerInfo();
/**
* Get the mapped port on the Docker host for an exposed container port.
*
* @param originalPort the exposed port in the container
* @return the mapped port on the host
*/
Integer getMappedPort(int originalPort);
/**
* @return the first mapped port
*/
Integer getFirstMappedPort();
/**
* @return list of all exposed ports
*/
List<Integer> getExposedPorts();
/**
* @return list of port bindings
*/
List<String> getPortBindings();
/**
* Get the list of bound port numbers on the host.
*
* @return list of bound port numbers (host-side ports)
*/
List<Integer> getBoundPortNumbers();
/**
* Get all logs from the container.
*
* @return container logs as a string
*/
String getLogs();
/**
* Get logs from the container, filtered by output type.
*
* @param types the output types to include (STDOUT, STDERR)
* @return filtered logs as a string
*/
String getLogs(OutputFrame.OutputType... types);
/**
* Execute a command inside the running container.
*
* @param command the command to execute
* @return result containing stdout, stderr, and exit code
* @throws Exception if execution fails
*/
Container.ExecResult execInContainer(String... command) throws Exception;
/**
* Execute a command inside the running container with a specific charset.
*
* @param charset the charset for decoding output
* @param command the command to execute
* @return result containing stdout, stderr, and exit code
* @throws Exception if execution fails
*/
Container.ExecResult execInContainer(Charset charset, String... command) throws Exception;
/**
* Execute a command using ExecConfig for advanced configuration.
*
* @param execConfig the execution configuration
* @return result containing stdout, stderr, and exit code
* @throws Exception if execution fails
*/
Container.ExecResult execInContainer(ExecConfig execConfig) throws Exception;
/**
* Execute a command using ExecConfig with a specific charset.
*
* @param charset the charset for decoding output
* @param execConfig the execution configuration
* @return result containing stdout, stderr, and exit code
* @throws Exception if execution fails
*/
Container.ExecResult execInContainer(Charset charset, ExecConfig execConfig) throws Exception;
/**
* Execute a command inside the running container as a specific user.
*
* @deprecated Use ExecConfig with user parameter instead
* @param user the user to execute as
* @param command the command to execute
* @return result containing stdout, stderr, and exit code
* @throws UnsupportedOperationException if not supported
* @throws IOException if I/O error occurs
* @throws InterruptedException if execution is interrupted
*/
@Deprecated
default Container.ExecResult execInContainerWithUser(String user, String... command)
throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Execute a command inside the running container as a specific user with charset.
*
* @deprecated Use ExecConfig with user parameter instead
* @param charset the charset for decoding output
* @param user the user to execute as
* @param command the command to execute
* @return result containing stdout, stderr, and exit code
* @throws UnsupportedOperationException if not supported
* @throws IOException if I/O error occurs
* @throws InterruptedException if execution is interrupted
*/
@Deprecated
default Container.ExecResult execInContainerWithUser(Charset charset, String user, String... command)
throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Copy a file or directory to the container.
*
* @param transferable the content to copy
* @param containerPath the destination path in the container
*/
void copyFileToContainer(Transferable transferable, String containerPath);
/**
* Copy a file from the container to the host.
*
* @param containerPath the source path in the container
* @param destinationPath the destination path on the host
*/
void copyFileFromContainer(String containerPath, String destinationPath);
/**
* Copy a file from the container and process it with a function.
* Useful for streaming file contents without writing to disk.
*
* @param containerPath the source path in the container
* @param function function that processes the InputStream and returns result
* @param <T> the return type
* @return the result from the function
*/
<T> T copyFileFromContainer(String containerPath, ThrowingFunction<InputStream, T> function);
}Usage Examples:
// Execute commands in container
Container.ExecResult result = container.execInContainer("psql", "-U", "postgres", "-c", "SELECT 1");
System.out.println("Exit code: " + result.getExitCode());
System.out.println("Output: " + result.getStdout());
System.out.println("Error: " + result.getStderr());
// Copy files
container.copyFileToContainer(
Transferable.of("test content"),
"/tmp/test.txt"
);
container.copyFileFromContainer("/var/log/app.log", "./app.log");
// Check logs
String logs = container.getLogs();
String stdoutOnly = container.getLogs(OutputFrame.OutputType.STDOUT);
// Get connection info
String jdbcUrl = String.format("jdbc:postgresql://%s:%d/testdb",
container.getHost(),
container.getMappedPort(5432)
);Configuration for advanced command execution in containers with support for custom user, working directory, and environment variables.
/**
* Configuration for exec operations in containers.
* Uses Lombok @Builder pattern for construction.
* Provides a fluent API for configuring command execution.
*/
@Builder
@Getter
public class ExecConfig {
/**
* The command to run as an array of strings.
*/
private String[] command;
/**
* The user to run the exec process as.
* If not specified, runs as the container's default user.
*/
private String user;
/**
* Key-value pairs of environment variables for the exec process.
* These are added to the environment in addition to the container's environment.
*/
private Map<String, String> envVars;
/**
* The working directory for the exec process.
* If not specified, uses the container's WORKDIR.
*/
private String workDir;
/**
* Create a new builder for ExecConfig.
* Generated by Lombok @Builder annotation.
*
* @return ExecConfigBuilder instance
*/
public static ExecConfig.ExecConfigBuilder builder();
/**
* Get the command array.
* Generated by Lombok @Getter annotation.
*
* @return command array
*/
public String[] getCommand();
/**
* Get the user to run as.
* Generated by Lombok @Getter annotation.
*
* @return user name, or null if not set
*/
public String getUser();
/**
* Get the environment variables.
* Generated by Lombok @Getter annotation.
*
* @return map of environment variables, or null if not set
*/
public Map<String, String> getEnvVars();
/**
* Get the working directory.
* Generated by Lombok @Getter annotation.
*
* @return working directory path, or null if not set
*/
public String getWorkDir();
/**
* Builder class for ExecConfig.
* Generated by Lombok @Builder annotation.
*/
public static class ExecConfigBuilder {
/**
* Set the command to execute.
*
* @param command array of command and arguments
* @return this builder for chaining
*/
public ExecConfigBuilder command(String[] command);
/**
* Set the user to run the command as.
*
* @param user username or UID
* @return this builder for chaining
*/
public ExecConfigBuilder user(String user);
/**
* Set environment variables for the command.
*
* @param envVars map of variable names to values
* @return this builder for chaining
*/
public ExecConfigBuilder envVars(Map<String, String> envVars);
/**
* Set the working directory for the command.
*
* @param workDir absolute path to working directory
* @return this builder for chaining
*/
public ExecConfigBuilder workDir(String workDir);
/**
* Build the ExecConfig instance.
*
* @return configured ExecConfig
*/
public ExecConfig build();
}
}Usage Examples:
import org.testcontainers.containers.ExecConfig;
import org.testcontainers.containers.Container;
import java.util.Map;
// Basic command execution with user
ExecConfig config = ExecConfig.builder()
.command(new String[]{"psql", "-c", "SELECT 1"})
.user("postgres")
.build();
Container.ExecResult result = container.execInContainer(config);
// Execute with custom working directory and environment
ExecConfig advancedConfig = ExecConfig.builder()
.command(new String[]{"npm", "test"})
.workDir("/app")
.user("node")
.envVars(Map.of(
"NODE_ENV", "test",
"CI", "true"
))
.build();
Container.ExecResult testResult = container.execInContainer(advancedConfig);
System.out.println("Exit code: " + testResult.getExitCode());
System.out.println("Output: " + testResult.getStdout());
// Database operation with password in environment
ExecConfig dbConfig = ExecConfig.builder()
.command(new String[]{
"psql",
"-U", "testuser",
"-d", "testdb",
"-c", "CREATE TABLE test (id serial PRIMARY KEY);"
})
.envVars(Map.of("PGPASSWORD", "secret"))
.build();
container.execInContainer(dbConfig);Container with fixed host port bindings (not recommended for most use cases due to port conflicts).
/**
* Container that allows binding to fixed host ports.
* Warning: May cause port conflicts in concurrent test execution.
*
* @param <SELF> Self-referential generic type
*/
public class FixedHostPortGenericContainer<SELF extends FixedHostPortGenericContainer<SELF>>
extends GenericContainer<SELF> {
/**
* Create a fixed host port container with the specified image.
*
* @deprecated This class is deprecated due to port conflict issues in concurrent execution
* @param dockerImageName the Docker image name
*/
@Deprecated
public FixedHostPortGenericContainer(String dockerImageName);
/**
* Bind a container port to a fixed host port.
*
* @param hostPort the host port to bind to
* @param containerPort the container port to expose
* @return self for method chaining
*/
public SELF withFixedExposedPort(int hostPort, int containerPort);
/**
* Bind a container port to a fixed host port with protocol.
*
* @param hostPort the host port to bind to
* @param containerPort the container port to expose
* @param protocol the protocol (TCP or UDP)
* @return self for method chaining
*/
public SELF withFixedExposedPort(int hostPort, int containerPort, InternetProtocol protocol);
}Usage Example:
FixedHostPortGenericContainer<?> redis = new FixedHostPortGenericContainer<>(
DockerImageName.parse("redis:7.0"))
.withFixedExposedPort(6379, 6379); // Bind host:6379 to container:6379
redis.start();
// Container is accessible at localhost:6379Interface for resources that can be started and stopped, with dependency management.
/**
* Represents something that can be started and stopped, typically a container.
*/
public interface Startable extends AutoCloseable {
/**
* Start the resource.
*/
void start();
/**
* Stop the resource.
*/
void stop();
/**
* Get the dependencies that must be started before this resource.
* Default implementation returns an empty set.
*
* @return set of dependencies (default: empty set)
*/
default Set<Startable> getDependencies() {
return Collections.emptySet();
}
/**
* Close the resource (calls stop()).
*/
@Override
default void close() {
stop();
}
}Utility class for managing multiple Startable instances with dependency resolution.
/**
* Utilities for managing multiple Startable resources.
*/
public class Startables {
/**
* Start multiple resources, respecting their dependencies.
*
* @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:
Network network = Network.newNetwork();
GenericContainer<?> database = new GenericContainer<>(DockerImageName.parse("postgres:15"))
.withNetwork(network)
.withNetworkAliases("db");
GenericContainer<?> redis = new GenericContainer<>(DockerImageName.parse("redis:7.0"))
.withNetwork(network)
.withNetworkAliases("cache");
GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
.withNetwork(network)
.dependsOn(database, redis);
// Start all containers respecting dependencies
Startables.deepStart(app).join();
// All containers are now running with proper startup orderProtected methods in GenericContainer that can be overridden in subclasses to customize behavior.
/**
* Override this method to configure the container before creation.
* Called during start() before container is created.
*/
protected void configure() {
// Override in subclass to add configuration
}
/**
* Called immediately after the container is created.
*
* @param containerId the container ID
*/
protected void containerIsCreated(String containerId) {
// Override in subclass for post-creation logic
}
/**
* Called during container startup, before waiting for readiness.
*
* @param containerInfo container inspection information
*/
protected void containerIsStarting(InspectContainerResponse containerInfo) {
// Override in subclass for startup logic
}
/**
* Called after the container is started and ready.
*
* @param containerInfo container inspection information
*/
protected void containerIsStarted(InspectContainerResponse containerInfo) {
// Override in subclass for post-startup logic
}Usage Example:
public class PostgreSQLContainer extends GenericContainer<PostgreSQLContainer> {
public PostgreSQLContainer(String dockerImageName) {
super(dockerImageName);
}
@Override
protected void configure() {
// Called before container creation
withExposedPorts(5432);
withEnv("POSTGRES_PASSWORD", "test");
waitingFor(Wait.forListeningPort());
}
@Override
protected void containerIsStarted(InspectContainerResponse containerInfo) {
// Called after container is ready
String jdbcUrl = getJdbcUrl();
logger.info("PostgreSQL started at: " + jdbcUrl);
}
public String getJdbcUrl() {
return String.format("jdbc:postgresql://%s:%d/test",
getHost(), getMappedPort(5432));
}
}Testcontainers uses types from the docker-java library for Docker API interactions:
// From com.github.dockerjava.api.command
import com.github.dockerjava.api.command.CreateContainerCmd;
import com.github.dockerjava.api.command.InspectContainerResponse;
// From com.github.dockerjava.api
import com.github.dockerjava.api.DockerClient;/**
* Volume bind modes for mounting directories.
*/
public enum BindMode {
/** Read-only mount */
READ_ONLY,
/** Read-write mount */
READ_WRITE;
/**
* The underlying Docker Java API AccessMode.
* Can be used for direct docker-java API interactions.
*/
public final com.github.dockerjava.api.model.AccessMode accessMode;
}
/**
* Internet protocols for port bindings.
*/
public enum InternetProtocol {
/** TCP protocol */
TCP,
/** UDP protocol */
UDP;
/**
* Convert protocol to Docker notation (lowercase).
* @return Protocol name in lowercase (e.g., "tcp", "udp")
*/
public String toDockerNotation();
/**
* Parse protocol from Docker notation string.
* @param protocol Protocol string in Docker notation (case-insensitive)
* @return Corresponding InternetProtocol enum value
*/
public static InternetProtocol fromDockerNotation(String protocol);
}
/**
* SELinux context for volume mounts.
*/
public enum SelinuxContext {
/** Shared SELinux context */
SHARED,
/** Single/private SELinux context */
SINGLE,
/** No SELinux context */
NONE;
/**
* The underlying Docker Java API SELContext.
* Can be used for direct docker-java API interactions.
*/
public final com.github.dockerjava.api.model.SELContext selContext;
}Interface for containers to be aware of test lifecycle events, enabling integration with test frameworks.
/**
* Interface for containers that need to be notified of test lifecycle events.
* Useful for integrating with JUnit and other test frameworks.
*/
public interface TestLifecycleAware {
/**
* Called before a test starts.
*
* @param description the test description
*/
void beforeTest(TestDescription description);
/**
* Called after a test completes.
*
* @param description the test description
* @param throwable optional throwable if test failed
*/
void afterTest(TestDescription description, Optional<Throwable> throwable);
}Usage Example:
public class CustomContainer extends GenericContainer<CustomContainer>
implements TestLifecycleAware {
public CustomContainer(String imageName) {
super(imageName);
}
@Override
public void beforeTest(TestDescription description) {
// Called before each test
logger.info("Starting test: " + description.getTestId());
// Setup test-specific configuration
}
@Override
public void afterTest(TestDescription description, Optional<Throwable> throwable) {
// Called after each test
if (throwable.isPresent()) {
logger.error("Test failed: " + description.getTestId(), throwable.get());
// Capture logs or diagnostics
}
// Cleanup test-specific resources
}
}Describes a test for lifecycle callbacks.
/**
* Describes a test being executed.
*/
public interface TestDescription {
/**
* Get the unique identifier for the test.
*
* @return test identifier
*/
String getTestId();
/**
* Get a filesystem-friendly name for the test.
* Useful for creating test-specific files or directories.
*
* @return filesystem-friendly test name
*/
String getFilesystemFriendlyName();
}A container reference that may not have been launched yet. Used for container linking and references.
/**
* A container that may not have been launched yet.
* Used for container linking patterns and references.
*
* @since 2.0.0
*/
@Data
public class FutureContainer implements LinkableContainer {
private final String containerName;
/**
* Constructor with container name.
*
* @param containerName the name of the container
*/
public FutureContainer(String containerName);
/**
* Get the container name for linking.
*
* @return the container name
*/
public String getContainerName();
}Usage Example:
// Create a future reference to a container for linking
FutureContainer futureDb = new FutureContainer("my-database");
// Use in container configuration (for legacy linking)
GenericContainer<?> app = new GenericContainer<>("myapp:latest")
.withLink(futureDb, "db");Provides utility methods for executing commands in containers using the Docker exec API.
/**
* Provides utility methods for executing commands in containers.
* Utility class with static methods for exec operations.
*/
@UtilityClass
public class ExecInContainerPattern {
/**
* Run a command inside a running container, interpreting output as UTF-8.
*
* @param dockerClient the Docker client
* @param containerInfo the container info
* @param command the command to execute
* @return the execution result
* @throws UnsupportedOperationException if docker exec is not supported
* @throws IOException if there's a communication error
* @throws InterruptedException if interrupted while waiting
*/
public Container.ExecResult execInContainer(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Run a command inside a running container with custom charset.
*
* @param dockerClient the Docker client
* @param containerInfo the container info
* @param outputCharset the character set for output
* @param command the command to execute
* @return the execution result
* @throws UnsupportedOperationException if docker exec is not supported
* @throws IOException if there's a communication error
* @throws InterruptedException if interrupted while waiting
*/
public Container.ExecResult execInContainer(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
Charset outputCharset,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Run a command inside a running container with exec configuration.
*
* @param dockerClient the Docker client
* @param containerInfo the container info
* @param execConfig the exec configuration
* @return the execution result
* @throws UnsupportedOperationException if docker exec is not supported
* @throws IOException if there's a communication error
* @throws InterruptedException if interrupted while waiting
*/
public Container.ExecResult execInContainer(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
ExecConfig execConfig
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* Run a command inside a running container with exec configuration and custom charset.
*
* @param dockerClient the Docker client
* @param containerInfo the container info
* @param outputCharset the character set for output
* @param execConfig the exec configuration
* @return the execution result
* @throws UnsupportedOperationException if docker exec is not supported
* @throws IOException if there's a communication error
* @throws InterruptedException if interrupted while waiting
*/
public Container.ExecResult execInContainer(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
Charset outputCharset,
ExecConfig execConfig
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, String...)}
*/
@Deprecated
public Container.ExecResult execInContainer(
InspectContainerResponse containerInfo,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, String...)}
*/
@Deprecated
public Container.ExecResult execInContainer(
InspectContainerResponse containerInfo,
Charset outputCharset,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, ExecConfig)}
*/
@Deprecated
public Container.ExecResult execInContainerWithUser(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
String user,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
/**
* @deprecated use {@link #execInContainer(DockerClient, InspectContainerResponse, Charset, ExecConfig)}
*/
@Deprecated
public Container.ExecResult execInContainerWithUser(
DockerClient dockerClient,
InspectContainerResponse containerInfo,
Charset outputCharset,
String user,
String... command
) throws UnsupportedOperationException, IOException, InterruptedException;
}Usage Example:
// Execute command in a running container
DockerClient dockerClient = DockerClientFactory.instance().client();
InspectContainerResponse containerInfo = container.getContainerInfo();
// Simple command execution
Container.ExecResult result = ExecInContainerPattern.execInContainer(
dockerClient,
containerInfo,
"ls", "-la", "/app"
);
System.out.println("Exit code: " + result.getExitCode());
System.out.println("Output: " + result.getStdout());
// With custom configuration
ExecConfig config = ExecConfig.builder()
.command(new String[]{"bash", "-c", "echo $HOME"})
.user("appuser")
.workDir("/app")
.envVars(Map.of("CUSTOM_VAR", "value"))
.build();
Container.ExecResult customResult = ExecInContainerPattern.execInContainer(
dockerClient,
containerInfo,
config
);A TCP proxy container using socat, enabling any TCP port of another container to be exposed publicly.
/**
* A socat container used as a TCP proxy, enabling any TCP port of another container
* to be exposed publicly, even if that container does not make the port public itself.
*
* Uses the alpine/socat image.
*/
public class SocatContainer extends GenericContainer<SocatContainer> {
/**
* Create a socat container with default image.
*/
public SocatContainer();
/**
* Create a socat container with custom image.
*
* @param dockerImageName the Docker image name
*/
public SocatContainer(DockerImageName dockerImageName);
/**
* Configure target host and port, exposing on the same port.
*
* @param exposedPort the port to expose
* @param host the target host
* @return this container for method chaining
*/
public SocatContainer withTarget(int exposedPort, String host);
/**
* Configure target host and port with different internal port.
*
* @param exposedPort the port to expose externally
* @param host the target host
* @param internalPort the internal port on target host
* @return this container for method chaining
*/
public SocatContainer withTarget(int exposedPort, String host, int internalPort);
}Usage Example:
// Create a TCP proxy to expose a port from another container
Network network = Network.newNetwork();
GenericContainer<?> database = new GenericContainer<>("mysql:8")
.withNetwork(network)
.withNetworkAliases("mysql")
.withEnv("MYSQL_ROOT_PASSWORD", "secret");
// Use socat to proxy the MySQL port
SocatContainer proxy = new SocatContainer()
.withNetwork(network)
.withTarget(3306, "mysql", 3306);
database.start();
proxy.start();
// Now you can access MySQL through the proxy's exposed port
String jdbcUrl = String.format(
"jdbc:mysql://%s:%d/test",
proxy.getHost(),
proxy.getMappedPort(3306)
);A sidekick container for recording VNC screen output from another container.
/**
* Sidekick container with the sole purpose of recording the VNC screen output
* from another container. Useful for debugging UI tests.
*
* Uses the testcontainers/vnc-recorder image.
*/
@Getter
@ToString
public class VncRecordingContainer extends GenericContainer<VncRecordingContainer> {
public static final String DEFAULT_VNC_PASSWORD = "secret";
public static final int DEFAULT_VNC_PORT = 5900;
static final VncRecordingFormat DEFAULT_RECORDING_FORMAT = VncRecordingFormat.FLV;
/**
* Create a VNC recording container attached to another container.
*
* @param targetContainer the container to record (must have network alias)
* @throws IllegalStateException if target has no network alias
*/
public VncRecordingContainer(@NonNull GenericContainer<?> targetContainer);
/**
* Create a VNC recording container on a specific network.
*
* @param network the network
* @param targetNetworkAlias the network alias of target container
* @throws IllegalStateException if configuration is invalid
*/
public VncRecordingContainer(@NonNull Network network, @NonNull String targetNetworkAlias);
/**
* Set the VNC password.
*
* @param vncPassword the VNC password
* @return this container for method chaining
*/
public VncRecordingContainer withVncPassword(@NonNull String vncPassword);
/**
* Set the VNC port.
*
* @param vncPort the VNC port
* @return this container for method chaining
*/
public VncRecordingContainer withVncPort(int vncPort);
/**
* Set the video format.
*
* @param videoFormat the video format (FLV or MP4)
* @return this container for method chaining
*/
public VncRecordingContainer withVideoFormat(VncRecordingFormat videoFormat);
/**
* Set the frame rate.
*
* @param frameRate the frame rate
* @return this container for method chaining
*/
public VncRecordingContainer withFrameRate(int frameRate);
/**
* Stream the recording.
*
* @return input stream of the recording
* @throws Exception if streaming fails
*/
public InputStream streamRecording() throws Exception;
/**
* Save the recording to a file.
*
* @param file the output file
* @throws Exception if saving fails
*/
public void saveRecordingToFile(@NonNull File file) throws Exception;
/**
* Video format options for VNC recording.
*/
@RequiredArgsConstructor
public enum VncRecordingFormat {
/** FLV format */
FLV("flv"),
/** MP4 format */
MP4("mp4");
@Getter
private final String filenameExtension;
/**
* Re-encode the recording to this format.
* Abstract method implemented by each enum value.
*
* @param container the VNC recording container
* @param source the source recording path
* @return the re-encoded recording path
*/
abstract String reencodeRecording(VncRecordingContainer container, String source);
}
}Usage Example:
// Record VNC session from a browser container
Network network = Network.newNetwork();
BrowserWebDriverContainer<?> chrome = new BrowserWebDriverContainer<>()
.withNetwork(network)
.withNetworkAliases("chrome")
.withCapabilities(new ChromeOptions());
// Attach VNC recorder
VncRecordingContainer recorder = new VncRecordingContainer(chrome)
.withVncPassword("secret")
.withVideoFormat(VncRecordingContainer.VncRecordingFormat.MP4);
chrome.start();
recorder.start();
// Run your tests...
// ...
// Save the recording
recorder.saveRecordingToFile(new File("test-recording.mp4"));
recorder.stop();
chrome.stop();Container for the Docker MCP Gateway service.
/**
* Testcontainers implementation of the Docker MCP Gateway container.
*
* Supported images: docker/mcp-gateway
* Exposed ports: 8811
*/
public class DockerMcpGatewayContainer extends GenericContainer<DockerMcpGatewayContainer> {
/**
* Create an MCP Gateway container with image name.
*
* @param dockerImageName the Docker image name
*/
public DockerMcpGatewayContainer(String dockerImageName);
/**
* Create an MCP Gateway container with DockerImageName.
*
* @param dockerImageName the Docker image name
*/
public DockerMcpGatewayContainer(DockerImageName dockerImageName);
/**
* Add a server with tools.
*
* @param server the server URL
* @param tools the tools to enable
* @return this container for method chaining
*/
public DockerMcpGatewayContainer withServer(String server, List<String> tools);
/**
* Add a server with tools (varargs).
*
* @param server the server URL
* @param tools the tools to enable
* @return this container for method chaining
*/
public DockerMcpGatewayContainer withServer(String server, String... tools);
/**
* Add secrets for the gateway.
*
* @param secrets the secrets map
* @return this container for method chaining
*/
public DockerMcpGatewayContainer withSecrets(Map<String, String> secrets);
/**
* Add a single secret.
*
* @param secretKey the secret key
* @param secretValue the secret value
* @return this container for method chaining
*/
public DockerMcpGatewayContainer withSecret(String secretKey, String secretValue);
/**
* Get the gateway endpoint URL.
*
* @return the endpoint URL
*/
public String getEndpoint();
}Usage Example:
// Create MCP Gateway container
DockerMcpGatewayContainer gateway = new DockerMcpGatewayContainer("docker/mcp-gateway:latest")
.withServer("http://my-mcp-server:8080", "tool1", "tool2")
.withSecret("API_KEY", "secret-key-value");
gateway.start();
// Get the endpoint
String endpoint = gateway.getEndpoint();
System.out.println("MCP Gateway available at: " + endpoint);Proxy container for the Docker Model Runner service provided by Docker Desktop.
/**
* Testcontainers proxy container for the Docker Model Runner service
* provided by Docker Desktop.
*
* Supported images: alpine/socat
* Exposed ports: 80
*/
@Slf4j
public class DockerModelRunnerContainer extends SocatContainer {
/**
* Create a Model Runner container with image name.
*
* @param image the Docker image name
*/
public DockerModelRunnerContainer(String image);
/**
* Create a Model Runner container with DockerImageName.
*
* @param image the Docker image name
*/
public DockerModelRunnerContainer(DockerImageName image);
/**
* Set the model to pull and use.
*
* @param model the model name
* @return this container for method chaining
*/
public DockerModelRunnerContainer withModel(String model);
/**
* Get the base endpoint URL.
*
* @return the base endpoint URL
*/
public String getBaseEndpoint();
/**
* Get the OpenAI-compatible endpoint URL.
* Returns base endpoint + "/engines".
*
* @return the OpenAI endpoint URL (e.g., "http://host:port/engines")
*/
public String getOpenAIEndpoint();
}Usage Example:
// Create Model Runner container with a specific model
DockerModelRunnerContainer modelRunner = new DockerModelRunnerContainer("alpine/socat:latest")
.withModel("llama3.2:latest");
modelRunner.start();
// Get endpoints
String baseEndpoint = modelRunner.getBaseEndpoint();
String openAiEndpoint = modelRunner.getOpenAIEndpoint();
System.out.println("Model Runner base endpoint: " + baseEndpoint);
System.out.println("OpenAI-compatible endpoint: " + openAiEndpoint);
// Use the endpoint with your AI client
// ...Callback interface for customizing container creation commands. This is a key extensibility point in Testcontainers, loaded via Java ServiceLoader mechanism.
/**
* Callback interface that can be used to customize a CreateContainerCmd.
* Implementations can be loaded via ServiceLoader to automatically apply
* modifications to all containers.
*
* @since 2.0.0
*/
public interface CreateContainerCmdModifier {
/**
* Callback to modify a CreateContainerCmd instance.
*
* @param createContainerCmd the command to modify
* @return the modified command
*/
CreateContainerCmd modify(CreateContainerCmd createContainerCmd);
}Usage Example (Direct):
// Use in GenericContainer directly
GenericContainer<?> container = new GenericContainer<>("nginx:latest")
.withCreateContainerCmdModifier(cmd -> cmd
.withHostName("custom-hostname")
.withMacAddress("02:42:ac:11:00:02")
);Usage Example (ServiceLoader):
Create a file META-INF/services/org.testcontainers.core.CreateContainerCmdModifier:
com.example.MyCustomModifierImplement the modifier:
package com.example;
import com.github.dockerjava.api.command.CreateContainerCmd;
import org.testcontainers.core.CreateContainerCmdModifier;
public class MyCustomModifier implements CreateContainerCmdModifier {
@Override
public CreateContainerCmd modify(CreateContainerCmd cmd) {
// Add custom labels to all containers
Map<String, String> labels = new HashMap<>(cmd.getLabels());
labels.put("com.example.environment", "test");
labels.put("com.example.version", "1.0");
return cmd.withLabels(labels);
}
}Common Use Cases:
// Add security options
container.withCreateContainerCmdModifier(cmd ->
cmd.withSecurityOpts(List.of("no-new-privileges"))
);
// Configure resource limits via HostConfig
container.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig()
.withCpuShares(512L)
.withMemory(512 * 1024 * 1024L) // 512 MB
.withMemorySwap(1024 * 1024 * 1024L); // 1 GB
});
// Configure ulimits (file descriptors, processes, etc.)
container.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig().withUlimits(
new Ulimit("nofile", 1024L, 2048L), // File descriptors: soft 1024, hard 2048
new Ulimit("nproc", 512L, 1024L) // Max processes: soft 512, hard 1024
);
});
// Set capabilities
container.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig()
.withCapAdd(Capability.NET_ADMIN)
.withCapDrop(Capability.CHOWN);
});
// Configure cgroup parent
container.withCreateContainerCmdModifier(cmd -> {
cmd.getHostConfig()
.withCgroupParent("custom-cgroup");
});
// Configure DNS
container.withCreateContainerCmdModifier(cmd ->
cmd.withDns("8.8.8.8", "8.8.4.4")
);Install with Tessl CLI
npx tessl i tessl/maven-org-testcontainers--testcontainersdocs