Flexible strategies for determining when containers are ready for use, supporting health checks, log messages, HTTP endpoints, port checks, and custom shell commands.
Static factory class for creating wait strategies.
/**
* Factory for creating wait strategies
*/
class Wait {
/**
* Wait for all provided strategies to succeed
* @param waitStrategies - Array of wait strategies to combine
* @returns Composite wait strategy that waits for all
*/
static forAll(waitStrategies: WaitStrategy[]): WaitStrategy;
/**
* Wait for all exposed ports to be listening
* @returns Wait strategy for listening ports (default strategy)
*/
static forListeningPorts(): WaitStrategy;
/**
* Wait for specific log message to appear
* @param message - String or regex to match in logs
* @param times - Number of occurrences to wait for (default: 1)
* @returns Wait strategy for log message
*/
static forLogMessage(message: string | RegExp, times?: number): WaitStrategy;
/**
* Wait for Docker health check to pass
* @returns Wait strategy for health check
*/
static forHealthCheck(): WaitStrategy;
/**
* Wait for one-shot container to complete and exit
* @returns Wait strategy for container exit
*/
static forOneShotStartup(): WaitStrategy;
/**
* Wait for HTTP endpoint to respond
* @param path - HTTP path to request (e.g., '/health', '/')
* @param port - Container port to connect to
* @param options - Additional HTTP wait options
* @returns HTTP wait strategy with additional configuration methods
*/
static forHttp(
path: string,
port: number,
options?: HttpWaitStrategyOptions
): HttpWaitStrategy;
/**
* Wait for shell command to execute successfully
* @param command - Shell command to execute in container
* @returns Wait strategy for command success
*/
static forSuccessfulCommand(command: string): WaitStrategy;
}Basic Usage Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Wait for log message
const redis = await new GenericContainer('redis:7-alpine')
.withExposedPorts(6379)
.withWaitStrategy(Wait.forLogMessage('Ready to accept connections'))
.start();
// Wait for health check
const postgres = await new GenericContainer('postgres:14')
.withExposedPorts(5432)
.withHealthCheck({
test: ['CMD-SHELL', 'pg_isready -U postgres'],
interval: 1000,
timeout: 3000,
retries: 5
})
.withWaitStrategy(Wait.forHealthCheck())
.start();
// Wait for HTTP endpoint
const api = await new GenericContainer('myapi:latest')
.withExposedPorts(3000)
.withWaitStrategy(Wait.forHttp('/health', 3000))
.start();
// Wait for listening ports (default)
const nginx = await new GenericContainer('nginx:alpine')
.withExposedPorts(80)
.withWaitStrategy(Wait.forListeningPorts())
.start();
// Wait for command success
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(8080)
.withWaitStrategy(Wait.forSuccessfulCommand('curl -f http://localhost:8080/ready'))
.start();Base interface for all wait strategies with timeout configuration.
/**
* Base interface for wait strategies
*/
interface WaitStrategy {
/**
* Set startup timeout for this wait strategy
* @param startupTimeoutMs - Timeout in milliseconds
* @returns This wait strategy for chaining
*/
withStartupTimeout(startupTimeoutMs: number): WaitStrategy;
}Advanced HTTP-based wait strategy with response validation and authentication.
/**
* HTTP endpoint wait strategy with advanced configuration
*/
interface HttpWaitStrategy extends WaitStrategy {
/**
* Expect specific HTTP status code
* @param statusCode - Expected status code (e.g., 200, 204)
* @returns This strategy for chaining
*/
forStatusCode(statusCode: number): this;
/**
* Use custom status code validation
* @param predicate - Function returning true for acceptable status codes
* @returns This strategy for chaining
*/
forStatusCodeMatching(predicate: (statusCode: number) => boolean): this;
/**
* Use custom response body validation
* @param predicate - Function returning true for acceptable responses
* @returns This strategy for chaining
*/
forResponsePredicate(predicate: (response: string) => boolean): this;
/**
* Set HTTP method
* @param method - HTTP method (default: 'GET')
* @returns This strategy for chaining
*/
withMethod(method: string): this;
/**
* Set request headers
* @param headers - Headers object
* @returns This strategy for chaining
*/
withHeaders(headers: { [key: string]: string }): this;
/**
* Set basic authentication credentials
* @param username - Username
* @param password - Password
* @returns This strategy for chaining
*/
withBasicCredentials(username: string, password: string): this;
/**
* Set read timeout for HTTP requests
* @param startupTimeoutMs - Timeout in milliseconds
* @returns This strategy for chaining
*/
withReadTimeout(startupTimeoutMs: number): this;
/**
* Use HTTPS instead of HTTP
* @returns This strategy for chaining
*/
usingTls(): this;
/**
* Allow insecure TLS connections (skip certificate validation)
* @returns This strategy for chaining
*/
allowInsecure(): this;
}HTTP Wait Strategy Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Basic HTTP health check
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(3000)
.withWaitStrategy(
Wait.forHttp('/health', 3000)
.forStatusCode(200)
.withStartupTimeout(60000)
)
.start();
// HTTP with custom status code validation
const api = await new GenericContainer('myapi:latest')
.withExposedPorts(8080)
.withWaitStrategy(
Wait.forHttp('/api/health', 8080)
.forStatusCodeMatching((status) => status >= 200 && status < 300)
.withMethod('GET')
)
.start();
// HTTP with response body validation
const service = await new GenericContainer('service:latest')
.withExposedPorts(9000)
.withWaitStrategy(
Wait.forHttp('/status', 9000)
.forResponsePredicate((body) => body.includes('"status":"ready"'))
.withHeaders({
'Accept': 'application/json',
'User-Agent': 'testcontainers'
})
)
.start();
// HTTPS with authentication
const secureApp = await new GenericContainer('secure-app:latest')
.withExposedPorts(443)
.withWaitStrategy(
Wait.forHttp('/health', 443)
.usingTls()
.allowInsecure() // For self-signed certificates
.withBasicCredentials('admin', 'password')
.forStatusCode(200)
)
.start();
// POST request health check
const worker = await new GenericContainer('worker:latest')
.withExposedPorts(8080)
.withWaitStrategy(
Wait.forHttp('/api/ping', 8080)
.withMethod('POST')
.withHeaders({ 'Content-Type': 'application/json' })
.forStatusCode(204)
)
.start();Combine multiple wait strategies for complex readiness conditions.
Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Wait for both log message AND HTTP endpoint
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(3000)
.withWaitStrategy(
Wait.forAll([
Wait.forLogMessage('Database connected'),
Wait.forLogMessage('Cache initialized'),
Wait.forHttp('/health', 3000).forStatusCode(200)
])
)
.start();
// Wait for multiple log messages
const postgres = await new GenericContainer('postgres:14')
.withExposedPorts(5432)
.withWaitStrategy(
Wait.forAll([
Wait.forLogMessage('database system is ready to accept connections', 2),
Wait.forListeningPorts()
])
)
.start();
// Complex readiness with timeout
const complexApp = await new GenericContainer('complex:latest')
.withExposedPorts(8080, 9090)
.withWaitStrategy(
Wait.forAll([
Wait.forLogMessage('Application started'),
Wait.forHttp('/health', 8080),
Wait.forHttp('/metrics', 9090),
Wait.forSuccessfulCommand('curl -f http://localhost:8080/ready')
]).withStartupTimeout(120000) // 2 minutes for all strategies
)
.start();Wait for specific log patterns with repeat counts.
Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Wait for simple string
const redis = await new GenericContainer('redis:7-alpine')
.withExposedPorts(6379)
.withWaitStrategy(Wait.forLogMessage('Ready to accept connections'))
.start();
// Wait for regex pattern
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(3000)
.withWaitStrategy(Wait.forLogMessage(/Server (?:started|listening) on port \d+/))
.start();
// Wait for message to appear multiple times
const postgres = await new GenericContainer('postgres:14')
.withExposedPorts(5432)
.withWaitStrategy(
Wait.forLogMessage('database system is ready to accept connections', 2)
.withStartupTimeout(60000)
)
.start();
// Wait for multiple different log messages
const service = await new GenericContainer('service:latest')
.withExposedPorts(8080)
.withWaitStrategy(
Wait.forAll([
Wait.forLogMessage('Config loaded'),
Wait.forLogMessage('Database connected'),
Wait.forLogMessage('Server started')
])
)
.start();Wait for Docker health checks to pass.
Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Container with built-in health check
const postgres = await new GenericContainer('postgres:14')
.withExposedPorts(5432)
.withWaitStrategy(Wait.forHealthCheck())
.start();
// Container with custom health check
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(3000)
.withHealthCheck({
test: ['CMD', 'curl', '-f', 'http://localhost:3000/health'],
interval: 5000,
timeout: 3000,
retries: 3,
startPeriod: 10000
})
.withWaitStrategy(Wait.forHealthCheck())
.start();
// Health check with startup timeout
const database = await new GenericContainer('mysql:8')
.withExposedPorts(3306)
.withHealthCheck({
test: ['CMD', 'mysqladmin', 'ping', '-h', 'localhost'],
interval: 2000,
timeout: 1000,
retries: 10
})
.withWaitStrategy(
Wait.forHealthCheck().withStartupTimeout(120000)
)
.start();Wait for a shell command to execute successfully inside the container.
Examples:
import { GenericContainer, Wait } from 'testcontainers';
// Wait for file to exist
const app = await new GenericContainer('myapp:latest')
.withExposedPorts(3000)
.withWaitStrategy(Wait.forSuccessfulCommand('test -f /tmp/ready'))
.start();
// Wait for process to be running
const service = await new GenericContainer('service:latest')
.withWaitStrategy(Wait.forSuccessfulCommand('pgrep -f myservice'))
.start();
// Wait for database to accept connections
const postgres = await new GenericContainer('postgres:14')
.withExposedPorts(5432)
.withWaitStrategy(
Wait.forSuccessfulCommand('pg_isready -U postgres')
.withStartupTimeout(60000)
)
.start();
// Wait for HTTP endpoint via curl
const api = await new GenericContainer('myapi:latest')
.withExposedPorts(8080)
.withWaitStrategy(
Wait.forSuccessfulCommand('curl -f http://localhost:8080/health')
)
.start();Wait for containers that execute a task and exit.
Example:
import { GenericContainer, Wait } from 'testcontainers';
// Wait for migration script to complete
const migration = await new GenericContainer('myapp-migrate:latest')
.withEnvironment({
DATABASE_URL: 'postgresql://localhost:5432/mydb'
})
.withWaitStrategy(Wait.forOneShotStartup())
.start();
// Container will be considered "ready" after it exits
// Check exit code via logs or inspectionAll wait strategies support custom timeouts:
import { GenericContainer, Wait } from 'testcontainers';
// Short timeout for fast-starting containers
const redis = await new GenericContainer('redis:7-alpine')
.withExposedPorts(6379)
.withWaitStrategy(
Wait.forLogMessage('Ready to accept connections')
.withStartupTimeout(10000) // 10 seconds
)
.start();
// Long timeout for slow-starting containers
const elasticsearch = await new GenericContainer('elasticsearch:8.9.0')
.withExposedPorts(9200)
.withEnvironment({
'discovery.type': 'single-node',
'xpack.security.enabled': 'false'
})
.withWaitStrategy(
Wait.forHttp('/_cluster/health', 9200)
.forStatusCode(200)
.withStartupTimeout(180000) // 3 minutes
)
.start();
// Combined strategies with overall timeout
const app = await new GenericContainer('complex-app:latest')
.withExposedPorts(3000)
.withWaitStrategy(
Wait.forAll([
Wait.forLogMessage('Database migration complete'),
Wait.forLogMessage('Cache warming complete'),
Wait.forHttp('/health', 3000)
]).withStartupTimeout(300000) // 5 minutes total
)
.start();import { GenericContainer, Wait } from 'testcontainers';
// Default: waits for all exposed ports
const container = await new GenericContainer('myapp:latest')
.withExposedPorts(8080, 9090)
.withWaitStrategy(Wait.forListeningPorts())
.start();import { GenericContainer, Wait } from 'testcontainers';
const fullstack = await new GenericContainer('fullstack:latest')
.withExposedPorts(3000, 3001)
.withWaitStrategy(
Wait.forAll([
// Backend ready
Wait.forHttp('/api/health', 3000).forStatusCode(200),
// Frontend ready
Wait.forHttp('/', 3001).forStatusCodeMatching(s => s < 400),
// Worker started
Wait.forLogMessage('Background worker started'),
// Cache connected
Wait.forSuccessfulCommand('redis-cli ping')
]).withStartupTimeout(120000)
)
.start();Advanced base class for custom startup check strategies.
/**
* Abstract base class for startup check strategies
* Allows custom logic to determine container startup state
*/
abstract class StartupCheckStrategy extends WaitStrategy {
/**
* Check the startup state of a container
* @param dockerClient - Dockerode client instance
* @param containerId - Container ID to check
* @returns Promise resolving to startup status
*/
abstract checkStartupState(dockerClient: Dockerode, containerId: string): Promise<StartupStatus>;
}
type StartupStatus = 'PENDING' | 'SUCCESS' | 'FAIL';This is an advanced API for implementing custom wait strategies that need low-level access to the Docker client to determine readiness.
interface WaitStrategy {
withStartupTimeout(startupTimeoutMs: number): WaitStrategy;
}
interface HttpWaitStrategyOptions {
abortOnContainerExit?: boolean;
}
interface HttpWaitStrategy extends WaitStrategy {
forStatusCode(statusCode: number): this;
forStatusCodeMatching(predicate: (statusCode: number) => boolean): this;
forResponsePredicate(predicate: (response: string) => boolean): this;
withMethod(method: string): this;
withHeaders(headers: { [key: string]: string }): this;
withBasicCredentials(username: string, password: string): this;
withReadTimeout(startupTimeoutMs: number): this;
usingTls(): this;
allowInsecure(): this;
}
type StartupStatus = 'PENDING' | 'SUCCESS' | 'FAIL';