Skip to content

withStartupTimeout() is not applied to the port-binding pre-wait (hardcoded 10 s) — measured under CPU contention #1446

Description

@kenzox

Problem

GenericContainer.start() waits for the host port bindings to appear (inspectContainerUntilPortsExposed) before the wait strategy runs. That pre-wait has a hardcoded 10 s budget: the util has timeout = 10_000 as a default parameter, and neither call site in generic-container.ts passes this.startupTimeoutMs. So withStartupTimeout(...) only extends the wait strategy; a container whose port binding takes longer than 10 s fails with

Timed out after 10000ms while waiting for container ports to be bound to the host

no matter what startup timeout the caller configured.

This looks like #1281, but I'd like to add a measured data point that is not Kafka/flaky-environment related — the pre-wait itself is the thing that trips, and only under CPU contention.

Measurements (testcontainers 12.1.0, Node 24.19, Docker 29.x, postgres:18-alpine)

  • Local (Apple M3 Pro, one container): host port bound in 1.1–2.6 s.
  • Local, six workspace packages each starting one postgres:18-alpine at the same time (vitest workers): successful starts bound their ports in 2.2–7.2 s; a cold image pull pushed some past 10 s.
  • GitHub Actions ubuntu-latest (2 vCPU, nproc = 2), same six concurrent containers: one start measured 16.7 s until the port was bound — the container was healthy afterwards, it was just slow under load. It failed at 10 s five times in one workflow run although every container had withStartupTimeout(120_000).

So the 10 s default is not "a few milliseconds of Docker Desktop race" in practice: on a loaded host the port binding legitimately takes longer, and the configured startup timeout is the natural budget for it.

Root cause

packages/testcontainers/src/generic-container/generic-container.ts — both calls omit the third argument:

inspectResult = await inspectContainerUntilPortsExposed(() => client.container.inspect(container), container.id);   // reuseContainer
const inspectResult = await inspectContainerUntilPortsExposed(
  () => client.container.inspect(container),
  container.id
);                                                                                                                    // startContainer

inspect-container-util-ports-exposed.ts: timeout = 10_000.

Proposed fix

Pass this.startupTimeoutMs at both call sites. When withStartupTimeout was never called the value is undefined, so the default parameter still yields 10 s — behaviour only changes for callers who explicitly asked for a longer budget. We run exactly this as a pnpm patch since yesterday; the same suite went from 5 pre-wait timeouts per run to 0. PR follows.

Environment

  • testcontainers: 12.1.0 (latest at the time of writing)
  • Node.js: v24.19.0
  • Docker: 29.6.1 (Docker Desktop, arm64) locally; GitHub-hosted ubuntu-latest in CI

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions