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

container-management.mddocs/

Container Management

Core container lifecycle management with comprehensive configuration options for creating, starting, and controlling Docker containers.

Capabilities

GenericContainer Class

Main class for creating and configuring Docker containers with a fluent builder API.

/**
 * Creates a container from a Docker image
 * @param image - Docker image name (e.g., 'redis:7-alpine', 'postgres:14')
 */
class GenericContainer {
  constructor(image: string);

  /**
   * Create container from a Dockerfile
   * @param context - Build context directory path
   * @param dockerfileName - Dockerfile name (default: 'Dockerfile')
   * @returns Builder for configuring the Docker build
   */
  static fromDockerfile(
    context: string,
    dockerfileName?: string
  ): GenericContainerBuilder;

  /**
   * Start the container with configured settings
   * @returns Promise resolving to started container instance
   */
  start(): Promise<StartedTestContainer>;

  // Command and entrypoint configuration
  withCommand(command: string[]): this;
  withEntrypoint(entrypoint: string[]): this;
  withName(name: string): this;
  withWorkingDir(workingDir: string): this;
  withUser(user: string): this;
  withHostname(hostname: string): this;

  // Environment configuration
  withEnvironment(environment: Environment): this;
  withLabels(labels: Labels): this;
  withPlatform(platform: string): this;

  // Network configuration
  withExposedPorts(...ports: PortWithOptionalBinding[]): this;
  withNetwork(network: StartedNetwork): this;
  withNetworkMode(networkMode: string): this;
  withNetworkAliases(...networkAliases: string[]): this;
  withExtraHosts(extraHosts: ExtraHost[]): this;
  withIpcMode(ipcMode: string): this;

  // File and volume operations
  withBindMounts(bindMounts: BindMount[]): this;
  withTmpFs(tmpFs: TmpFs): this;
  withCopyFilesToContainer(filesToCopy: FileToCopy[]): this;
  withCopyDirectoriesToContainer(directoriesToCopy: DirectoryToCopy[]): this;
  withCopyContentToContainer(contentsToCopy: ContentToCopy[]): this;
  withCopyArchivesToContainer(archivesToCopy: ArchiveToCopy[]): this;

  // Resource and capability configuration
  withResourcesQuota(resourcesQuota: ResourcesQuota): this;
  withSharedMemorySize(bytes: number): this;
  withUlimits(ulimits: Ulimits): this;
  withAddedCapabilities(...capabilities: string[]): this;
  withDroppedCapabilities(...capabilities: string[]): this;
  withPrivilegedMode(): this;

  // Health and readiness
  withHealthCheck(healthCheck: HealthCheck): this;
  withWaitStrategy(waitStrategy: WaitStrategy): this;
  withStartupTimeout(startupTimeoutMs: number): this;

  // Lifecycle options
  withReuse(): this;
  withAutoRemove(autoRemove: boolean): this;
  withPullPolicy(pullPolicy: ImagePullPolicy): this;
  withDefaultLogDriver(): this;
  withLogConsumer(logConsumer: (stream: Readable) => unknown): this;
}

Usage Examples:

import { GenericContainer, Wait } from 'testcontainers';

// Basic container with port mapping
const redis = await new GenericContainer('redis:7-alpine')
  .withExposedPorts(6379)
  .start();

// Container with environment variables and volume
const postgres = await new GenericContainer('postgres:14')
  .withEnvironment({
    POSTGRES_USER: 'test',
    POSTGRES_PASSWORD: 'test',
    POSTGRES_DB: 'testdb'
  })
  .withExposedPorts(5432)
  .withBindMounts([{
    source: '/local/data',
    target: '/var/lib/postgresql/data',
    mode: 'rw'
  }])
  .withWaitStrategy(Wait.forLogMessage('database system is ready to accept connections'))
  .start();

// Container with resource limits
const app = await new GenericContainer('myapp:latest')
  .withExposedPorts(8080)
  .withResourcesQuota({
    memory: 0.512, // 512MB in GB
    cpu: 1
  })
  .withUser('appuser')
  .withWorkingDir('/app')
  .start();

