Essential utility classes for working with Docker images, files, and configuration. These classes provide type-safe abstractions and helper methods for common Docker operations in Testcontainers.
Parse, validate, and manipulate Docker image names with full support for registries, repositories, and tags.
/**
* Represents a Docker image name with registry, repository, and tag components.
* Provides type-safe image name handling and compatibility declarations.
*/
public class DockerImageName {
/**
* Parse a Docker image name string (deprecated constructor).
*
* @param fullImageName image name
* @deprecated use {@link #parse(String)} instead
*/
@Deprecated
public DockerImageName(String fullImageName);
/**
* Parse a Docker image name with separate version (deprecated constructor).
*
* @param nameWithoutTag image name without tag
* @param version version tag
* @deprecated use {@link #parse(String)}.{@link #withTag(String)} instead
*/
@Deprecated
public DockerImageName(String nameWithoutTag, String version);
/**
* Parse a Docker image name string.
*
* @param fullImageName image name (e.g., "redis:7.0", "docker.io/library/postgres:15")
* @return parsed DockerImageName instance
* @throws IllegalArgumentException if image name is invalid
*/
public static DockerImageName parse(String fullImageName);
/**
* Set or change the image tag.
*
* @param tag new tag (e.g., "latest", "15.2-alpine")
* @return new DockerImageName with updated tag
*/
public DockerImageName withTag(String tag);
/**
* Set or change the registry.
*
* @param registry registry URL (e.g., "gcr.io", "my-registry.com:5000")
* @return new DockerImageName with updated registry
*/
public DockerImageName withRegistry(String registry);
/**
* Set or change the repository.
*
* @param repository repository name (e.g., "library/redis", "myorg/myapp")
* @return new DockerImageName with updated repository
*/
public DockerImageName withRepository(String repository);
/**
* Get the canonical string representation.
* Includes registry, repository, and tag.
*
* @return canonical image name string
*/
public String asCanonicalNameString();
/**
* Declare this image as a compatible substitute for another.
* Useful for custom/modified images that work as drop-in replacements.
* This allows compatibility checks to pass when using a custom image
* in place of a standard one (e.g., custom Redis image for redis:7.0).
*
* @param baseImage base image name string
* @return this image with compatibility declaration
*/
public DockerImageName asCompatibleSubstituteFor(String baseImage);
/**
* Declare this image as a compatible substitute for another.
* This method records that the current image can be used in place of
* the specified base image, enabling compatibility validation to succeed.
*
* @param baseImage base image
* @return this image with compatibility declaration
*/
public DockerImageName asCompatibleSubstituteFor(DockerImageName baseImage);
/**
* Check if this image is compatible with another image.
*
* @param other image to check compatibility with
* @return true if compatible
*/
public boolean isCompatibleWith(DockerImageName other);
/**
* Assert that this image is compatible with one or more other images.
* Throws exception if not compatible with any of them.
*
* @param anyOthers images to check compatibility with
* @throws IllegalStateException if not compatible
*/
public void assertCompatibleWith(DockerImageName... anyOthers);
/**
* Validate that this image name is properly formed.
*
* @throws IllegalArgumentException if invalid
*/
public void assertValid();
/**
* Get the image name without version/tag.
*
* @return unversioned part (registry + repository)
*/
public String getUnversionedPart();
/**
* Get the version/tag part.
*
* @return tag/version string
*/
public String getVersionPart();
/**
* Get the registry.
*
* @return registry string, or null if using default
*/
public String getRegistry();
/**
* Get the repository.
*
* @return repository string
*/
public String getRepository();
/**
* Returns a string representation of this Docker image name.
* Same as asCanonicalNameString().
*
* @return string representation of the image name
*/
public String toString();
}Usage Examples:
import org.testcontainers.utility.DockerImageName;
// Parse image names
DockerImageName redis = DockerImageName.parse("redis:7.0");
DockerImageName postgres = DockerImageName.parse("postgres:15-alpine");
DockerImageName custom = DockerImageName.parse("my-registry.com:5000/myapp:1.0.0");
// Modify image names
DockerImageName latest = redis.withTag("latest");
DockerImageName gcr = postgres.withRegistry("gcr.io");
// Get components
String tag = redis.getVersionPart(); // "7.0"
String repo = redis.getRepository(); // "redis"
String registry = redis.getRegistry(); // null (default)
String canonical = redis.asCanonicalNameString(); // "redis:7.0"
// Custom images as substitutes
DockerImageName customRedis = DockerImageName
.parse("my-company/redis-custom:1.0")
.asCompatibleSubstituteFor("redis:7.0");
// Check compatibility
if (customRedis.isCompatibleWith(DockerImageName.parse("redis:7.0"))) {
System.out.println("Custom Redis is compatible");
}
// Validate image names
try {
DockerImageName.parse("invalid::image").assertValid();
} catch (IllegalArgumentException e) {
System.err.println("Invalid image name: " + e.getMessage());
}
// Using with containers
GenericContainer<?> container = new GenericContainer<>(
DockerImageName.parse("postgres:15")
.withTag("15.2-alpine")
);
// Enterprise registry with authentication
DockerImageName enterpriseImage = DockerImageName
.parse("enterprise-registry.company.com:5000/internal/myapp:latest");Represents files that can be mounted or copied into containers from various sources.
/**
* Represents a file that can be mounted into a container.
* Supports files from host filesystem, classpath, and Docker resources.
* Implements Transferable for file transfer operations.
* Note: getFileMode() method is part of the Transferable interface.
*/
public class MountableFile implements Transferable {
/**
* Create a mountable file from a host filesystem path.
*
* @param path host filesystem path
* @return MountableFile instance
*/
public static MountableFile forHostPath(String path);
/**
* Create a mountable file from a host filesystem path with custom file mode.
*
* @param path host filesystem path
* @param mode Unix file permissions (e.g., 0755)
* @return MountableFile instance
*/
public static MountableFile forHostPath(String path, Integer mode);
/**
* Create a mountable file from a Path object.
*
* @param path Path object
* @return MountableFile instance
*/
public static MountableFile forHostPath(Path path);
/**
* Create a mountable file from a Path object with custom file mode.
*
* @param path Path object
* @param mode Unix file permissions (e.g., 0755)
* @return MountableFile instance
*/
public static MountableFile forHostPath(Path path, Integer mode);
/**
* Create a mountable file from a classpath resource.
*
* @param resourcePath classpath resource path
* @return MountableFile instance
*/
public static MountableFile forClasspathResource(String resourcePath);
/**
* Create a mountable file from a classpath resource with custom file mode.
*
* @param resourcePath classpath resource path
* @param mode Unix file permissions (e.g., 0755)
* @return MountableFile instance
*/
public static MountableFile forClasspathResource(String resourcePath, Integer mode);
/**
* Get the Unix file mode (permissions) for a path.
*
* @param path the file path
* @return Unix file permissions as an integer
*/
public static int getUnixFileMode(Path path);
/**
* Get the resolved absolute path to the file.
*
* @return resolved path string
*/
public String getResolvedPath();
/**
* Get the filesystem path.
*
* @return filesystem path
*/
public String getFilesystemPath();
// Transferable interface methods
@Override
public void transferTo(TarArchiveOutputStream outputStream, String destinationPath);
@Override
public long getSize();
@Override
public void updateChecksum(Checksum checksum);
@Override
public String getDescription();
}Usage Examples:
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.MountableFile;
import org.testcontainers.utility.DockerImageName;
import java.nio.file.Paths;
// Mount host file
GenericContainer<?> nginx = new GenericContainer<>(DockerImageName.parse("nginx:alpine"))
.withCopyFileToContainer(
MountableFile.forHostPath("/host/path/nginx.conf"),
"/etc/nginx/nginx.conf"
);
// Mount classpath resource
GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
.withCopyFileToContainer(
MountableFile.forClasspathResource("config/application.yml"),
"/app/config/application.yml"
);
// Mount with custom permissions
GenericContainer<?> script = new GenericContainer<>(DockerImageName.parse("alpine:latest"))
.withCopyFileToContainer(
MountableFile.forClasspathResource("scripts/entrypoint.sh", 0755),
"/entrypoint.sh"
)
.withCommand("/entrypoint.sh");
// Mount from Path
Path configPath = Paths.get("src/test/resources/config.json");
GenericContainer<?> configured = new GenericContainer<>(DockerImageName.parse("app:latest"))
.withCopyFileToContainer(
MountableFile.forHostPath(configPath),
"/app/config.json"
);
// Multiple files
GenericContainer<?> multiFile = new GenericContainer<>(DockerImageName.parse("app:latest"))
.withCopyFileToContainer(
MountableFile.forClasspathResource("data/users.csv"),
"/data/users.csv"
)
.withCopyFileToContainer(
MountableFile.forClasspathResource("data/products.csv"),
"/data/products.csv"
)
.withCopyFileToContainer(
MountableFile.forHostPath("/etc/ssl/certs/ca-cert.pem"),
"/app/certs/ca-cert.pem"
);Interface for content that can be transferred to containers with support for various sources.
/**
* Represents content that can be transferred to a container.
* Provides factory methods for creating transferables from various sources.
*/
public interface Transferable {
/** Default file mode (0100644 - regular file with rw-r--r-- permissions) */
int DEFAULT_FILE_MODE = 0100644;
/** Default directory mode (040755 - directory with rwxr-xr-x permissions) */
int DEFAULT_DIR_MODE = 040755;
/**
* Transfer content to a tar archive stream.
*
* @param outputStream tar archive output stream
* @param destinationPath destination path in archive
* @throws RuntimeException if transfer fails
*/
void transferTo(TarArchiveOutputStream outputStream, String destinationPath);
/**
* Get the size of the transferable content.
*
* @return size in bytes, or -1 if unknown
*/
long getSize();
/**
* Get the file mode (Unix permissions) for this transferable.
* Default implementation returns DEFAULT_FILE_MODE.
*
* @return Unix file permissions (e.g., 0644)
*/
default int getFileMode();
/**
* Get the bytes of this transferable content.
* Default implementation returns an empty byte array.
*
* @return byte array of content (default: empty array)
*/
default byte[] getBytes();
/**
* Update a checksum with the transferable content.
* Default implementation throws UnsupportedOperationException.
*
* @param checksum checksum to update
* @throws UnsupportedOperationException if not implemented in subclass
*/
default void updateChecksum(Checksum checksum);
/**
* Get a description of this transferable.
*
* @return human-readable description
*/
String getDescription();
// Static factory methods
/**
* Create a transferable from a byte array.
*
* @param bytes content bytes
* @return Transferable instance
*/
static Transferable of(byte[] bytes);
/**
* Create a transferable from bytes with file mode.
*
* @param bytes content bytes
* @param fileMode Unix file permissions (e.g., 0644)
* @return Transferable instance
*/
static Transferable of(byte[] bytes, int fileMode);
/**
* Create a transferable from a string (UTF-8).
*
* @param string string content
* @return Transferable instance
*/
static Transferable of(String string);
/**
* Create a transferable from a string with custom file mode.
*
* @param string string content
* @param fileMode Unix file permissions (e.g., 0755)
* @return Transferable instance
*/
static Transferable of(String string, int fileMode);
}Usage Examples:
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.utility.DockerImageName;
import java.nio.file.Paths;
// Transfer string content
String configYaml = """
server:
port: 8080
database:
url: jdbc:postgresql://db:5432/test
""";
GenericContainer<?> app = new GenericContainer<>(DockerImageName.parse("myapp:latest"))
.withCopyToContainer(
Transferable.of(configYaml),
"/app/config.yml"
);
// Transfer byte array
byte[] binaryData = loadBinaryData();
GenericContainer<?> dataContainer = new GenericContainer<>(DockerImageName.parse("app:latest"))
.withCopyToContainer(
Transferable.of(binaryData),
"/data/file.dat"
);
// Transfer with custom permissions
String script = "#!/bin/bash\necho 'Hello'\n";
GenericContainer<?> scriptContainer = new GenericContainer<>(DockerImageName.parse("alpine:latest"))
.withCopyToContainer(
Transferable.of(script.getBytes(), 0755), // Executable
"/scripts/hello.sh"
);
// Generate content dynamically
String dynamicConfig = generateConfig();
GenericContainer<?> dynamic = new GenericContainer<>(DockerImageName.parse("app:latest"))
.withCopyToContainer(
Transferable.of(dynamicConfig),
"/app/config/dynamic.conf"
);
// Multiple transfers
GenericContainer<?> multi = new GenericContainer<>(DockerImageName.parse("app:latest"))
.withCopyToContainer(Transferable.of("log_level=DEBUG"), "/app/.env")
.withCopyToContainer(Transferable.of(secretBytes, 0600), "/app/secrets.key");Global configuration for Testcontainers from properties files and environment variables.
/**
* Global configuration for Testcontainers.
* Loads settings from ~/.testcontainers.properties and environment variables.
*/
public class TestcontainersConfiguration {
/**
* Get the singleton configuration instance.
*
* @return configuration instance
*/
public static TestcontainersConfiguration getInstance();
/**
* Get all configuration properties.
* Note: Returns internal mutable Properties object. Modifications may affect configuration.
*
* @return properties object
*/
public Properties getProperties();
/**
* Get a configuration value from environment variable or property.
* Environment variable takes precedence.
*
* @param envVar environment variable name
* @param propertyName property name
* @return configuration value, or null if not set
*/
public String getEnvVarOrProperty(String envVar, String propertyName);
/**
* Get a user property or environment variable.
*
* @param propertyName property name
* @param defaultValue default value if not set
* @return configuration value
*/
public String getEnvVarOrUserProperty(String propertyName, String defaultValue);
/**
* Check if Ryuk container should run with privileged mode.
*
* @return true if Ryuk should be privileged
*/
public boolean isRyukPrivileged();
/**
* Check if the environment supports container reuse.
*
* @return true if reuse is supported and enabled
*/
public boolean environmentSupportsReuse();
/**
* Get the configured image pull policy.
*
* @return image pull policy string
*/
public String getImagePullPolicy();
}Configuration Properties:
# ~/.testcontainers.properties
# Docker host
docker.host=tcp://localhost:2375
# Container reuse
testcontainers.reuse.enable=true
# Disable resource reaper (for debugging)
testcontainers.ryuk.disabled=false
# Image substitution
testcontainers.hub.image.name.prefix=my-registry.com/
# Logging
testcontainers.diagnostics.enabled=trueUsage Example:
import org.testcontainers.utility.TestcontainersConfiguration;
import java.util.Properties;
// Get configuration
TestcontainersConfiguration config = TestcontainersConfiguration.getInstance();
// Check if reuse is enabled
String reuseEnabled = config.getEnvVarOrProperty(
"TESTCONTAINERS_REUSE_ENABLE",
"testcontainers.reuse.enable"
);
System.out.println("Reuse enabled: " + reuseEnabled);
// Get all properties
Properties props = config.getProperties();
props.forEach((key, value) -> {
System.out.println(key + " = " + value);
});
// Use in tests
if ("true".equals(reuseEnabled)) {
// Configure containers for reuse
GenericContainer<?> reusable = new GenericContainer<>(
DockerImageName.parse("postgres:15"))
.withReuse(true);
}Control and customize Docker image names at runtime through image substitutors. This system allows organizations to redirect image pulls to private registries or apply custom transformations.
/**
* Interface for substituting Docker image names.
* Implementations can transform image names before container creation.
* Marked as @UnstableAPI - subject to change.
*/
@UnstableAPI
public abstract class ImageNameSubstitutor {
/**
* Apply substitution to an image name.
*
* @param original the original image name
* @return substituted image name
*/
public abstract DockerImageName apply(DockerImageName original);
/**
* Get a description of this substitutor.
*
* @return human-readable description
*/
protected abstract String getDescription();
/**
* Get the configured image substitutor instance.
* Loads from configuration or returns default implementation.
*
* @return active substitutor instance
*/
public static ImageNameSubstitutor instance();
/**
* Get a no-op substitutor that doesn't modify image names.
*
* @return pass-through substitutor
*/
public static ImageNameSubstitutor noop();
}Testcontainers provides three concrete implementations of ImageNameSubstitutor for common image substitution needs.
/**
* Default image name substitutor that chains multiple strategies.
* This is the default substitutor used by Testcontainers.
* Applies ConfigurationFileImageNameSubstitutor first, then PrefixingImageNameSubstitutor.
*
* @since 1.14.0
*/
@UnstableAPI
final class DefaultImageNameSubstitutor extends ImageNameSubstitutor {
/**
* Default constructor.
* Creates instances of ConfigurationFileImageNameSubstitutor and PrefixingImageNameSubstitutor.
*/
public DefaultImageNameSubstitutor();
/**
* Apply both configuration file and prefixing substitution in sequence.
* First checks configuration file for specific image substitutions,
* then applies registry prefix if configured.
*
* @param original the original image name
* @return substituted image name (may be the same as original if no substitution applies)
*/
@Override
public DockerImageName apply(DockerImageName original);
/**
* Get description of this substitutor.
*
* @return description including both delegate substitutors
*/
@Override
protected String getDescription();
}
/**
* Applies a registry prefix to Docker Hub image names.
* Only affects images without an explicit registry (Docker Hub images).
* Images with explicit registries (e.g., gcr.io/myimage) are not modified.
*
* Controlled by:
* - Environment variable: TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX
* - Property: hub.image.name.prefix
*
* Example: If prefix is "my-registry.com:5000/mirror/",
* "redis:7.0" becomes "my-registry.com:5000/mirror/redis:7.0"
*
* @since 1.14.0
*/
@UnstableAPI
final class PrefixingImageNameSubstitutor extends ImageNameSubstitutor {
/**
* Configuration property key for the prefix.
* Value: "hub.image.name.prefix"
*/
@VisibleForTesting
static final String PREFIX_PROPERTY_KEY = "hub.image.name.prefix";
/**
* Default constructor using TestcontainersConfiguration.getInstance().
*/
public PrefixingImageNameSubstitutor();
/**
* Apply registry prefix to Docker Hub images.
* Only modifies images without an explicit registry.
* If no prefix is configured, returns the original image unchanged.
*
* @param original the original image name
* @return image with prefix applied (if original is Docker Hub image and prefix is configured)
*/
@Override
public DockerImageName apply(DockerImageName original);
/**
* Get description of this substitutor.
*
* @return simple class name
*/
@Override
protected String getDescription();
}
/**
* Substitutes image names based on configuration file mappings.
* Reads from TestcontainersConfiguration for specific image substitutions.
* This allows mapping specific images to alternatives on a per-image basis.
*
* Configuration is read from ~/.testcontainers.properties using keys specific
* to the TestcontainersConfiguration implementation.
*
* Note: This mechanism is deprecated and will be removed in future versions.
* Users should migrate to PrefixingImageNameSubstitutor for registry redirection.
*
* @since 1.14.0
*/
@UnstableAPI
final class ConfigurationFileImageNameSubstitutor extends ImageNameSubstitutor {
/**
* Default constructor using TestcontainersConfiguration.getInstance().
*/
public ConfigurationFileImageNameSubstitutor();
/**
* Apply configured image substitution.
* Looks up the original image in configuration and returns the configured
* substitute if found, otherwise returns the original.
*
* Logs a warning when substitution occurs, as this mechanism is deprecated.
*
* @param original the original image name
* @return configured substitute image (marked as compatible with original), or original if no substitution configured
*/
@Override
public DockerImageName apply(DockerImageName original);
/**
* Get description of this substitutor.
*
* @return simple class name
*/
@Override
protected String getDescription();
}Configuration Example:
# In ~/.testcontainers.properties
# Apply prefix to all images (redirect to private registry)
hub.image.name.prefix=my-registry.company.com:5000/
# Or via environment variable
# TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=my-registry.company.com:5000/Usage Example:
import org.testcontainers.utility.ImageNameSubstitutor;
import org.testcontainers.utility.DockerImageName;
// Image names are automatically transformed using configured substitutor
// Original: postgres:15
// With prefix: my-registry.company.com:5000/postgres:15
GenericContainer<?> postgres = new GenericContainer<>(
DockerImageName.parse("postgres:15")
);
// Testcontainers automatically applies the substitutor
// Check active substitutor
ImageNameSubstitutor substitutor = ImageNameSubstitutor.instance();
System.out.println("Active substitutor: " + substitutor.getDescription());
// Manual substitution (rarely needed)
DockerImageName original = DockerImageName.parse("redis:7.0");
DockerImageName substituted = substitutor.apply(original);
System.out.println("Substituted: " + substituted.asCanonicalNameString());Common Use Cases:
Private Registry Redirection:
hub.image.name.prefix=registry.internal.company.com/Transforms postgres:15 → registry.internal.company.com/postgres:15
Air-Gapped Environments:
hub.image.name.prefix=offline-registry.local/mirror/All public images pulled from local mirror
Custom Image Mappings: Configure specific image substitutions for organization-specific images
Version comparison utility for Docker API versions and image tags.
/**
* Comparable version implementation for version strings.
* Supports semantic versioning and complex version schemes.
* Parses version strings intelligently, handling numeric and string components.
*/
public class ComparableVersion implements Comparable<ComparableVersion> {
/**
* Create a comparable version.
*
* @param version version string
*/
public ComparableVersion(String version);
@Override
public int compareTo(ComparableVersion other);
@Override
public boolean equals(Object obj);
@Override
public int hashCode();
@Override
public String toString();
}Usage Example:
import org.testcontainers.utility.ComparableVersion;
// Compare versions
ComparableVersion v1 = new ComparableVersion("1.2.3");
ComparableVersion v2 = new ComparableVersion("1.2.10");
ComparableVersion v3 = new ComparableVersion("2.0.0");
System.out.println(v1.compareTo(v2)); // -1 (v1 < v2)
System.out.println(v2.compareTo(v3)); // -1 (v2 < v3)
System.out.println(v3.compareTo(v1)); // 1 (v3 > v1)
// Check Docker API compatibility
String dockerApiVersion = DockerClientFactory.instance().getActiveApiVersion();
ComparableVersion apiVersion = new ComparableVersion(dockerApiVersion);
ComparableVersion required = new ComparableVersion("1.41");
if (apiVersion.compareTo(required) >= 0) {
System.out.println("Docker API version is sufficient");
} else {
throw new IllegalStateException("Docker API version too old");
}Base58 encoding utility for generating random strings.
/**
* Base58 encoding utility.
* Used for generating unique identifiers.
*/
public class Base58 {
/**
* Generate a random Base58 string.
*
* @param length desired length
* @return random Base58 string
*/
public static String randomString(int length);
}Usage Example:
import org.testcontainers.utility.Base58;
// Generate unique test identifiers
String testId = Base58.randomString(8);
System.out.println("Test ID: " + testId);
// Generate unique resource names
String networkName = "test-network-" + Base58.randomString(6);
String containerName = "test-container-" + Base58.randomString(6);Execute shell commands from Java.
/**
* Utility for executing shell commands.
* Regular class with static utility methods (not annotated with @UtilityClass).
*/
public class CommandLine {
/**
* Run a shell command synchronously and return its output.
*
* @param command variable number of command arguments (command and its arguments)
* @return command stdout output (trimmed)
* @throws ShellCommandException if execution fails (wraps IOException, InterruptedException, TimeoutException, or InvalidExitValueException)
*/
public static String runShellCommand(String... command);
/**
* Check whether an executable exists, either at a specific path or on the PATH.
*
* @param executable name of an executable on the PATH or complete path to an executable
* @return true if the executable exists and is executable
*/
public static boolean executableExists(String executable);
/**
* Get the system PATH environment variable as an array of path strings.
*
* @return array of path strings from the PATH environment variable, split by path separator
*/
@NotNull
public static String[] getSystemPath();
}Create loggers for container output.
/**
* Factory for creating container-specific loggers.
*/
public class DockerLoggerFactory {
/**
* Get a logger for a Docker image.
*
* @param imageName image name
* @return logger instance
*/
public static Logger getLogger(String imageName);
}Usage Example:
import org.testcontainers.utility.DockerLoggerFactory;
import org.slf4j.Logger;
// Get logger for container
Logger logger = DockerLoggerFactory.getLogger("postgres:15");
logger.info("Starting PostgreSQL container");import org.testcontainers.containers.GenericContainer;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.utility.MountableFile;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.utility.TestcontainersConfiguration;
import org.testcontainers.utility.Base58;
import java.nio.file.Paths;
public class UtilityClassesExample {
@Test
public void testWithUtilities() {
// Generate unique test ID
String testId = Base58.randomString(8);
// Parse and manipulate image names
DockerImageName baseImage = DockerImageName.parse("nginx:alpine");
DockerImageName customImage = baseImage
.withRegistry("my-registry.com:5000")
.withTag("1.23-alpine");
// Prepare configuration file
String nginxConfig = generateNginxConfig(testId);
// Create container with various file sources
GenericContainer<?> nginx = new GenericContainer<>(customImage)
.withExposedPorts(80, 443)
// String content
.withCopyToContainer(
Transferable.of(nginxConfig),
"/etc/nginx/nginx.conf"
)
// Classpath resource
.withCopyFileToContainer(
MountableFile.forClasspathResource("ssl/cert.pem"),
"/etc/nginx/ssl/cert.pem"
)
// Host file
.withCopyFileToContainer(
MountableFile.forHostPath(Paths.get("www/index.html")),
"/usr/share/nginx/html/index.html"
)
// Environment from config
.withEnv("TEST_ID", testId)
.withLabel("test.id", testId);
// Check configuration
TestcontainersConfiguration config = TestcontainersConfiguration.getInstance();
if ("true".equals(config.getEnvVarOrProperty("DEBUG", "debug"))) {
nginx.withEnv("DEBUG", "true");
}
nginx.start();
// Use container
String url = String.format("http://%s:%d",
nginx.getHost(),
nginx.getMappedPort(80));
// Test with generated test ID
testNginx(url, testId);
nginx.stop();
}
private String generateNginxConfig(String testId) {
return String.format("""
events { worker_connections 1024; }
http {
server {
listen 80;
server_name test-%s;
location / {
root /usr/share/nginx/html;
index index.html;
}
}
}
""", testId);
}
}Utility for detecting Docker environment capabilities and configuration.
/**
* Provides utility methods for determining facts about the test environment.
* All methods are static.
*/
@UtilityClass
public class TestEnvironment {
/**
* Check if Docker API version meets minimum requirement.
*
* @param minimumVersion minimum version string (e.g., "1.41")
* @return true if current API version >= minimum
*/
public static boolean dockerApiAtLeast(String minimumVersion);
/**
* Check if Docker execution driver supports exec commands.
* Returns false for legacy 'lxc' driver.
*
* @return true if exec is supported
*/
public static boolean dockerExecutionDriverSupportsExec();
/**
* Check if running against Docker Machine.
*
* @return true if Docker Machine is being used
*/
public static boolean dockerIsDockerMachine();
}Usage Examples:
import org.testcontainers.utility.TestEnvironment;
// Check API version before using features
if (TestEnvironment.dockerApiAtLeast("1.41")) {
// Use features requiring API 1.41+
container.withCreateContainerCmdModifier(cmd ->
cmd.withCgroupnsMode("private")
);
}
// Check exec support (used by ExecInContainerPattern)
if (!TestEnvironment.dockerExecutionDriverSupportsExec()) {
throw new UnsupportedOperationException(
"Docker exec not supported with lxc driver"
);
}
// Adjust behavior for Docker Machine
if (TestEnvironment.dockerIsDockerMachine()) {
System.out.println("Running on Docker Machine");
// May need special port mapping or volume handling
}
// Conditional test execution based on Docker API version
@Test
public void testModernDockerFeature() {
assumeTrue(
TestEnvironment.dockerApiAtLeast("1.43"),
"Test requires Docker API 1.43+ for cgroup v2 support"
);
// Use modern Docker features requiring API 1.43+
GenericContainer<?> container = new GenericContainer<>("alpine:latest")
.withCreateContainerCmdModifier(cmd ->
cmd.withCgroupnsMode("private")
);
container.start();
}
// Skip tests when Docker Machine is in use (e.g., volume mounting issues)
@BeforeAll
public static void checkEnvironment() {
assumeFalse(
TestEnvironment.dockerIsDockerMachine(),
"Test incompatible with Docker Machine due to volume mounting"
);
}
// Conditional feature usage based on exec support
@Test
public void testExecInContainer() {
assumeTrue(
TestEnvironment.dockerExecutionDriverSupportsExec(),
"Test requires exec support (not available with lxc driver)"
);
GenericContainer<?> container = new GenericContainer<>("alpine:latest")
.withCommand("sleep", "infinity");
container.start();
// Now safe to use exec
Container.ExecResult result = container.execInContainer("echo", "test");
assertEquals(0, result.getExitCode());
}
// Version-specific test class with multiple conditionally enabled tests
public class DockerVersionSpecificTests {
@Test
public void testBasicFeatures() {
// Always runs - requires no minimum version
GenericContainer<?> container = new GenericContainer<>("alpine:latest");
container.start();
}
@Test
public void testApi140Features() {
assumeTrue(TestEnvironment.dockerApiAtLeast("1.40"));
// Use features from API 1.40+
}
@Test
public void testApi143Features() {
assumeTrue(TestEnvironment.dockerApiAtLeast("1.43"));
// Use features from API 1.43+
}
}Path manipulation utilities for filesystem operations.
/**
* Utilities for path manipulation and filesystem operations.
*/
@UtilityClass
public class PathUtils {
/**
* Recursively delete a directory and all its subdirectories and files.
*
* @param directory the directory to delete (must be non-null)
* @throws IOException if deletion fails (exception is caught and ignored internally)
*/
public static void recursiveDeleteDir(@NonNull Path directory);
/**
* Create a directory plus any required parent directories (like mkdir -p).
*
* @param directory the directory path to create
* @throws IllegalStateException if directory creation fails
*/
public static void mkdirp(Path directory);
/**
* Create a MinGW compatible path from a Windows path.
* Converts backslashes to forward slashes and formats drive letters appropriately.
*
* @param path a usual Windows path
* @return MinGW compatible path
*/
public static String createMinGWPath(String path);
}Usage Example:
import org.testcontainers.utility.PathUtils;
import java.nio.file.Path;
import java.nio.file.Paths;
// Create directory with parents
Path tempDir = Paths.get("/tmp/test/nested/dir");
PathUtils.mkdirp(tempDir);
// Convert Windows path to MinGW format
String windowsPath = "C:\\Users\\test\\data";
String mingwPath = PathUtils.createMinGWPath(windowsPath);
// Result: "/c/Users/test/data"
// Clean up test directories
Path testDir = Paths.get("/tmp/test");
PathUtils.recursiveDeleteDir(testDir);Utilities for analyzing Docker container states and timestamps.
/**
* Utilities for checking Docker container status.
*/
@UtilityClass
public class DockerStatus {
/** Docker zero timestamp constant */
public static final String DOCKER_TIMESTAMP_ZERO = "0001-01-01T00:00:00Z";
/**
* Check if a container is running and has been doing so for a specified amount of time.
*
* @param state container state from inspection
* @param minimumRunningDuration minimum running duration (nullable)
* @param now current instant
* @return true if container is running for at least the minimum duration
*/
public static boolean isContainerRunning(
InspectContainerResponse.ContainerState state,
Duration minimumRunningDuration,
Instant now
);
/**
* Check if a container has halted (started but is now stopped).
*
* @param state container state from inspection
* @return true if container is stopped
*/
public static boolean isContainerStopped(InspectContainerResponse.ContainerState state);
/**
* Check if a Docker timestamp is non-empty and valid.
*
* @param dockerTimestamp Docker timestamp string
* @return true if timestamp is non-empty and not the zero timestamp
*/
public static boolean isDockerTimestampNonEmpty(String dockerTimestamp);
/**
* Check if a container exit code indicates success (exit code 0).
*
* @param state container state from inspection
* @return true if exit code is 0
*/
public static boolean isContainerExitCodeSuccess(InspectContainerResponse.ContainerState state);
}Usage Example:
import org.testcontainers.utility.DockerStatus;
import com.github.dockerjava.api.command.InspectContainerResponse;
import java.time.Duration;
import java.time.Instant;
// Check container status
InspectContainerResponse.ContainerState state = container.getContainerInfo().getState();
// Check if running for at least 5 seconds
if (DockerStatus.isContainerRunning(state, Duration.ofSeconds(5), Instant.now())) {
System.out.println("Container has been running for at least 5 seconds");
}
// Check if container stopped
if (DockerStatus.isContainerStopped(state)) {
System.out.println("Container has stopped");
}
// Check exit code
if (DockerStatus.isContainerExitCodeSuccess(state)) {
System.out.println("Container exited successfully");
}
// Validate Docker timestamp
String startedAt = state.getStartedAt();
if (DockerStatus.isDockerTimestampNonEmpty(startedAt)) {
System.out.println("Container has a valid start timestamp");
}Functional interface that allows checked exceptions.
/**
* Function that can throw exceptions.
* Similar to java.util.function.Function but allows checked exceptions.
*
* @param <T> input type
* @param <R> result type
*/
@FunctionalInterface
public interface ThrowingFunction<T, R> {
/**
* Apply the function.
*
* @param t the input
* @return the result
* @throws Exception if an error occurs
*/
R apply(T t) throws Exception;
}Usage Example:
import org.testcontainers.utility.ThrowingFunction;
// Use with operations that throw exceptions
ThrowingFunction<String, Integer> parser = str -> {
// Can throw NumberFormatException without wrapping
return Integer.parseInt(str);
};
// Use in streaming operations
List<String> numbers = Arrays.asList("1", "2", "3");
List<Integer> parsed = numbers.stream()
.map(wrapFunction(parser))
.collect(Collectors.toList());
// Helper to convert ThrowingFunction to regular Function
private static <T, R> Function<T, R> wrapFunction(ThrowingFunction<T, R> func) {
return t -> {
try {
return func.apply(t);
} catch (Exception e) {
throw new RuntimeException(e);
}
};
}Utility for locating Docker registry authentication credentials for image pulls.
/**
* Utility to look up registry authentication information for images.
* Follows Docker CLI authentication lookup order:
* 1. credHelpers (credential helpers for specific registries)
* 2. credsStore (global credential store)
* 3. Base64 encoded auth in auths section
* 4. Falls back to docker-java's default behavior
*
* Singleton pattern - use instance() to get the instance.
*/
public class RegistryAuthLocator {
/**
* Get the singleton instance.
*
* @return the RegistryAuthLocator instance
*/
public static synchronized RegistryAuthLocator instance();
/**
* Look up authentication configuration for a specific image.
* Checks Docker config files (~/.docker/config.json) and credential helpers.
*
* @param dockerImageName image name to look up (may include registry)
* @param defaultAuthConfig fallback auth config if no credentials found
* @return AuthConfig for the image's registry, or defaultAuthConfig if none found
*/
public AuthConfig lookupAuthConfig(
DockerImageName dockerImageName,
AuthConfig defaultAuthConfig
);
}Usage Example:
import org.testcontainers.utility.RegistryAuthLocator;
import org.testcontainers.utility.DockerImageName;
import com.github.dockerjava.api.model.AuthConfig;
// Look up credentials for private registry image
DockerImageName imageName = DockerImageName.parse("myregistry.com/myapp:latest");
AuthConfig defaultAuth = new AuthConfig();
RegistryAuthLocator locator = RegistryAuthLocator.instance();
AuthConfig auth = locator.lookupAuthConfig(imageName, defaultAuth);
// Use with GenericContainer
GenericContainer<?> container = new GenericContainer<>(imageName)
.withImagePullPolicy(PullPolicy.alwaysPull());
// Authentication is automatically handled by TestcontainersAuthentication Sources:
The locator searches for credentials in this order:
credHelpers in config.json) - Registry-specific helpers like docker-credential-ecr-logincredsStore in config.json) - System credential managers like osxkeychain, wincred, passauths in config.json) - Base64 encoded username:passwordDOCKER_AUTH_CONFIG) - JSON config overrideConfiguration Example:
// ~/.docker/config.json
{
"auths": {
"https://index.docker.io/v1/": {},
"myregistry.com": {
"auth": "dXNlcm5hbWU6cGFzc3dvcmQ="
}
},
"credHelpers": {
"123456789.dkr.ecr.us-east-1.amazonaws.com": "ecr-login"
},
"credsStore": "osxkeychain"
}Client for interacting with Docker Machine installations.
/**
* Wrapper for docker-machine CLI operations.
* Provides methods to detect, configure, and manage Docker Machine instances.
*
* Singleton pattern - use instance() to get the instance.
*/
public class DockerMachineClient {
/**
* Get the singleton instance.
*
* @return the DockerMachineClient instance
*/
public static synchronized DockerMachineClient instance();
/**
* Check if docker-machine executable is installed and available.
*
* @return true if docker-machine is installed
*/
public boolean isInstalled();
/**
* Get the default Docker Machine name to use.
* Checks in order:
* 1. DOCKER_MACHINE_NAME environment variable
* 2. Machine named "default"
* 3. First machine from `docker-machine ls`
*
* @return optional machine name
*/
public Optional<String> getDefaultMachine();
/**
* Ensure a Docker Machine is running, starting it if necessary.
*
* @param machineName name of the machine
*/
public void ensureMachineRunning(@NonNull String machineName);
/**
* Get the Docker daemon URL for a specific machine.
* Returns the connection URL (e.g., tcp://192.168.99.100:2376).
*
* @param machineName name of the machine
* @return Docker daemon URL
*/
public String getDockerDaemonUrl(@NonNull String machineName);
/**
* Check if a specific Docker Machine is running.
*
* @param machineName name of the machine
* @return true if machine is running
*/
public boolean isMachineRunning(String machineName);
/**
* Check if the default Docker Machine is running.
*
* @return true if default machine is running
*/
public boolean isDefaultMachineRunning();
/**
* Get the Docker daemon IP address for a machine.
*
* @param machineName name of the machine
* @return IP address of the Docker daemon
* @deprecated Use getDockerDaemonUrl() for full connection details
*/
@Deprecated
public String getDockerDaemonIpAddress(@NonNull String machineName);
}Usage Example:
import org.testcontainers.utility.DockerMachineClient;
DockerMachineClient machineClient = DockerMachineClient.instance();
// Check if Docker Machine is available
if (machineClient.isInstalled()) {
// Get default machine
Optional<String> machineName = machineClient.getDefaultMachine();
if (machineName.isPresent()) {
String machine = machineName.get();
// Ensure machine is running
machineClient.ensureMachineRunning(machine);
// Get connection details
String dockerUrl = machineClient.getDockerDaemonUrl(machine);
System.out.println("Docker daemon URL: " + dockerUrl);
// Check status
boolean running = machineClient.isMachineRunning(machine);
System.out.println("Machine " + machine + " running: " + running);
}
}
// Use in tests to skip if Docker Machine required
@BeforeAll
public static void checkDockerMachine() {
assumeTrue(
DockerMachineClient.instance().isInstalled(),
"Tests require Docker Machine"
);
}Docker Machine Context:
Docker Machine is primarily used for:
Most modern setups use Docker Desktop directly and don't require Docker Machine. Testcontainers automatically detects and configures Docker Machine if present through the DockerMachineClientProviderStrategy.
---
## Internal/Advanced Utility Classes
### LazyFuture (Internal Implementation Detail)
**WARNING: This is an internal implementation class. Most users should NOT use this directly. It is documented here for completeness and for those implementing custom extensions.**
Lazy-evaluating Future implementation for deferred computation. Used internally by `RemoteDockerImage` and `ImageFromDockerfile` for async image resolution. The public API provides higher-level abstractions that handle lazy evaluation internally.
```java { .api }
/**
* INTERNAL CLASS - Do not use directly.
* Future implementation with lazy result evaluation in the same Thread as caller.
* Abstract class requiring implementation of resolve() method.
* Used internally by RemoteDockerImage and ImageFromDockerfile.
*
* @param <T> result type
*/
public abstract class LazyFuture<T> implements Future<T> {
/**
* Implement this method to define the computation.
* Called once when get() is first invoked.
*
* @return the computed result
*/
protected abstract T resolve();
/**
* Cannot cancel lazy futures.
* @return always false
*/
@Override
public boolean cancel(boolean mayInterruptIfRunning);
/**
* Lazy futures cannot be cancelled.
* @return always false
*/
@Override
public boolean isCancelled();
/**
* Check if the computation has been performed.
* @return true if resolve() has been called
*/
@Override
public boolean isDone();
/**
* Get the result, computing it if necessary.
* Thread-safe lazy initialization.
* @return the computed result
*/
@Override
public T get();
/**
* Get the result with timeout.
* @param timeout the timeout value
* @param unit the timeout unit
* @return the computed result
* @throws InterruptedException if interrupted during computation
* @throws TimeoutException if computation times out
*/
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, TimeoutException;
}Why You Probably Don't Need This:
The Testcontainers API abstracts away lazy evaluation:
// Users should use RemoteDockerImage directly
GenericContainer<?> container = new GenericContainer<>(
new RemoteDockerImage(DockerImageName.parse("postgres:15"))
);
// Or use ImageFromDockerfile
GenericContainer<?> customContainer = new GenericContainer<>(
new ImageFromDockerfile()
.withDockerfilePath("src/main/docker/Dockerfile")
);
// LazyFuture is INTERNAL - you don't interact with it directly
// The image resolution is handled transparently when the container starts
container.start();Internal Usage Pattern (For Reference Only):
// How RemoteDockerImage uses LazyFuture internally
public class RemoteDockerImage extends LazyFuture<String> {
private final DockerImageName imageName;
@Override
protected String resolve() {
// Pull image if needed, return image name
// This is called transparently when container needs the image
return pullImageAndGetName(imageName);
}
}Do not extend LazyFuture in application code unless you are implementing a custom image provider for Testcontainers.