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

index.mddocs/

Testcontainers PostgreSQL

Testcontainers PostgreSQL provides lightweight, throwable PostgreSQL database container instances for Java integration testing. It extends JdbcDatabaseContainer to offer seamless integration with PostgreSQL database instances running in Docker containers, automatically managing the container lifecycle and providing JDBC connection details.

Package Information

  • Package Name: postgresql
  • Package Coordinates: org.testcontainers:postgresql
  • Package Type: maven
  • Language: Java
  • Version: 1.21.4
  • Installation:
    <dependency>
        <groupId>org.testcontainers</groupId>
        <artifactId>postgresql</artifactId>
        <version>1.21.4</version>
        <scope>test</scope>
    </dependency>

Core Imports

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

For R2DBC support:

import org.testcontainers.containers.PostgreSQLR2DBCDatabaseContainer;
import io.r2dbc.spi.ConnectionFactoryOptions;

Quick Start

import org.testcontainers.containers.PostgreSQLContainer;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

public class PostgreSQLTest {
    public void testWithPostgreSQL() throws Exception {
        // Create and start a PostgreSQL container
        try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
            postgres.start();

            // Get connection details
            String jdbcUrl = postgres.getJdbcUrl();
            String username = postgres.getUsername();
            String password = postgres.getPassword();

            // Connect and execute queries
            try (Connection conn = DriverManager.getConnection(jdbcUrl, username, password);
                 Statement stmt = conn.createStatement()) {
                ResultSet rs = stmt.executeQuery("SELECT 1");
                rs.next();
                int result = rs.getInt(1);
                // result is 1
            }
        } // Container automatically stopped
    }
}

Architecture

The Testcontainers PostgreSQL module is built around several key components:

  • PostgreSQLContainer: Main container class providing full lifecycle management and configuration
  • JdbcDatabaseContainer: Base class providing JDBC connectivity and database-specific abstractions
  • Container Providers: Factory classes for creating containers via JDBC URL patterns (PostgreSQLContainerProvider, PostgisContainerProvider, TimescaleDBContainerProvider, PgVectorContainerProvider)
  • R2DBC Support: Reactive database connectivity through PostgreSQLR2DBCDatabaseContainer
  • Wait Strategies: Automatic readiness detection by monitoring PostgreSQL log messages
  • Service Provider Interface: Java SPI mechanism for automatic registration of JDBC and R2DBC providers

Complete API Reference

PostgreSQLContainer Class

Core PostgreSQL container class with full lifecycle management, configuration, and JDBC connectivity.

public class PostgreSQLContainer<SELF extends PostgreSQLContainer<SELF>>
    extends JdbcDatabaseContainer<SELF> {

    // Constants
    public static final String NAME = "postgresql";
    public static final String IMAGE = "postgres";
    public static final String DEFAULT_TAG = "9.6.12";
    public static final Integer POSTGRESQL_PORT = 5432;

    // Constructors
    @Deprecated
    public PostgreSQLContainer();
    public PostgreSQLContainer(String dockerImageName);
    public PostgreSQLContainer(DockerImageName dockerImageName);

    // Connection information (PostgreSQLContainer-specific)
    public String getDriverClassName();
    public String getJdbcUrl();
    public String getDatabaseName();
    public String getUsername();
    public String getPassword();
    public String getTestQueryString();

    // Configuration methods (PostgreSQLContainer-specific)
    public SELF withDatabaseName(String databaseName);
    public SELF withUsername(String username);
    public SELF withPassword(String password);
}

Inherited Methods from JdbcDatabaseContainer:

// Lifecycle management
public void start();
public void stop();
public boolean isRunning();
public boolean isCreated();

// Port and network information
public String getHost();
public Integer getMappedPort(int originalPort);
public List<Integer> getExposedPorts();
public Set<Integer> getLivenessCheckPortNumbers();
public String getContainerId();
public String getContainerName();

