Skip to content

Containers

Starting a container

Create and start any container using a Generic Container:

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

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

To use a specific image version:

const container = await new GenericContainer("alpine:3.10").start();

With a pull policy

Testcontainers will automatically pull an image if it doesn't exist. This is configurable:

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

const container = await new GenericContainer("alpine")
  .withPullPolicy(new AlwaysPullPolicy())
  .start();

With a command

const container = await new GenericContainer("alpine")
  .withCommand(["sleep", "infinity"])
  .start();

With an entrypoint

const container = await new GenericContainer("alpine")
  .withEntrypoint(["cat"])
  .start();

With environment variables

const container = await new GenericContainer("alpine")
  .withEnvironment({ ENV: "VALUE" })
  .start();

With bind mounts

Not recommended.

Bind mounts are not portable. They do not work with Docker in Docker or in cases where the Docker agent is remote. It is preferred to copy files/content into the container instead.

const container = await new GenericContainer("alpine")
  .withBindMounts([{ 
    source: "/local/file.txt", 
    target:"/remote/file.txt" 
  }, {
    source: "/local/dir",
    target:"/remote/dir",
    mode: "ro"
  }])
  .start();

With labels

const container = await new GenericContainer("alpine")
  .withLabels({ label: "value" })
  .start();

With a name

Not recommended.

If a container with the same name already exists, Docker will raise a conflict. If you are specifying a name to enable container to container communication, look into creating a network and using network aliases.

const container = await new GenericContainer("alpine")
  .withName("custom-container-name")
  .start();

With files/content

const container = await new GenericContainer("alpine")
  .withCopyFilesToContainer([{ 
    source: "/local/file.txt", 
    target: "/remote/file1.txt"
  }])
  .withCopyContentToContainer([{ 
    content: "hello world",
    target: "/remote/file2.txt"
  }])
  .start();

With default log driver

May be necessary when the driver of your docker host does not support reading logs, and you want to use the Log output wait strategy.

See log drivers.

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

With a tmpfs mount

const container = await new GenericContainer("alpine")
  .withTmpFs({ "/temp_pgdata": "rw,noexec,nosuid,size=65536k" })
  .start();

With user

Value can be a username or UID (format: <name|uid>[:<group|gid>]).

const container = await new GenericContainer("alpine")
  .withUser("bob")
  .start();

With privileged mode

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

With added capabilities

See capabilities.

const container = await new GenericContainer("alpine")
  .withAddedCapabilities("NET_ADMIN", "IPC_LOCK")
  .start();

With dropped capabilities

See capabilities.

const container = await new GenericContainer("alpine")
  .withDroppedCapabilities("NET_ADMIN", "IPC_LOCK")
  .start();

With ulimits

const container = await new GenericContainer("aline")
  .withUlimits({ 
    memlock: { 
      hard: -1, 
      soft: -1 
    }
  })
  .start();

With IPC mode

See IPC mode.

const container = await new GenericContainer("alpine")
  .withIpcMode("host")
  .start();

Stopping a container

Testcontainers by default will not wait until the container has stopped. It will simply issue the stop command and return immediately. This is to save time when running tests.

const container = await new GenericContainer("postgres").start();
await container.stop();

If you need to wait for the container to be stopped, you can provide a timeout:

const container = await new GenericContainer("postgres").start();
await container.stop({ timeout: 10000 }); // ms

Volumes created by the container are removed when stopped. This is configurable:

const container = await new GenericContainer("postgres").start();
await container.stop({ removeVolumes: false });

Restarting a container

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

Reusing a container

Enabling container re-use means that Testcontainers will not start a new container if a Testcontainers managed container with the same configuration is already running.

This is useful for example if you want to share a container across tests without global set up.

const container1 = await new GenericContainer("alpine")
  .withCommand(["sleep", "infinity"])
  .withReuse()
  .start();

const container2 = await new GenericContainer("alpine")
  .withCommand(["sleep", "infinity"])
  .withReuse()
  .start();

expect(container1.getId()).toBe(container2.getId());

Exposing container ports

Specify which container ports you want accessible by the host:

const container = await new GenericContainer("alpine")
  .withExposedPorts(22, 80, 443)
  .start();

Testcontainers will automatically bind an available, random port on the host to each exposed container port. This is to avoid port conflicts when running tests quickly or in parallel.

Retrieve the mapped port as follows:

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

const httpPort = container.getMappedPort(80);

Specify fixed host port bindings (not recommended):

const container = await new GenericContainer("alpine")
  .withExposedPorts({
    container: 80,
    host: 80
  })
  .start();

Running commands

const container = await new GenericContainer("alpine")
  .withCommand(["sleep", "infinity"])
  .start();

const { output, exitCode } = await container.exec(["echo", "hello", "world"]);

Streaming logs

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

(await container.logs())
  .on("data", line => console.log(line))
  .on("err", line => console.error(line))
  .on("end", () => console.log("Stream closed"));