or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

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

utility-classes.mddocs/

Utility Classes

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.

Capabilities

DockerImageName

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

MountableFile

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

Transferable

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

TestcontainersConfiguration

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=true

Usage 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);
}

Additional Utility Classes

Image Name Substitution

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();
}

Built-in Image Name Substitutors

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:

  1. Private Registry Redirection:

    hub.image.name.prefix=registry.internal.company.com/

    Transforms postgres:15registry.internal.company.com/postgres:15

  2. Air-Gapped Environments:

    hub.image.name.prefix=offline-registry.local/mirror/

    All public images pulled from local mirror

  3. Custom Image Mappings: Configure specific image substitutions for organization-specific images

ComparableVersion

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

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

CommandLine

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();
}

DockerLoggerFactory

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

Complete Usage Example

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

Advanced Utility Classes

TestEnvironment

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+
    }
}

PathUtils

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

DockerStatus

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

ThrowingFunction

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

RegistryAuthLocator

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 Testcontainers

Authentication Sources:

The locator searches for credentials in this order:

  1. Credential helpers (credHelpers in config.json) - Registry-specific helpers like docker-credential-ecr-login
  2. Credential store (credsStore in config.json) - System credential managers like osxkeychain, wincred, pass
  3. Embedded credentials (auths in config.json) - Base64 encoded username:password
  4. Environment variable (DOCKER_AUTH_CONFIG) - JSON config override

Configuration 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"
}

DockerMachineClient

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:

  • Legacy environments - Pre-Docker Desktop setups
  • Remote Docker hosts - Managing Docker on VMs or remote servers
  • Multi-host setups - Provisioning multiple Docker environments

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.