// Configuration (inherited from JdbcDatabaseContainer)
public SELF withInitScript(String initScriptPath);
public SELF withInitScripts(String... initScriptPaths);
public SELF withInitScripts(Iterable<String> initScriptPaths);
public SELF withCommand(String cmd);
public SELF withCommand(String... commandParts);
public SELF withCommand();
public SELF withUrlParam(String paramName, String paramValue);
public SELF withEnv(String key, String value);
public SELF withEnv(Map<String, String> env);
public SELF withStartupTimeoutSeconds(int startupTimeoutSeconds);
public SELF withConnectTimeoutSeconds(int connectTimeoutSeconds);
public SELF withReuse(boolean reusable);
public SELF withExposedPorts(Integer... ports);
public SELF withNetworkMode(String networkMode);
public SELF withNetworkAliases(String... aliases);

// Container information
public String getDockerImageName();
public String getLogs();
public String getLogs(OutputFrame.OutputType outputType);
public ExecResult execInContainer(String... command);
public ExecResult execInContainer(String user, String... command);
public ExecResult execInContainer(Duration timeout, String... command);

// Wait strategies
public SELF waitingFor(WaitStrategy waitStrategy);
public SELF withStartupCheckStrategy(StartupCheckStrategy strategy);

// Resource management
public void close();

Supported Docker Images:

  • postgres - Official PostgreSQL images (all versions)
  • pgvector/pgvector - PostgreSQL with vector similarity search extension

Default Configuration:

  • Database Name: "test"
  • Username: "test"
  • Password: "test"
  • Port: 5432 (container port, mapped to random host port)
  • Default Command: "postgres -c fsync=off" (optimized for testing)
  • Startup Timeout: 60 seconds
  • Connection Timeout: 120 seconds (inherited from base class)

Complete PostgreSQL Container Documentation

Container Configuration

Fluent API for configuring PostgreSQL containers with custom settings, environment variables, initialization scripts, and startup parameters.

// PostgreSQLContainer-specific configuration
public SELF withDatabaseName(String databaseName);
public SELF withUsername(String username);
public SELF withPassword(String password);

// Inherited configuration methods (from JdbcDatabaseContainer)
public SELF withInitScript(String initScriptPath);
public SELF withInitScripts(String... initScriptPaths);
public SELF withInitScripts(Iterable<String> initScriptPaths);
public SELF withCommand(String cmd);
public SELF withCommand(String... commandParts);
public SELF withCommand();
public SELF withUrlParam(String paramName, String paramValue);
public SELF withEnv(String key, String value);
public SELF withEnv(Map<String, String> env);
public SELF withStartupTimeoutSeconds(int startupTimeoutSeconds);
public SELF withConnectTimeoutSeconds(int connectTimeoutSeconds);
public SELF withReuse(boolean reusable);
public SELF withExposedPorts(Integer... ports);
public SELF withNetworkMode(String networkMode);
public SELF withNetworkAliases(String... aliases);
public SELF waitingFor(WaitStrategy waitStrategy);
public SELF withStartupCheckStrategy(StartupCheckStrategy strategy);

Complete Configuration Documentation

JDBC URL Pattern (Automatic Lifecycle)

Special JDBC URL scheme that automatically manages container lifecycle without explicit start/stop calls.

// JDBC URL Format
// jdbc:tc:postgresql:<version>://<host>/<database>?user=<user>&password=<password>

public class PostgreSQLContainerProvider extends JdbcDatabaseContainerProvider {
    public static final String USER_PARAM = "user";
    public static final String PASSWORD_PARAM = "password";

    public boolean supports(String databaseType);
    public JdbcDatabaseContainer newInstance();
    public JdbcDatabaseContainer newInstance(String tag);
    public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl);
}

JDBC URL Format:

jdbc:tc:postgresql:<version>://<host>/<database>?<parameters>

Supported Parameters:

  • user - Database username (default: "test")
  • password - Database password (default: "test")
  • TC_INITSCRIPT - Path to initialization script (classpath resource)
  • TC_INITFUNCTION - Init function class name
  • TC_DAEMON - Keep container running (true/false)
  • TC_TMPFS - Tmpfs mount configuration
  • Standard PostgreSQL JDBC parameters (ssl, sslmode, connectTimeout, etc.)

JDBC URL Pattern Documentation

R2DBC Support

Reactive database connectivity support for non-blocking PostgreSQL access using R2DBC.

public class PostgreSQLR2DBCDatabaseContainer implements R2DBCDatabaseContainer {
    public PostgreSQLR2DBCDatabaseContainer(PostgreSQLContainer<?> container);

