Testcontainers module for creating throwaway MySQL database containers for integration testing
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()Install with Tessl CLI
npx tessl i tessl/maven-org-testcontainers--mysql