or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

container-management.mddocker-compose.mdindex.mdnetworking.mdutilities.mdwait-strategies.md
tile.json

wait-strategies.mddocs/

Wait Strategies

Flexible strategies for determining when containers are ready for use, supporting health checks, log messages, HTTP endpoints, port checks, and custom shell commands.

Capabilities

Wait Factory Class

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();

WaitStrategy Interface

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;
}

HttpWaitStrategy Interface

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();

Composite Wait Strategies

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();

Log Message Wait Strategy

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();

Health Check Wait Strategy

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();

Shell Command Wait Strategy

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();

One-Shot Container Wait Strategy

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 inspection

Custom Startup Timeout

All 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();

Advanced Wait Strategy Patterns

Waiting for Specific Port

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();

Combining Different Strategy Types

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();

StartupCheckStrategy Class

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.

Types

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';