    public static ConnectionFactoryOptions getOptions(PostgreSQLContainer<?> container);
    public ConnectionFactoryOptions configure(ConnectionFactoryOptions options);

    public void start();
    public void stop();
}

public class PostgreSQLR2DBCDatabaseContainerProvider
    implements R2DBCDatabaseContainerProvider {

    public boolean supports(ConnectionFactoryOptions options);
    public R2DBCDatabaseContainer createContainer(ConnectionFactoryOptions options);
    public ConnectionFactoryMetadata getMetadata(ConnectionFactoryOptions options);
}

R2DBC URL Format:

r2dbc:tc:postgresql:///<database>?TC_IMAGE_TAG=<version>

R2DBC Support Documentation

PostgreSQL Variants

Support for specialized PostgreSQL distributions including PostGIS, TimescaleDB, and PgVector.

// PostGIS Provider
public class PostgisContainerProvider extends JdbcDatabaseContainerProvider {
    public static final String USER_PARAM = "user";
    public static final String PASSWORD_PARAM = "password";
    public boolean supports(String databaseType); // Returns true for "postgis"
    public JdbcDatabaseContainer newInstance(); // Default: postgis/postgis:12-3.0
    public JdbcDatabaseContainer newInstance(String tag);
    public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl);
}

// TimescaleDB Provider
public class TimescaleDBContainerProvider extends JdbcDatabaseContainerProvider {
    public static final String USER_PARAM = "user";
    public static final String PASSWORD_PARAM = "password";
    public boolean supports(String databaseType); // Returns true for "timescaledb"
    public JdbcDatabaseContainer newInstance(); // Default: timescale/timescaledb:2.1.0-pg11
    public JdbcDatabaseContainer newInstance(String tag);
    public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl);
}

// PgVector Provider
public class PgVectorContainerProvider extends JdbcDatabaseContainerProvider {
    public static final String USER_PARAM = "user";
    public static final String PASSWORD_PARAM = "password";
    public boolean supports(String databaseType); // Returns true for "pgvector"
    public JdbcDatabaseContainer newInstance(); // Default: pgvector/pgvector:pg16
    public JdbcDatabaseContainer newInstance(String tag);
    public JdbcDatabaseContainer newInstance(ConnectionUrl connectionUrl);
}

JDBC URL Formats:

jdbc:tc:postgis:<version>://<host>/<database>?user=<user>&password=<password>
jdbc:tc:timescaledb:<version>://<host>/<database>?user=<user>&password=<password>
jdbc:tc:pgvector://<host>/<database>?user=<user>&password=<password>

PostgreSQL Variants Documentation

Types

// DockerImageName - Used for specifying and validating Docker images
public class DockerImageName {
    public static DockerImageName parse(String fullImageName);
    public DockerImageName withTag(String tag);
    public DockerImageName asCompatibleSubstituteFor(String... baseImageNames);
}

Error Handling and Troubleshooting

Common Errors

Container startup timeout:

// Increase startup timeout for slow environments
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withStartupTimeoutSeconds(180);

Connection failures:

try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
    postgres.start();
    
    // Wait for container to be ready
    if (!postgres.isRunning()) {
        throw new IllegalStateException("Container failed to start");
    }
    
    // Verify connection
    try (Connection conn = DriverManager.getConnection(
            postgres.getJdbcUrl(),
            postgres.getUsername(),
            postgres.getPassword())) {
        // Connection successful
    } catch (SQLException e) {
        // Handle connection error
        System.err.println("Connection failed: " + e.getMessage());
        System.err.println("Container logs: " + postgres.getLogs());
        throw e;
    }
}

Docker image pull failures:

// Use DockerImageName for better error messages
DockerImageName image = DockerImageName.parse("postgres:15")
    .asCompatibleSubstituteFor("postgres");
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>(image);

Port conflicts:

// Check if port is already in use
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
try {
    postgres.start();
} catch (Exception e) {
    if (e.getMessage().contains("port") || e.getMessage().contains("bind")) {
        System.err.println("Port conflict detected. Container will use random port.");
        // Retry - container will automatically use different port
    }
    throw e;
}

Resource Cleanup

