testcontainers / testcontainers/testcontainers-node
withStartupTimeout() is not applied to the port-binding pre-wait (hardcoded 10 s) — measured under CPU contention
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.6k
- Forks
- 272
- PR merge metrics
- No merged PRs in 30d
Description
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-alpineat 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 hadwithStartupTimeout(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 (
latestat the time of writing) - Node.js: v24.19.0
- Docker: 29.6.1 (Docker Desktop, arm64) locally; GitHub-hosted
ubuntu-latestin CI
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in packages/testcontainers/src/generic-container/generic-container.ts and inspect both calls to inspectContainerUntilPortsExposed, then read packages/testcontainers/src/generic-container/inspect-container-util-ports-exposed.ts to understand its default timeout. Run the existing package suite under concurrent container startup; done means an explicit withStartupTimeout budget covers the pre-wait while callers without one retain the 10-second default.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- docker, node.js, typescript
- Domain
- devops, testing-qa
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 86/100