// Container with file copying
const nginx = await new GenericContainer('nginx:alpine')
  .withExposedPorts(80)
  .withCopyFilesToContainer([{
    source: './config/nginx.conf',
    target: '/etc/nginx/nginx.conf'
  }])
  .withCopyContentToContainer([{
    content: '<html><body>Hello</body></html>',
    target: '/usr/share/nginx/html/index.html'
  }])
  .start();

GenericContainerBuilder Class

Builder for creating containers from Dockerfiles with build-time configuration.

/**
 * Builder for containers created from Dockerfiles
 */
class GenericContainerBuilder {
  /**
   * Set Docker build arguments
   * @param buildArgs - Key-value pairs of build arguments
   */
  withBuildArgs(buildArgs: BuildArgs): this;

  /**
   * Set image pull policy for base images during build
   * @param pullPolicy - Pull policy configuration
   */
  withPullPolicy(pullPolicy: ImagePullPolicy): this;

  /**
   * Enable or disable build cache
   * @param cache - Whether to use build cache (default: true)
   */
  withCache(cache: boolean): this;

  /**
   * Enable BuildKit for the build
   */
  withBuildkit(): this;

  /**
   * Set target platform for multi-platform builds
   * @param platform - Platform string (e.g., 'linux/amd64', 'linux/arm64')
   */
  withPlatform(platform: string): this;

  /**
   * Set build target stage for multi-stage Dockerfiles
   * @param target - Target stage name
   */
  withTarget(target: string): this;

  /**
   * Build the image and return a GenericContainer instance
   * @param image - Optional custom image name (default: auto-generated)
   * @param options - Build options including deleteOnExit flag
   * @returns Promise resolving to GenericContainer instance
   */
  build(image?: string, options?: BuildOptions): Promise<GenericContainer>;
}

Usage Example:

import { GenericContainer } from 'testcontainers';

// Build from Dockerfile with build args
const container = await GenericContainer
  .fromDockerfile('./my-app', 'Dockerfile')
  .withBuildArgs({
    NODE_VERSION: '18',
    BUILD_ENV: 'test'
  })
  .withTarget('test')
  .withBuildkit()
  .build();

const started = await container
  .withExposedPorts(3000)
  .start();

StartedTestContainer Interface

Represents a running container with control and inspection methods.

/**
 * Interface for controlling and inspecting running containers
 */
interface StartedTestContainer {
  /**
   * Stop the container
   * @param options - Stop options including timeout and removal settings
   */
  stop(options?: Partial<StopOptions>): Promise<StoppedTestContainer>;

  /**
   * Restart the container
   * @param options - Restart options including timeout
   */
  restart(options?: Partial<RestartOptions>): Promise<void>;

  /**
   * Commit container changes to a new image
   * @param options - Commit options including image name and changes
   * @returns Promise resolving to new image ID
   */
  commit(options: CommitOptions): Promise<string>;

  /**
   * Get the Docker host address
   * @returns Host address (e.g., 'localhost' or Docker machine IP)
   */
  getHost(): string;

  /**
   * Get container's internal hostname
   * @returns Container hostname
   */
  getHostname(): string;

  /**
   * Get the first mapped port
   * @returns Host port number
   */
  getFirstMappedPort(): number;

  /**
   * Get host port mapped to a container port
   * @param port - Container port number
   * @param protocol - Optional protocol ('tcp' or 'udp')
   * @returns Host port number
   */
  getMappedPort(port: number, protocol?: string): number;
  getMappedPort(portWithProtocol: `${number}/${"tcp" | "udp"}`): number;

  /**
   * Get container name
   * @returns Container name
   */
  getName(): string;

  /**
   * Get container labels
   * @returns Object containing all container labels
   */
  getLabels(): Labels;

  /**
   * Get container ID
   * @returns Full container ID
   */
  getId(): string;

  /**
   * Get names of networks the container is connected to
   * @returns Array of network names
   */
  getNetworkNames(): string[];

  /**
   * Get network ID for a specific network name
   * @param networkName - Name of the network
   * @returns Network ID
   */
  getNetworkId(networkName: string): string;

