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")
);