or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

container-configuration.mdcontainer-customization.mdcontainer-lifecycle.mddatabase-initialization.mdindex.mdjdbc-connection.mdr2dbc-support.mdurl-parameters-timeouts.md
tile.json

container-configuration.mddocs/

Container Configuration

This document covers the creation and configuration of MySQL containers including Docker image selection, database credentials, and MySQL server configuration overrides.

Quick Reference

Constructor Options:

  • MySQLContainer(String dockerImageName) - String-based image name
  • MySQLContainer(DockerImageName dockerImageName) - Type-safe image name (recommended)

Configuration Methods (Fluent Interface, Return SELF):

  • withDatabaseName(String databaseName) - Set database name (default: "test")
  • withUsername(String username) - Set username (default: "test")
  • withPassword(String password) - Set password (default: "test")
  • withConfigurationOverride(String configPath) - Mount custom my.cnf files from classpath directory

Getter Methods:

  • getDatabaseName(): String - Returns configured database name
  • getUsername(): String - Returns configured username
  • getPassword(): String - Returns configured password

Constraints:

  • All configuration methods must be called before start()
  • Configuration cannot be changed after container is started
  • Database name, username, password are immutable after container creation

Capabilities

Container Construction

Create a new MySQL container instance with a specified Docker image.

/**
 * Creates a MySQL container with the specified Docker image name string.
 * The image name should follow Docker image naming conventions (e.g., "mysql:8.0" or "mysql:5.7.34").
 *
 * @param dockerImageName the Docker image name as a string (e.g., "mysql:8.0", "mysql:5.7.34")
 * @throws IllegalArgumentException if dockerImageName is null or empty
 */
MySQLContainer(String dockerImageName);

/**
 * Creates a MySQL container with the specified DockerImageName object.
 * This is the recommended constructor as it provides type safety and validation.
 *
 * @param dockerImageName the Docker image name as a DockerImageName object
 * @throws IllegalArgumentException if dockerImageName is null
 */
MySQLContainer(DockerImageName dockerImageName);

Usage Example:

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

// Using string image name
MySQLContainer<?> mysql1 = new MySQLContainer<>("mysql:8.0");

// Using DockerImageName (recommended)
MySQLContainer<?> mysql2 = new MySQLContainer<>(DockerImageName.parse("mysql:8.0.36"));

Image Selection Guidelines:

  • Use specific version tags (e.g., "mysql:8.0.36") for reproducible tests
  • Use major version tags (e.g., "mysql:8.0") for flexibility with patch updates
  • Avoid "latest" tag in production tests for stability
  • MySQL 8.0+ recommended for modern applications
  • MySQL 5.7 supported but consider upgrading
  • Image must be available in Docker registry (Docker Hub by default)

Common Image Tags:

  • mysql:8.0 - Latest MySQL 8.0 (recommended)
  • mysql:8.0.36 - Specific MySQL 8.0 patch version
  • mysql:5.7 - Latest MySQL 5.7
  • mysql:5.7.34 - Specific MySQL 5.7 patch version

Database Name Configuration

Set the name of the database to be created when the container starts.

/**
 * Sets the name of the database to be created in the MySQL container.
 * This database will be automatically created when the container starts.
 * The database name cannot be changed after container creation.
 *
 * @param databaseName the database name (must be valid MySQL identifier)
 * @return self for method chaining (SELF extends MySQLContainer<SELF>)
 * @throws IllegalArgumentException if databaseName is null or empty
 */
SELF withDatabaseName(String databaseName);

/**
 * Gets the configured database name.
 * Returns the default value "test" if not explicitly set.
 *
 * @return the database name (default: "test")
 */
String getDatabaseName();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withDatabaseName("myapp_test");

mysql.start();
String dbName = mysql.getDatabaseName(); // "myapp_test"

Important Notes:

  • Database is created automatically on container startup
  • Database name must be valid MySQL identifier (alphanumeric, underscore, no spaces, not a reserved keyword)
  • Default database name is "test" if not specified
  • Database name cannot be changed after container creation
  • Database name is case-sensitive on case-sensitive filesystems
  • Maximum length: 64 characters (MySQL limit)