  /**
   * Get container's IP address on a specific network
   * @param networkName - Name of the network
   * @returns IP address
   */
  getIpAddress(networkName: string): string;

  /**
   * Copy archive from container
   * @param path - Path in container to copy from
   * @returns Readable stream of tar archive
   */
  copyArchiveFromContainer(path: string): Promise<NodeJS.ReadableStream>;

  /**
   * Copy tar archive to container
   * @param tar - Readable stream of tar archive
   * @param target - Target path in container (default: '/')
   */
  copyArchiveToContainer(tar: Readable, target?: string): Promise<void>;

  /**
   * Copy directories to container
   * @param directoriesToCopy - Array of directory copy specifications
   */
  copyDirectoriesToContainer(directoriesToCopy: DirectoryToCopy[]): Promise<void>;

  /**
   * Copy files to container
   * @param filesToCopy - Array of file copy specifications
   */
  copyFilesToContainer(filesToCopy: FileToCopy[]): Promise<void>;

  /**
   * Copy content to container
   * @param contentsToCopy - Array of content copy specifications
   */
  copyContentToContainer(contentsToCopy: ContentToCopy[]): Promise<void>;

  /**
   * Execute command in running container
   * @param command - Command string or array of command parts
   * @param opts - Execution options (working directory, user, environment)
   * @returns Promise resolving to execution result with output and exit code
   */
  exec(command: string | string[], opts?: Partial<ExecOptions>): Promise<ExecResult>;

  /**
   * Get container logs
   * @param opts - Log options including since timestamp and tail count
   * @returns Promise resolving to readable stream of logs
   */
  logs(opts?: { since?: number; tail?: number }): Promise<Readable>;

  /**
   * Async disposal support for automatic cleanup
   */
  [Symbol.asyncDispose](): Promise<void>;
}

Usage Examples:

import { GenericContainer } from 'testcontainers';

const container = await new GenericContainer('postgres:14')
  .withExposedPorts(5432)
  .start();

// Get connection details
const host = container.getHost();
const port = container.getMappedPort(5432);
console.log(`Connect to: postgresql://${host}:${port}/postgres`);

// Execute commands
const result = await container.exec(['psql', '-U', 'postgres', '-c', 'SELECT version()']);
console.log(result.output);

// Copy files
await container.copyFilesToContainer([{
  source: './init.sql',
  target: '/docker-entrypoint-initdb.d/init.sql'
}]);

// Get logs
const logs = await container.logs({ tail: 100 });
logs.on('data', (line) => console.log(line));

// Network info
const networks = container.getNetworkNames();
const ip = container.getIpAddress(networks[0]);

// Cleanup
await container.stop();

StoppedTestContainer Interface

Represents a stopped container with limited operations.

/**
 * Interface for stopped containers
 */
interface StoppedTestContainer {
  /**
   * Get container ID
   * @returns Container ID
   */
  getId(): string;

  /**
   * Copy archive from stopped container
   * @param path - Path in container to copy from
   * @returns Readable stream of tar archive
   */
  copyArchiveFromContainer(path: string): Promise<NodeJS.ReadableStream>;
}

AbstractStartedContainer Class

Base class for creating custom container implementations with lifecycle hooks.

/**
 * Abstract base class for custom started container implementations
 * Wraps a StartedTestContainer and delegates all operations to it
 */
abstract class AbstractStartedContainer implements StartedTestContainer {
  /**
   * Construct a custom started container wrapper
   * @param startedTestContainer - The underlying started container
   */
  constructor(startedTestContainer: StartedTestContainer);

  /**
   * Optional lifecycle hook called before container stops
   */
  protected containerStopping?(): Promise<void>;

  /**
   * Optional lifecycle hook called after container stops
   */
  protected containerStopped?(): Promise<void>;

  // All methods from StartedTestContainer interface are available
}

Usage Example:

import { AbstractStartedContainer, GenericContainer, StartedTestContainer } from 'testcontainers';

