This document covers advanced MySQL container customization including environment variables, command overrides, port mapping, logging, and container information retrieval.
Environment Variables (Fluent Interface, Return SELF):
withEnv(String key, String value) - Set single environment variablewithEnv(Map<String, String> env) - Set multiple environment variablesCommand Override (Fluent Interface, Return SELF):
withCommand(String... commandParts) - Override container commandPort Management:
withExposedPorts(Integer... ports): SELF - Expose additional portsgetMappedPort(int originalPort): Integer - Get mapped host port (requires container to be started)getExposedPorts(): List<Integer> - Get list of exposed portsgetLivenessCheckPortNumbers(): Set<Integer> - Get ports used for liveness checksLogging (Fluent Interface, Return SELF):
withLogConsumer(Consumer<OutputFrame> consumer) - Set log consumerContainer Information:
getHost(): String - Get container host (typically "localhost", requires container to be started)getMappedPort(int): Integer - Get mapped port for container port (requires container to be started)Constraints:
start()Set custom environment variables in the MySQL container.
/**
* Sets a single environment variable in the container.
* Environment variables are set before the container starts and affect
* MySQL server initialization and runtime behavior.
*
* @param key the environment variable name (must not be null)
* @param value the environment variable value (can be null or empty)
* @return self for method chaining (SELF extends MySQLContainer<SELF>)
* @throws IllegalArgumentException if key is null
*/
SELF withEnv(String key, String value);
/**
* Sets multiple environment variables in the container.
* Replaces any previously set environment variables with the same keys.
*
* @param env map of environment variable names to values (must not be null)
* @return self for method chaining (SELF extends MySQLContainer<SELF>)
* @throws IllegalArgumentException if env is null
*/
SELF withEnv(Map<String, String> env);Usage Example:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.util.HashMap;
import java.util.Map;
// Set single environment variable
MySQLContainer<?> mysql1 = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withEnv("MYSQL_ROOT_HOST", "%") // Allow root login from any host
.withEnv("TZ", "America/New_York"); // Set timezone
// Set multiple environment variables
Map<String, String> envVars = new HashMap<>();
envVars.put("MYSQL_ROOT_HOST", "%");
envVars.put("TZ", "UTC");
envVars.put("MYSQL_INITDB_SKIP_TZINFO", "1");
MySQLContainer<?> mysql2 = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withEnv(envVars);
mysql1.start();Common MySQL Environment Variables:
MYSQL_ROOT_HOST - Host pattern for root user access (e.g., "%" for all hosts)MYSQL_ROOT_PASSWORD - Root password (automatically set by container)MYSQL_DATABASE - Database name (automatically set by container)MYSQL_USER - User name (automatically set by container)MYSQL_PASSWORD - User password (automatically set by container)MYSQL_ALLOW_EMPTY_PASSWORD - Allow empty root password (automatically set if applicable)TZ - Container timezone (e.g., "UTC", "America/New_York")MYSQL_INITDB_SKIP_TZINFO - Skip loading timezone informationOverride the default MySQL container command.
/**
* Overrides the container's command.
* This replaces the default MySQL server command with a custom one,
* allowing you to pass custom MySQL server options.
*
* The command parts are passed directly to the container's entrypoint.
* The first part should typically be "mysqld" to start the MySQL server.
*
* @param commandParts the command parts to execute (must not be null or empty)
* @return self for method chaining (SELF extends MySQLContainer<SELF>)
* @throws IllegalArgumentException if commandParts is null or empty
*/
SELF withCommand(String... commandParts);Usage Example:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
// Override command to set MySQL server variables
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withCommand("mysqld", "--max_connections=500", "--auto_increment_increment=5");
mysql.start();
// Verify the setting
try (Connection conn = mysql.createConnection("")) {
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SHOW VARIABLES LIKE 'max_connections'");
rs.next();
String maxConn = rs.getString("Value");
// maxConn == "500"
}Common MySQL Server Options:
--max_connections=N - Maximum concurrent connections--auto_increment_increment=N - Auto-increment increment value--auto_increment_offset=N - Auto-increment offset value--max_allowed_packet=N - Maximum packet size--innodb_buffer_pool_size=N - InnoDB buffer pool size--character-set-server=charset - Server character set--general_log=1 - Enable general query log--slow_query_log=1 - Enable slow query logExpose additional container ports beyond the default MySQL port.
/**
* Exposes additional ports from the container.
* MySQL port 3306 is exposed by default and does not need to be specified.
* Additional ports are mapped to random available host ports.
*
* @param ports additional port numbers to expose (must not be null)
* @return self for method chaining (SELF extends MySQLContainer<SELF>)
* @throws IllegalArgumentException if ports is null
*/
SELF withExposedPorts(Integer... ports);
/**
* Returns the list of exposed ports.
* Includes the default MySQL port (3306) and any additional ports.
* Can be called before or after container is started.
*
* @return list of exposed port numbers
*/
List<Integer> getExposedPorts();Usage Example:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.util.List;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withExposedPorts(3306, 33060); // Add X Protocol port
mysql.start();
List<Integer> exposedPorts = mysql.getExposedPorts();
// exposedPorts contains [3306, 33060]
Integer xProtocolMappedPort = mysql.getMappedPort(33060);Port Notes:
getMappedPort() to retrieve the host port for a container portCapture and process container log output.
/**
* Sets a consumer to receive container log output.
* Useful for debugging container issues or monitoring MySQL server logs.
* The consumer receives log frames as they are produced by the container.
*
* @param consumer consumer function that receives log frames (must not be null)
* @return self for method chaining (SELF extends MySQLContainer<SELF>)
* @throws IllegalArgumentException if consumer is null
*/
SELF withLogConsumer(Consumer<OutputFrame> consumer);Usage Example:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.utility.DockerImageName;
Logger logger = LoggerFactory.getLogger(MyTest.class);
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withLogConsumer(new Slf4jLogConsumer(logger));
mysql.start();
// All MySQL server logs are output via SLF4J loggerCustom Log Consumer:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.output.OutputFrame;
import org.testcontainers.utility.DockerImageName;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withLogConsumer((OutputFrame frame) -> {
String log = frame.getUtf8String();
if (log.contains("ERROR")) {
System.err.println("MySQL Error: " + log);
}
});
mysql.start();Retrieve container host and port mapping information.
/**
* Returns the host that the container is accessible from.
* Typically "localhost" but may differ in complex networking setups.
* This method requires the container to be started.
*
* @return the container host (typically "localhost")
* @throws IllegalStateException if called before container is started
*/
String getHost();
/**
* Returns the host port that is mapped to the specified container port.
* MySQL port 3306 is automatically mapped to a random available host port.
* This method requires the container to be started.
*
* @param originalPort the container port number
* @return the mapped host port number
* @throws IllegalStateException if called before container is started
* @throws IllegalArgumentException if originalPort is not exposed
*/
Integer getMappedPort(int originalPort);Usage Example:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();
String host = mysql.getHost(); // "localhost"
Integer mappedPort = mysql.getMappedPort(MySQLContainer.MYSQL_PORT); // e.g., 32768
String jdbcUrl = String.format("jdbc:mysql://%s:%d/test", host, mappedPort);
// Same as mysql.getJdbcUrl()Host and Port Notes:
getJdbcUrl() for complete JDBC URL instead of manual constructionIllegalStateException if called before start()Get the ports used for container liveness checks.
/**
* Returns the set of port numbers used for liveness checks.
* These are the mapped host ports that Testcontainers checks to verify
* the container is running and accessible.
* This method requires the container to be started.
*
* @return set of liveness check port numbers
* @throws IllegalStateException if called before container is started
*/
Set<Integer> getLivenessCheckPortNumbers();Usage Example:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.util.Set;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();
Set<Integer> livenessCheckPorts = mysql.getLivenessCheckPortNumbers();
// Contains the mapped port for MySQL (3306)
Integer mysqlMappedPort = mysql.getMappedPort(MySQLContainer.MYSQL_PORT);
// livenessCheckPorts.contains(mysqlMappedPort) == trueimport org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.output.Slf4jLogConsumer;
import org.testcontainers.utility.DockerImageName;
import java.sql.Connection;
import java.util.HashMap;
import java.util.Map;
public class CompleteCustomizationExample {
private static final Logger logger = LoggerFactory.getLogger(CompleteCustomizationExample.class);
public void runCustomizedContainer() throws Exception {
// Prepare environment variables
Map<String, String> env = new HashMap<>();
env.put("MYSQL_ROOT_HOST", "%");
env.put("TZ", "UTC");
// Create highly customized container
try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withDatabaseName("customdb")
.withUsername("customuser")
.withPassword("custompass")
.withEnv(env)
.withCommand("mysqld", "--max_connections=200", "--character-set-server=utf8mb4")
.withExposedPorts(3306, 33060)
.withLogConsumer(new Slf4jLogConsumer(logger))
.withConfigurationOverride("mysql-custom-config")
.withInitScript("schema.sql")
.withStartupTimeoutSeconds(180)
.withReuse(false)) {
mysql.start();
// Get container information
String host = mysql.getHost();
Integer mysqlPort = mysql.getMappedPort(3306);
Integer xProtocolPort = mysql.getMappedPort(33060);
System.out.println("MySQL accessible at: " + host + ":" + mysqlPort);
System.out.println("X Protocol accessible at: " + host + ":" + xProtocolPort);
// Use container
try (Connection conn = mysql.createConnection("")) {
// Execute queries...
}
}
}
}import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
boolean isDebugMode = System.getProperty("debug") != null;
boolean isCiEnvironment = System.getenv("CI") != null;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withDatabaseName("testdb");
if (isDebugMode) {
// Enable verbose logging in debug mode
mysql.withLogConsumer(new Slf4jLogConsumer(logger))
.withCommand("mysqld", "--general_log=1", "--general_log_file=/var/log/mysql-queries.log");
}
if (isCiEnvironment) {
// Use longer timeouts in CI
mysql.withStartupTimeoutSeconds(300)
.withConnectTimeoutSeconds(120);
}
mysql.start();import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.wait.strategy.Wait;
import org.testcontainers.utility.DockerImageName;
import java.time.Duration;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withDatabaseName("testdb")
.waitingFor(Wait.forListeningPort().withStartupTimeout(Duration.ofMinutes(3)));
mysql.start();For connecting multiple containers:
import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.containers.Network;
import org.testcontainers.utility.DockerImageName;
Network network = Network.newNetwork();
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withNetwork(network)
.withNetworkAliases("mysql-server")
.withDatabaseName("shareddb");
// Another container can connect to this MySQL at "mysql-server:3306"
mysql.start();For complex customization, use a custom Dockerfile:
# Dockerfile.mysql-custom
FROM mysql:8.0
# Install additional tools
RUN apt-get update && apt-get install -y \
mysql-client \
vim
# Copy custom configuration
COPY my-custom.cnf /etc/mysql/conf.d/
# Copy custom scripts
COPY custom-init.sh /docker-entrypoint-initdb.d/import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.images.builder.ImageFromDockerfile;
MySQLContainer<?> mysql = new MySQLContainer<>(
new ImageFromDockerfile()
.withDockerfile(Paths.get("Dockerfile.mysql-custom"))
);
mysql.start();import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
.withCommand("mysqld", "--general_log=1", "--log_output=TABLE");
mysql.start();
// View queries: SELECT * FROM mysql.general_log;import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import com.github.dockerjava.api.command.InspectContainerResponse;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();
InspectContainerResponse containerInfo = mysql.getContainerInfo();
System.out.println("Container ID: " + containerInfo.getId());
System.out.println("Container Name: " + containerInfo.getName());
System.out.println("Network Settings: " + containerInfo.getNetworkSettings());import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;
import org.testcontainers.containers.Container.ExecResult;
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"));
mysql.start();
// Execute command in container
ExecResult result = mysql.execInContainer("mysql", "--version");
System.out.println("MySQL Version: " + result.getStdout());getMappedPort() instead of assuming port numbersstart()