Skip to content

Wait Strategies

A wait strategy will resolve the call to container.start() only after a condition is met. This allows you to ensure the container can perform its work before resolving.

There are many built-in wait strategies, supporting varying conditions, which are listed below.

Note that the startup timeout of all wait strategies is configurable:

const { GenericContainer } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withStartupTimeout(120000) // wait 120s
  .start();

Listening ports

The default wait strategy used by Testcontainers. It will wait up to 60 seconds for the container's mapped network ports to be bound.

const { GenericContainer } = require("testcontainers");

const container = await new GenericContainer("alpine").withExposedPorts(6379).start();

It can be set explicitly but is not required:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withExposedPorts(6379)
  .withWaitStrategy(Wait.forListeningPorts())
  .start();

Log output

Wait until the container has logged a message:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forLogMessage("Ready to accept connections"))
  .start();

With a regular expression:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forLogMessage(/Listening on port \d+/))
  .start();

Wait until the container has logged a message a number of times:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forLogMessage("Listening on port 8080", 2))
  .start();

Health check

Wait until the container's health check is successful:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine").withWaitStrategy(Wait.forHealthCheck()).start();

Define your own health check:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withHealthCheck({
    test: ["CMD-SHELL", "curl -f http://localhost || exit 1"],
    interval: 1000,
    timeout: 3000,
    retries: 5,
    startPeriod: 1000,
  })
  .withWaitStrategy(Wait.forHealthCheck())
  .start();

Note that interval, timeout, retries and startPeriod are optional as they are inherited from the image or parent image if omitted.

To execute the test with a shell use the form ["CMD-SHELL", "command"]:

["CMD-SHELL", "curl -f http://localhost:8000 || exit 1"];

To execute the test without a shell, use the form: ["CMD", "command", "arg1", "arg2"]. This may be needed when working with distroless images:

["CMD", "/usr/bin/wget", "--no-verbose", "--tries=1", "--spider", "http://localhost:8080/hello-world"];

HTTP

Wait for an HTTP request to satisfy a condition. By default, it will wait for a 200 response:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("redis").withWaitStrategy(Wait.forHttp("/health", 8080)).start();

Stop waiting after container exited if waiting for container restart not needed.

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("redis")
  .withWaitStrategy(Wait.forHttp("/health", 8080, { abortOnContainerExit: true }))
  .start();

For status code

.withWaitStrategy(Wait.forHttp("/health", 8080)
  .forStatusCode(201))

.withWaitStrategy(Wait.forHttp("/health", 8080)
  .forStatusCodeMatching(statusCode => statusCode === 201))

For response body

.withWaitStrategy(Wait.forHttp("/health", 8080)
  .forResponsePredicate(response => response === "OK"))

Custom request

.withWaitStrategy(Wait.forHttp("/health", 8080)
  .withMethod("POST")
  .withHeaders({ X_CUSTOM_VALUE: "custom" })
  .withBasicCredentials("username", "password")
  .withReadTimeout(10000))

Use TLS

.withWaitStrategy(Wait.forHttp("/health", 8443)
  .useTls())

Insecure TLS

.withWaitStrategy(Wait.forHttp("/health", 8443)
  .useTls()
  .insecureTls())

Shell command

Wait until a shell command returns a successful exit code:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forSuccessfulCommand("stat /tmp/app.lock"))
  .start();

One shot

This strategy is intended for use with containers that only run briefly and exit of their own accord. As such, success is deemed to be when the container has stopped with exit code 0.

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forOneShotStartup()))
  .start();

Composite

Multiple wait strategies can be chained together:

const { GenericContainer, Wait } = require("testcontainers");

const container = await new GenericContainer("alpine")
  .withWaitStrategy(Wait.forAll([Wait.forListeningPorts(), Wait.forLogMessage("Ready to accept connections")]))
  .start();

The composite wait strategy by default will respect each individual wait strategy's startup timeout. For example:

const w1 = Wait.forListeningPorts().withStartupTimeout(1000);
const w2 = Wait.forLogMessage("READY").withStartupTimeout(2000);

const composite = Wait.forAll([w1, w2]);

expect(w1.getStartupTimeout()).toBe(1000);
expect(w2.getStartupTimeout()).toBe(2000);

The startup timeout of inner wait strategies that have not defined their own startup timeout can be set by setting the startup timeout on the composite:

const w1 = Wait.forListeningPorts().withStartupTimeout(1000);
const w2 = Wait.forLogMessage("READY");

const composite = Wait.forAll([w1, w2]).withStartupTimeout(2000);

expect(w1.getStartupTimeout()).toBe(1000);
expect(w2.getStartupTimeout()).toBe(2000);

The startup timeout of all wait strategies can be controlled by setting a deadline on the composite. In this case, the composite will throw unless all inner wait strategies have resolved before the deadline.

const w1 = Wait.forListeningPorts();
const w2 = Wait.forLogMessage("READY");
const composite = Wait.forAll([w1, w2]).withDeadline(2000);

Custom Wait Strategies

If these options do not meet your requirements, you can subclass StartupCheckStrategy and use Dockerode, which is the underlying Docker client used by Testcontainers:

const Dockerode = require("dockerode");
const {
  GenericContainer,
  StartupCheckStrategy,
  StartupStatus
} = require("testcontainers");

class ReadyAfterDelayWaitStrategy extends StartupCheckStrategy {
  public checkStartupState(dockerClient: Dockerode, containerId: string): Promise<StartupStatus> {
    return new Promise((resolve) => setTimeout(() => resolve("SUCCESS"), 3000));
  }
}

const container = await new GenericContainer("alpine")
  .withWaitStrategy(new ReadyAfterDelayWaitStrategy())
  .start();