class RedisContainer extends GenericContainer {
  constructor() {
    super('redis:7-alpine');
    this.withExposedPorts(6379);
  }

  async start(): Promise<StartedRedisContainer> {
    return new StartedRedisContainer(await super.start());
  }
}

class StartedRedisContainer extends AbstractStartedContainer {
  getConnectionUrl(): string {
    return `redis://${this.getHost()}:${this.getMappedPort(6379)}`;
  }

  protected async containerStopping(): Promise<void> {
    // Custom cleanup logic before stopping
    console.log('Flushing Redis data...');
    await this.exec(['redis-cli', 'FLUSHALL']);
  }
}

// Usage
const redis = await new RedisContainer().start();
console.log(redis.getConnectionUrl()); // Custom method
await redis.stop(); // Calls containerStopping() hook

AbstractStoppedContainer Class

Base class for custom stopped container implementations.

/**
 * Abstract base class for custom stopped container implementations
 */
abstract class AbstractStoppedContainer implements StoppedTestContainer {
  /**
   * Construct a custom stopped container wrapper
   * @param stoppedTestContainer - The underlying stopped container
   */
  constructor(stoppedTestContainer: StoppedTestContainer);

  getId(): string;
  copyArchiveFromContainer(path: string): Promise<NodeJS.ReadableStream>;
}

Types

type Environment = { [key: string]: string };
type Labels = { [key: string]: string };
type BuildArgs = { [key: string]: string };
type TmpFs = { [dir: string]: string };

type BindMode = 'rw' | 'ro' | 'z' | 'Z';

interface BindMount {
  source: string;
  target: string;
  mode?: BindMode;
}

interface FileToCopy {
  source: string;
  target: string;
  mode?: number;
}

interface DirectoryToCopy {
  source: string;
  target: string;
  mode?: number;
}

type Content = string | Buffer | Readable;

interface ContentToCopy {
  content: Content;
  target: string;
  mode?: number;
}

interface ArchiveToCopy {
  tar: Readable;
  target: string;
}

interface ExtraHost {
  host: string;
  ipAddress: string;
}

interface ResourcesQuota {
  memory?: number; // Memory limit in Gigabytes
  cpu?: number; // CPU quota in units of CPUs
}

interface Ulimits {
  [name: string]: {
    hard: number | undefined;
    soft: number | undefined;
  };
}

interface HealthCheck {
  test: ['CMD-SHELL', string] | ['CMD', ...string[]];
  interval?: number;
  timeout?: number;
  retries?: number;
  startPeriod?: number;
}

interface ExecOptions {
  workingDir?: string;
  user?: string;
  env?: { [key: string]: string };
}

interface ExecResult {
  output: string;
  stdout: string;
  stderr: string;
  exitCode: number;
}

interface StopOptions {
  timeout: number;
  remove: boolean;
  removeVolumes: boolean;
}

interface RestartOptions {
  timeout: number;
}

interface CommitOptions {
  repo?: string;
  comment?: string;
  author?: string;
  pause?: boolean;
  changes?: string[];
  deleteOnExit?: boolean;
}

interface BuildOptions {
  deleteOnExit: boolean;
}

type PortWithOptionalBinding =
  | number
  | `${number}/${'tcp' | 'udp'}`
  | PortWithBinding;

interface PortWithBinding {
  container: number;
  host: number;
  protocol?: 'tcp' | 'udp';
}

interface ImagePullPolicy {
  shouldPull(): boolean;
}

type HealthCheckStatus = 'none' | 'starting' | 'unhealthy' | 'healthy';

type HostPortBindings = Array<{ hostIp: string; hostPort: number }>;

type Ports = { [containerPortWithProtocol: string]: HostPortBindings };

interface NetworkSettings {
  networkId: string;
  ipAddress: string;
}

interface InspectResult {
  name: string;
  hostname: string;
  ports: Ports;
  healthCheckStatus: HealthCheckStatus;
  networkSettings: { [networkName: string]: NetworkSettings };
  state: {
    status: string;
    running: boolean;
    startedAt: Date;
    finishedAt: Date | undefined;
  };
  labels: { [key: string]: string };
}