or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdindex.mdjdbc-url-pattern.mdpostgresql-container.mdpostgresql-variants.mdr2dbc-support.md
tile.json

configuration.mddocs/

Container Configuration

PostgreSQL containers can be configured with initialization scripts, custom startup commands, JDBC URL parameters, environment variables, container reuse options, and network settings. All configuration methods follow a fluent API pattern, returning the container instance for method chaining. Configuration must be done before calling start().

Complete Configuration API

Initialization Scripts

Execute SQL scripts when the PostgreSQL container starts, useful for setting up database schema and test data. Scripts are executed after PostgreSQL is ready to accept connections, in the order specified.

/**
 * Set a single initialization script to execute on container startup
 * Script is executed after PostgreSQL is ready to accept connections
 * @param initScriptPath Classpath resource path to SQL script (must not be null)
 * @return this for method chaining
 * @throws IllegalArgumentException if initScriptPath is null or empty
 * @throws IllegalStateException if container is already started
 */
public SELF withInitScript(String initScriptPath);

/**
 * Set multiple initialization scripts to execute in order on container startup
 * @param initScriptPaths Variable number of classpath resource paths to SQL scripts
 * @return this for method chaining
 * @throws IllegalArgumentException if any initScriptPath is null or empty
 * @throws IllegalStateException if container is already started
 */
public SELF withInitScripts(String... initScriptPaths);

/**
 * Set multiple initialization scripts from an iterable collection
 * @param initScriptPaths Iterable of classpath resource paths to SQL scripts
 * @return this for method chaining
 * @throws IllegalArgumentException if initScriptPaths is null or contains null/empty paths
 * @throws IllegalStateException if container is already started
 */
public SELF withInitScripts(Iterable<String> initScriptPaths);

Usage Examples:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;

// Single initialization script
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withInitScript("schema.sql");

// Multiple initialization scripts (executed in order)
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withInitScripts("schema.sql", "seed-data.sql", "test-fixtures.sql");

// Using a list of scripts
List<String> scripts = Arrays.asList("schema.sql", "data.sql");
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withInitScripts(scripts);

postgres.start();

// Verify scripts executed
try (Connection conn = DriverManager.getConnection(
        postgres.getJdbcUrl(),
        postgres.getUsername(),
        postgres.getPassword());
     Statement stmt = conn.createStatement()) {
    // Tables from schema.sql should exist
}

Example init script (schema.sql):

CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_users_email ON users(email);

Error Handling for Init Scripts:

try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withInitScript("schema.sql")) {
    postgres.start();
    
    // Verify init script executed successfully
    try (Connection conn = DriverManager.getConnection(
            postgres.getJdbcUrl(),
            postgres.getUsername(),
            postgres.getPassword());
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(
             "SELECT COUNT(*) FROM information_schema.tables WHERE table_name = 'users'")) {
        rs.next();
        if (rs.getInt(1) == 0) {
            throw new IllegalStateException("Init script failed: users table not found");
        }
    }
} catch (Exception e) {
    // Check container logs for SQL errors
    System.err.println("Container logs: " + postgres.getLogs());
    throw e;
}

// Error: Init script not found
try {
    PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withInitScript("nonexistent.sql");
    postgres.start();  // May fail if script not found
} catch (Exception e) {
    // Script file not found in classpath
}

// Error: Setting init script after start
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
postgres.start();
try {
    postgres.withInitScript("schema.sql");  // Throws IllegalStateException
} catch (IllegalStateException e) {
    // Cannot configure after start
}

Command Override

Override the default PostgreSQL startup command to customize database settings. The default command is postgres -c fsync=off.

/**
 * Override the default PostgreSQL startup command with a single command string
 * Default command is "postgres -c fsync=off"
 * @param cmd Full PostgreSQL command with arguments (must not be null)
 * @return this for method chaining
 * @throws IllegalArgumentException if cmd is null
 * @throws IllegalStateException if container is already started
 */