Valid Database Names:

  • "testdb", "myapp_test", "integration_test_db"
  • Invalid: "test db" (spaces), "test-db" (hyphens), "SELECT" (reserved keyword)

Username Configuration

Set the username for database connections.

/**
 * Sets the username for database connections.
 * If set to "root", special behavior applies for password handling.
 * The username cannot be changed after container creation.
 *
 * @param username the database username (must be valid MySQL identifier)
 * @return self for method chaining (SELF extends MySQLContainer<SELF>)
 * @throws IllegalArgumentException if username is null or empty
 */
SELF withUsername(String username);

/**
 * Gets the configured username.
 * Returns the default value "test" if not explicitly set.
 *
 * @return the username (default: "test")
 */
String getUsername();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("myuser");

mysql.start();
String user = mysql.getUsername(); // "myuser"

Important Notes:

  • The default username is "test"
  • When using username "root" with an empty password, you must also set the environment variable MYSQL_ROOT_HOST to allow root login from any host (typically set to "%")
  • When using a non-root username, an empty password will cause the container to fail to start with ContainerLaunchException
  • Username cannot be changed after container creation
  • Username must be valid MySQL identifier (alphanumeric, underscore, no spaces)

Root User Special Case:

// Root user with empty password requires MYSQL_ROOT_HOST
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("root")
    .withPassword("")
    .withEnv("MYSQL_ROOT_HOST", "%"); // Required for root with empty password

Password Configuration

Set the password for database connections.

/**
 * Sets the password for database connections.
 * The password applies to both the configured user and the root user.
 * The password cannot be changed after container creation.
 *
 * @param password the database password (can be empty string for root user only)
 * @return self for method chaining (SELF extends MySQLContainer<SELF>)
 * @throws IllegalArgumentException if password is null
 */
SELF withPassword(String password);

/**
 * Gets the configured password.
 * Returns the default value "test" if not explicitly set.
 *
 * @return the password (default: "test")
 */
String getPassword();

Usage Example:

MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("myuser")
    .withPassword("mypassword");

mysql.start();
String pwd = mysql.getPassword(); // "mypassword"

Password Behavior:

  • The default password is "test"
  • When a custom password is specified, it applies to BOTH the configured user AND the MySQL root user
  • Empty passwords are only allowed when using the "root" username (and MYSQL_ROOT_HOST environment variable is set)
  • Empty passwords with non-root usernames will throw a ContainerLaunchException at startup
  • Password cannot be changed after container creation
  • Password can contain any characters (no restrictions)

Error Scenario: Empty Password with Non-Root User

// This will throw ContainerLaunchException
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("testuser")
    .withPassword(""); // Empty password not allowed for non-root user
mysql.start(); // Throws ContainerLaunchException

Solution:

// Use root user with MYSQL_ROOT_HOST
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("root")
    .withPassword("")
    .withEnv("MYSQL_ROOT_HOST", "%");

MySQL Configuration Override

Mount custom MySQL configuration files (my.cnf) to override server settings.

/**
 * Mounts custom MySQL configuration files from a classpath directory.
 * All .cnf files in the specified directory will be mapped to /etc/mysql/conf.d
 * in the container, allowing MySQL server settings to be overridden.
 * Configuration is applied at container startup and cannot be changed without restart.
 *
 * @param configPath path to a directory on the classpath containing .cnf files
 * @return self for method chaining (SELF extends MySQLContainer<SELF>)
 * @throws IllegalArgumentException if configPath is null or empty
 * @throws ContainerLaunchException if configPath does not exist on classpath or contains no .cnf files
 */
SELF withConfigurationOverride(String configPath);

Usage Example:

// Assumes somepath/mysql_conf_override is a directory on the classpath
// containing .cnf files (e.g., custom.cnf)
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withConfigurationOverride("somepath/mysql_conf_override");

mysql.start();

