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().
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
}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;
}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=5Common JDBC Parameters:
ssl - Enable SSL connection (true/false)sslmode - SSL mode (disable/allow/prefer/require/verify-ca/verify-full)connectTimeout - Connection timeout in secondsloginTimeout - Login timeout in secondssocketTimeout - Socket timeout in secondsApplicationName - Application name for connection trackingcharSet - Character set encodingcurrentSchema - Default schema for queriestcpKeepAlive - 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
}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 locationPOSTGRES_HOST_AUTH_METHOD - Authentication method (trust/md5/scram-sha-256)PGDATA - PostgreSQL data directory pathTZ - TimezoneLANG - LocaleLC_ALL - LocaleVerifying 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");
}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:
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
}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 containerImportant Notes About Container Reuse:
Container reuse requires .testcontainers.properties file in home directory with:
testcontainers.reuse.enable=trueReused containers are identified by image name and configuration
Containers are not stopped when test completes
Useful for development but should typically be disabled in CI/CD pipelines
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 $$;");
}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);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();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
}
}
}
}PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withCommand("postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off")
.withReuse(true); // Reuse in developmentPostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
.withCommand("postgres -c max_connections=100 -c shared_buffers=128MB")
.withUrlParam("ssl", "true")
.withUrlParam("sslmode", "require")
.withStartupTimeoutSeconds(180);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);// 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
}
}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());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;
}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
}