public SELF withCommand(String cmd);

/**
 * Override the default PostgreSQL startup command with separate command parts
 * @param commandParts Command executable and arguments as separate strings
 * @return this for method chaining
 * @throws IllegalArgumentException if commandParts is null or empty
 * @throws IllegalStateException if container is already started
 */
public SELF withCommand(String... commandParts);

/**
 * Reset the command to the default (no custom command override)
 * Used to unset a previously configured command
 * @return this for method chaining
 * @throws IllegalStateException if container is already started
 */
public SELF withCommand();

Usage Examples:

// Override with single string
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c max_connections=200 -c shared_buffers=256MB");

// Override with command parts
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres", "-c", "max_connections=200", "-c", "shared_buffers=256MB");

// Set and then reset command
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c max_connections=42")  // Set custom command
    .withCommand();  // Reset to default

postgres.start();

Common PostgreSQL Configuration Options:

// Increase connection limit
.withCommand("postgres -c max_connections=200")

// Adjust memory settings
.withCommand("postgres -c shared_buffers=256MB -c work_mem=16MB")

// Enable query logging
.withCommand("postgres -c log_statement=all -c log_duration=on")

// Multiple configurations
.withCommand("postgres -c max_connections=100 -c fsync=off -c synchronous_commit=off")

// Performance optimization for testing
.withCommand("postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off")

Validating Command Configuration:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c max_connections=200");

postgres.start();

// Verify configuration was applied
try (Connection conn = DriverManager.getConnection(
        postgres.getJdbcUrl(),
        postgres.getUsername(),
        postgres.getPassword());
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SHOW max_connections")) {
    rs.next();
    int maxConnections = Integer.parseInt(rs.getString(1));
    // maxConnections should be 200
    assert maxConnections == 200;
}

JDBC URL Parameters

Add custom parameters to the JDBC connection URL. Parameters are appended to the URL as query string parameters.

/**
 * Add a parameter to the JDBC connection URL
 * Parameters are appended to the URL as query string parameters
 * @param paramName Parameter name (must not be null)
 * @param paramValue Parameter value (must not be null)
 * @return this for method chaining
 * @throws IllegalArgumentException if paramName or paramValue is null
 * @throws IllegalStateException if container is already started
 */
public SELF withUrlParam(String paramName, String paramValue);

Usage Examples:

// Add SSL parameter
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withUrlParam("ssl", "true")
    .withUrlParam("sslmode", "require");

// Add character encoding
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withUrlParam("charSet", "UTF-8");

// Add application name
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withUrlParam("ApplicationName", "MyTestSuite");

// Multiple parameters
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withUrlParam("ssl", "true")
    .withUrlParam("sslmode", "prefer")
    .withUrlParam("connectTimeout", "10")
    .withUrlParam("loginTimeout", "5");

postgres.start();
String jdbcUrl = postgres.getJdbcUrl();
// Result: jdbc:postgresql://localhost:54321/test?loggerLevel=OFF&ssl=true&sslmode=prefer&connectTimeout=10&loginTimeout=5

Common JDBC Parameters:

  • ssl - Enable SSL connection (true/false)
  • sslmode - SSL mode (disable/allow/prefer/require/verify-ca/verify-full)
  • connectTimeout - Connection timeout in seconds
  • loginTimeout - Login timeout in seconds
  • socketTimeout - Socket timeout in seconds
  • ApplicationName - Application name for connection tracking
  • charSet - Character set encoding
  • currentSchema - Default schema for queries
  • tcpKeepAlive - Enable TCP keep-alive (true/false)
  • readOnly - Read-only connection (true/false)

URL Parameter Validation:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withUrlParam("connectTimeout", "10")
    .withUrlParam("ApplicationName", "TestApp");

postgres.start();

// Verify URL contains parameters
String jdbcUrl = postgres.getJdbcUrl();
assert jdbcUrl.contains("connectTimeout=10");
assert jdbcUrl.contains("ApplicationName=TestApp");