Configuration Override Details:

  • The specified path must point to a directory on the classpath (typically src/test/resources/)
  • All .cnf files in that directory will be mounted into the container
  • Files are placed in /etc/mysql/conf.d/ within the container
  • Configuration settings in these files override MySQL default settings
  • This is useful for adjusting buffer sizes, timeouts, character sets, etc.
  • Configuration is applied at container startup and cannot be changed without restart
  • Path uses forward slashes regardless of OS
  • Path is case-sensitive on case-sensitive filesystems

Example my.cnf override file:

[mysqld]
innodb_max_undo_log_size=20000000
max_connections=500
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
innodb_buffer_pool_size=1G

Common Configuration Overrides:

  • max_connections - Maximum concurrent connections
  • innodb_buffer_pool_size - InnoDB buffer pool size
  • character-set-server - Server character set (e.g., "utf8mb4")
  • collation-server - Server collation
  • innodb_log_file_size - InnoDB log file size
  • max_allowed_packet - Maximum packet size
  • wait_timeout - Connection timeout
  • interactive_timeout - Interactive session timeout

Complete Configuration Example

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0.36"))
        .withDatabaseName("integration_test_db")
        .withUsername("testuser")
        .withPassword("testpass123")
        .withConfigurationOverride("mysql-config")) {

    mysql.start();

    // Container is ready with custom configuration
    String jdbcUrl = mysql.getJdbcUrl();
    // jdbc:mysql://localhost:xxxxx/integration_test_db?useSSL=false&allowPublicKeyRetrieval=true
}

Root User Configuration Example

import org.testcontainers.containers.MySQLContainer;
import org.testcontainers.utility.DockerImageName;

// Using root user with empty password
try (MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
        .withDatabaseName("testdb")
        .withUsername("root")
        .withPassword("")
        .withEnv("MYSQL_ROOT_HOST", "%")) { // Required for root with empty password

    mysql.start();
    // Container is ready for root access with no password
}

Root User Notes:

  • Root user requires MYSQL_ROOT_HOST environment variable when using empty password
  • Set MYSQL_ROOT_HOST to "%" to allow root login from any host
  • Root user has full privileges on all databases
  • Use root user only when necessary for testing administrative operations
  • Root user password is set to the same value as the configured password

Error Scenarios

ContainerLaunchException: Empty password with non-root user

// This will throw ContainerLaunchException
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("testuser")
    .withPassword(""); // Empty password not allowed for non-root user
mysql.start(); // Throws ContainerLaunchException

Solution:

// Use root user with MYSQL_ROOT_HOST
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withUsername("root")
    .withPassword("")
    .withEnv("MYSQL_ROOT_HOST", "%");

ContainerLaunchException: Invalid configuration override path

// This will throw ContainerLaunchException if path doesn't exist
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withConfigurationOverride("nonexistent/path");
mysql.start(); // Throws ContainerLaunchException

Solution:

// Ensure path exists on classpath and contains .cnf files
// Place files in src/test/resources/mysql-config/
MySQLContainer<?> mysql = new MySQLContainer<>(DockerImageName.parse("mysql:8.0"))
    .withConfigurationOverride("mysql-config"); // Valid classpath path

IllegalArgumentException: Null or empty parameters

// These will throw IllegalArgumentException
new MySQLContainer<>((String) null);
new MySQLContainer<>((DockerImageName) null);
mysql.withDatabaseName(null);
mysql.withUsername("");

Best Practices

  1. Use DockerImageName.parse(): Provides type safety and validation
  2. Specify Database Names: Use descriptive names that indicate test purpose
  3. Avoid Root User: Use dedicated test users unless root privileges are required
  4. Use Strong Passwords: Even in tests, use non-trivial passwords to catch password-related issues
  5. Version Pinning: Use specific image versions (e.g., "mysql:8.0.36") for reproducible tests
  6. Configuration Overrides: Use configuration overrides for production-like testing scenarios
  7. Document Custom Configurations: Explain why specific configurations were chosen
  8. Validate Configuration Early: Call all configuration methods before start() to catch errors early
  9. Use Fluent Interface: Chain configuration methods for readability
  10. Check Classpath Resources: Ensure configuration override paths exist before using them