Always use try-with-resources to ensure proper cleanup:

// Correct: Automatic cleanup
try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
    postgres.start();
    // Use container
} // Container automatically stopped and removed

// Incorrect: Manual cleanup (may leak resources on exceptions)
PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15");
postgres.start();
try {
    // Use container
} finally {
    postgres.stop(); // May not execute if exception occurs
}

Thread Safety

PostgreSQLContainer instances are not thread-safe. Each test should use its own container instance:

// Correct: One container per test
@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
    }
}

// Incorrect: Shared container across threads
private static PostgreSQLContainer<?> sharedContainer; // NOT thread-safe

Debugging

Enable container logging for troubleshooting:

try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
    postgres.start();
    
    // Get container logs
    String logs = postgres.getLogs();
    System.out.println("Container logs: " + logs);
    
    // Get container info
    System.out.println("Container ID: " + postgres.getContainerId());
    System.out.println("Container name: " + postgres.getContainerName());
    System.out.println("Host: " + postgres.getHost());
    System.out.println("Port: " + postgres.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT));
    System.out.println("Docker image: " + postgres.getDockerImageName());
}

Performance Considerations

Container Reuse

For faster test execution in development, enable container reuse:

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

Note: Requires .testcontainers.properties file in home directory with:

testcontainers.reuse.enable=true

Connection Pooling

Use connection pooling for better performance:

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

try (PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")) {
    postgres.start();
    
    HikariConfig config = new HikariConfig();
    config.setJdbcUrl(postgres.getJdbcUrl());
    config.setUsername(postgres.getUsername());
    config.setPassword(postgres.getPassword());
    config.setMaximumPoolSize(10);
    config.setMinimumIdle(2);
    
    try (HikariDataSource dataSource = new HikariDataSource(config)) {
        // Use pooled connections
    }
}

Optimization Settings

For faster test execution, use optimized PostgreSQL settings:

PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:15")
    .withCommand("postgres -c fsync=off -c synchronous_commit=off -c full_page_writes=off");

Integration Patterns

JUnit 5 with Shared Container

import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.containers.PostgreSQLContainer;

@Testcontainers
public class SharedContainerTest {
    @Container
    private static final PostgreSQLContainer<?> postgres = 
        new PostgreSQLContainer<>("postgres:15")
            .withInitScript("schema.sql");
    
    @Test
    void test1() {
        String jdbcUrl = postgres.getJdbcUrl();
        // Use container
    }
    
    @Test
    void test2() {
        String jdbcUrl = postgres.getJdbcUrl();
        // Use same container instance
    }
}

JUnit 5 with Per-Test Container

import org.junit.jupiter.api.Test;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.containers.PostgreSQLContainer;

@Testcontainers
public class PerTestContainerTest {
    @Container
    private final PostgreSQLContainer<?> postgres = 
        new PostgreSQLContainer<>("postgres:15");
    
    @Test
    void test1() {
        String jdbcUrl = postgres.getJdbcUrl();
        // Each test gets its own container
    }
    
    @Test
    void test2() {
        String jdbcUrl = postgres.getJdbcUrl();
        // Different container instance
    }
}

Spring Boot Integration

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;

@SpringBootTest
@Testcontainers
public class SpringBootTest {
    @Container
    private static final PostgreSQLContainer<?> postgres = 
        new PostgreSQLContainer<>("postgres:15");
    
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        postgres.start();
        registry.add("spring.datasource.url", postgres::getJdbcUrl);
        registry.add("spring.datasource.username", postgres::getUsername);
        registry.add("spring.datasource.password", postgres::getPassword);
        registry.add("spring.datasource.driver-class-name", 
            () -> "org.postgresql.Driver");
    }
}

Spring Boot with JDBC URL Pattern

import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;

@SpringBootTest
public class SpringBootJdbcUrlTest {
    @DynamicPropertySource
    static void configureProperties(DynamicPropertyRegistry registry) {
        registry.add("spring.datasource.url",
            () -> "jdbc:tc:postgresql:15:///testdb?TC_INITSCRIPT=schema.sql");
        registry.add("spring.datasource.driver-class-name",
            () -> "org.testcontainers.jdbc.ContainerDatabaseDriver");
    }
}