// Error: Null parameter name
try {
    postgres.withUrlParam(null, "value");
} catch (IllegalArgumentException e) {
    // Parameter name cannot be null
}

// Error: Setting parameter after start
postgres.start();
try {
    postgres.withUrlParam("ssl", "true");  // Throws IllegalStateException
} catch (IllegalStateException e) {
    // Cannot configure after start
}

Environment Variables

Set custom environment variables for the PostgreSQL container. These are set in the container environment and can be used by PostgreSQL configuration.

/**
 * Set an environment variable in the PostgreSQL container
 * @param key Environment variable name (must not be null)
 * @param value Environment variable value (must not be null)
 * @return this for method chaining
 * @throws IllegalArgumentException if key or value is null
 * @throws IllegalStateException if container is already started
 */
public SELF withEnv(String key, String value);

/**
 * Set multiple environment variables from a map
 * @param env Map of environment variable names to values (must not be null)
 * @return this for method chaining
 * @throws IllegalArgumentException if env is null or contains null keys/values
 * @throws IllegalStateException if container is already started
 */
public SELF withEnv(Map<String, String> env);

Usage Examples:

import java.util.HashMap;
import java.util.Map;

// Set custom PostgreSQL configuration via environment variables
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withEnv("POSTGRES_INITDB_ARGS", "-E UTF8 --locale=en_US.UTF-8")
    .withEnv("PGDATA", "/var/lib/postgresql/data/pgdata");

// Set multiple environment variables
Map<String, String> envVars = new HashMap<>();
envVars.put("POSTGRES_INITDB_ARGS", "-E UTF8");
envVars.put("TZ", "UTC");

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withEnv(envVars);

postgres.start();

Automatically Set Environment Variables:

The container automatically sets these environment variables based on configuration:

  • POSTGRES_DB - Database name (from withDatabaseName())
  • POSTGRES_USER - Username (from withUsername())
  • POSTGRES_PASSWORD - Password (from withPassword())

Common Environment Variables:

  • POSTGRES_INITDB_ARGS - Arguments for initdb (database initialization)
  • POSTGRES_INITDB_WALDIR - WAL directory location
  • POSTGRES_HOST_AUTH_METHOD - Authentication method (trust/md5/scram-sha-256)
  • PGDATA - PostgreSQL data directory path
  • TZ - Timezone
  • LANG - Locale
  • LC_ALL - Locale

Verifying Environment Variables:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withEnv("TZ", "UTC")
    .withEnv("POSTGRES_INITDB_ARGS", "-E UTF8");

postgres.start();

// Verify timezone setting
try (Connection conn = DriverManager.getConnection(
        postgres.getJdbcUrl(),
        postgres.getUsername(),
        postgres.getPassword());
     Statement stmt = conn.createStatement();
     ResultSet rs = stmt.executeQuery("SHOW timezone")) {
    rs.next();
    String timezone = rs.getString(1);
    // timezone should be "UTC"
    assert timezone.equals("UTC");
}

Startup and Connection Timeouts

Configure how long to wait for container startup and database connections.

/**
 * Set the maximum time to wait for container startup
 * @param startupTimeoutSeconds Timeout in seconds (must be positive)
 * @return this for method chaining
 * @throws IllegalArgumentException if startupTimeoutSeconds is not positive
 * @throws IllegalStateException if container is already started
 */
public SELF withStartupTimeoutSeconds(int startupTimeoutSeconds);

/**
 * Set the maximum time to wait for database connections
 * @param connectTimeoutSeconds Timeout in seconds (must be positive)
 * @return this for method chaining
 * @throws IllegalArgumentException if connectTimeoutSeconds is not positive
 * @throws IllegalStateException if container is already started
 */
public SELF withConnectTimeoutSeconds(int connectTimeoutSeconds);

Usage Examples:

// Increase startup timeout for slow environments
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withStartupTimeoutSeconds(120);  // Wait up to 2 minutes

// Set connection timeout
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withConnectTimeoutSeconds(30);  // Wait up to 30 seconds for connections

// Both timeouts
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withStartupTimeoutSeconds(180)
    .withConnectTimeoutSeconds(60);

postgres.start();

Default Timeouts:

  • Startup timeout: 60 seconds
  • Connect timeout: Inherited from JdbcDatabaseContainer (typically 120 seconds)

Handling Timeout Errors:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withStartupTimeoutSeconds(30);  // Short timeout for testing

try {
    postgres.start();
} catch (ContainerLaunchException e) {
    // Container failed to start within timeout
    System.err.println("Startup timeout: " + e.getMessage());
    System.err.println("Container logs: " + postgres.getLogs());
    // Consider increasing timeout or checking Docker environment
    throw e;
}

// Error: Invalid timeout value
try {
    postgres.withStartupTimeoutSeconds(-1);  // Throws IllegalArgumentException
} catch (IllegalArgumentException e) {
    // Timeout must be positive
}

Container Reuse

Enable container reuse across multiple test runs to improve performance. Reused containers are not stopped when tests complete.

/**
 * Enable or disable container reuse across test executions
 * When enabled, the container will not be stopped after tests complete
 * and will be reused in subsequent test runs
 * @param reusable true to enable reuse, false to disable
 * @return this for method chaining
 * @throws IllegalStateException if container is already started
 */
public SELF withReuse(boolean reusable);

Usage Examples:

// Enable container reuse for faster test execution
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withReuse(true);

postgres.start();
// Container remains running after tests complete
// Next test run will reuse this container

Important Notes About Container Reuse:

  1. Container reuse requires .testcontainers.properties file in home directory with:

    testcontainers.reuse.enable=true
  2. Reused containers are identified by image name and configuration

  3. Containers are not stopped when test completes

  4. Useful for development but should typically be disabled in CI/CD pipelines

  5. Database state persists between test runs - may need explicit cleanup

Data Cleanup with Reused Containers:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withReuse(true);

postgres.start();

// Clean up data between test runs
try (Connection conn = DriverManager.getConnection(
        postgres.getJdbcUrl(),
        postgres.getUsername(),
        postgres.getPassword());
     Statement stmt = conn.createStatement()) {
    // Truncate all tables
    stmt.execute("DO $$ DECLARE r RECORD; BEGIN FOR r IN (SELECT tablename FROM pg_tables WHERE schemaname = 'public') LOOP EXECUTE 'TRUNCATE TABLE ' || quote_ident(r.tablename) || ' CASCADE'; END LOOP; END $$;");
}

Port Exposure

Configure which ports are exposed from the container. PostgreSQL containers automatically expose port 5432.

/**
 * Set the ports to expose from the container
 * PostgreSQL containers automatically expose port 5432
 * @param ports Variable number of port numbers to expose
 * @return this for method chaining
 * @throws IllegalArgumentException if any port is null or invalid
 * @throws IllegalStateException if container is already started
 */
public SELF withExposedPorts(Integer... ports);

Usage Examples:

// Expose additional ports (5432 is automatically exposed)
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withExposedPorts(5432, 9090);  // Expose PostgreSQL + custom metrics port

postgres.start();

// Access additional port
Integer metricsPort = postgres.getMappedPort(9090);

Network Configuration

Configure container network settings.

/**
 * Set the network mode for the container
 * @param networkMode Network mode (e.g., "bridge", "host", "none")
 * @return this for method chaining
 * @throws IllegalArgumentException if networkMode is null
 * @throws IllegalStateException if container is already started
 */
public SELF withNetworkMode(String networkMode);

/**
 * Set network aliases for the container
 * @param aliases Network aliases
 * @return this for method chaining
 * @throws IllegalStateException if container is already started
 */
public SELF withNetworkAliases(String... aliases);

Usage Examples:

// Create container with network isolation
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withNetworkMode("bridge");  // Isolated network

// Set network aliases
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withNetworkAliases("postgres", "db");

postgres.start();

Complete Configuration Example

import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.utility.DockerImageName;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.HashMap;
import java.util.Map;

public class AdvancedDatabaseTest {
    public void testWithFullConfiguration() throws Exception {
        DockerImageName image = DockerImageName.parse("postgres:15");

        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(image)
                // Basic configuration
                .withDatabaseName("myapp_test")
                .withUsername("testuser")
                .withPassword("testpass123")

                // Initialization
                .withInitScripts("schema.sql", "seed-data.sql")

                // PostgreSQL configuration
                .withCommand(
                    "postgres",
                    "-c", "max_connections=200",
                    "-c", "shared_buffers=256MB",
                    "-c", "log_statement=all"
                )

                // JDBC URL parameters
                .withUrlParam("ssl", "false")
                .withUrlParam("ApplicationName", "MyTestSuite")
                .withUrlParam("connectTimeout", "10")

                // Environment variables
                .withEnv("TZ", "UTC")
                .withEnv("POSTGRES_INITDB_ARGS", "-E UTF8")

                // Timeouts
                .withStartupTimeoutSeconds(120)
                .withConnectTimeoutSeconds(60)

                // Performance optimization (development only)
                .withReuse(true)) {

            postgres.start();

            // Get connection and run tests
            String jdbcUrl = postgres.getJdbcUrl();
            try (Connection conn = DriverManager.getConnection(
                    jdbcUrl,
                    postgres.getUsername(),
                    postgres.getPassword())) {
                // Run your tests
            }
        }
    }
}

Configuration Best Practices

For Fast Test Execution

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off")
    .withReuse(true);  // Reuse in development

For Production-like Testing

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c max_connections=100 -c shared_buffers=128MB")
    .withUrlParam("ssl", "true")
    .withUrlParam("sslmode", "require")
    .withStartupTimeoutSeconds(180);

For High-Volume Testing

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c max_connections=500 -c shared_buffers=512MB -c work_mem=32MB")
    .withInitScript("large-dataset.sql")
    .withStartupTimeoutSeconds(300);

For Parallel Test Execution

// Each test gets its own isolated container
@Test
void test1() {
    try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
        postgres.start();
        // Test code
    }
}

@Test
void test2() {
    try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
        postgres.start();
        // Test code
    }
}

Error Handling Patterns

Validating Configuration

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withDatabaseName("testdb")
    .withUsername("testuser")
    .withPassword("testpass");

postgres.start();

// Verify configuration
assert "testdb".equals(postgres.getDatabaseName());
assert "testuser".equals(postgres.getUsername());
assert "testpass".equals(postgres.getPassword());

Handling Init Script Failures

try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
        .withInitScript("schema.sql")) {
    postgres.start();
    
    // Verify init script executed
    try (Connection conn = DriverManager.getConnection(
            postgres.getJdbcUrl(),
            postgres.getUsername(),
            postgres.getPassword());
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(
             "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema = 'public'")) {
        rs.next();
        int tableCount = rs.getInt(1);
        if (tableCount == 0) {
            throw new IllegalStateException("Init script may have failed - no tables found");
        }
    }
} catch (Exception e) {
    System.err.println("Container logs: " + postgres.getLogs());
    throw e;
}

Configuration After Start

All configuration methods throw IllegalStateException if called after start():

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
postgres.start();

// All of these will throw IllegalStateException:
try {
    postgres.withDatabaseName("newdb");
    postgres.withInitScript("schema.sql");
    postgres.withCommand("postgres -c max_connections=200");
    postgres.withUrlParam("ssl", "true");
    postgres.withEnv("TZ", "UTC");
    postgres.withReuse(true);
} catch (IllegalStateException e) {
    // Cannot configure after start
}