testcontainers-dotnet Documentation

A library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions.

RAW Doc

Docker & Self-Hosted Deployment Verified OCI

Deploy testcontainers-dotnet on any server, homelab, or cloud container platform.

1-Click Terminal Command
bash (docker run)
docker run -d --name testcontainers-dotnet -p 8000:8000 --restart always python:3.8-alpine
Configured Services & Ports
⚙️ docs
image: python:3.8-alpine
🔌 Ports: 8000:8000
docker-compose.yml
yaml (docker-compose.yml)
services:
  docs:
    image: python:3.8-alpine
    command: sh -c "pip install -r requirements.txt && mkdocs serve -a 0.0.0.0:8000"
    working_dir: /docs
    volumes:
      - ./:/docs
    ports:
      - 8000:8000

Technical Documentation: testcontainers/testcontainers-dotnet

ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference

Overview (`README.md`)

Testcontainers




Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions. The library is built on top of the .NET Docker Remote API and provides a lightweight implementation to support your test environment in all circumstances.

[Read the documentation here][testcontainers-dotnet-documentation]

License

See LICENSE.

Copyright (c) 2019 - 2026 Andre Hofmeister and other authors.

See contributors for all contributors.


Join our Slack Workspace | Testcontainers OSS


Overview (`examples/Flyway/README.md`)

Testcontainers for .NET Flyway example

This example demonstrates how to use Testcontainers in conjunction with Flyway to execute database migrations and prepare a dependent database before running tests. The FlywayTest test class executes its tests against the pre-configured PostgreSQL database, interacting with the table that is created, altered, and seeded beforehand. This test class receives a class fixture, which provides access to the prepared database through the DbConnection property. The database is started, created, and seeded once and is shared across the tests within the FlywayTest test collection. Checkout and run the tests on your machine:

console
git clone --branch develop [email protected]:testcontainers/testcontainers-dotnet.git
cd ./testcontainers-dotnet/examples/Flyway/
dotnet test Flyway.sln --configuration=Release

Overview (`examples/Respawn/README.md`)

Testcontainers for .NET Respawn example

This example demonstrates how to use Respawn to reset the state of a PostgreSQL database between tests, ensuring isolation and consistency. The RespawnTest test class interacts with a pre-configured database, using Respawn to clear the data before each test run. This allows the tests to execute in a clean environment without interfering with each other.

console
git clone --branch develop [email protected]:testcontainers/testcontainers-dotnet.git
cd ./testcontainers-dotnet/examples/Respawn/
dotnet test Respawn.sln --configuration=Release

Overview (`examples/WeatherForecast/README.md`)

Testcontainers for .NET WeatherForecast example

This example builds and ships a Blazor application in a Docker image build, runs a Docker container and executes tests against a running instance of the application. Testcontainers for .NET takes care of the Docker image build and the Docker container that hosts the application. Spin up as much as containers as you like and run your tests heavily in parallel. Checkout and run the tests on your machine:

console
git lfs version
git clone --branch develop [email protected]:testcontainers/testcontainers-dotnet.git
cd ./testcontainers-dotnet/examples/WeatherForecast/
dotnet test WeatherForecast.sln --configuration=Release

*) One unit test depends on Selenium and requires Chrome in version 145. The WebDriver module includes different browsers and does not depend on a local installation.


Overview (`src/Templates/README.md`)

.NET module template

The .NET module template scaffolds Testcontainers for .NET modules. While simple modules can inherit from ContainerBuilder, more advanced modules usually require additional properties and methods to set up a running configuration. To scaffold a new module, install the template dotnet new --install ./src/Templates and run dotnet new tcm --name ${module_name} --output ${output_directory}.


Overview (`tests/Testcontainers.Tests/Assets/setup/README.md`)

This Markdown file should not be part of the DockerfileArchiveTar unit test tar archive.


Index (`docs/index.md`)

Welcome to Testcontainers for .NET!

shell title="install the nuget dependency"
dotnet add package Testcontainers
csharp title="run the hello world container"
// Create a new instance of a container.
var container = new ContainerBuilder("testcontainers/helloworld:1.3.0")
  // Bind port 8080 of the container to a random port on the host.
  .WithPortBinding(8080, true)
  // Wait until the HTTP endpoint of the container is available.
  .WithWaitStrategy(Wait.ForUnixContainer().UntilHttpRequestIsSucceeded(r => r.ForPort(8080)))
  // Build the container configuration.
  .Build();

// Start the container.
await container.StartAsync()
  .ConfigureAwait(false);

// Create a new instance of HttpClient to send HTTP requests.
using var httpClient = new HttpClient();

// Construct the request URI by specifying the scheme, hostname, assigned random host port, and the endpoint "uuid".
var requestUri = new UriBuilder(Uri.UriSchemeHttp, container.Hostname, container.GetMappedPublicPort(8080), "uuid").Uri;

// Send an HTTP GET request to the specified URI and retrieve the response as a string.
var guid = await httpClient.GetStringAsync(requestUri)
  .ConfigureAwait(false);

// Ensure that the retrieved UUID is a valid GUID.
Debug.Assert(Guid.TryParse(guid, out _));

Not using .NET? Here are other supported languages!

Java Go .NET Node.js Python Rust Ruby PHP Haskell Clojure Elixir Scala Native

About

Testcontainers for .NET is a library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions. The library is built on top of the .NET Docker Remote API and provides a lightweight implementation to support your test environment in all circumstances.

Choose from existing pre-configured modules and start containers within a second, to support and run your tests. Or create your own container images with Dockerfiles and run your containers and tests immediately afterward.

For more detailed instructions and guidance, please refer to the Testcontainers' Getting Started guide. If you are specifically interested in using Testcontainers for .NET, you find a dedicated follow-up guide here. These resources will provide you with comprehensive information to help you get started and make the most out of Testcontainers.

Supported operating systems

Testcontainers supports Windows, Linux, and macOS as host systems. Linux Docker containers are supported on all three operating systems. Native Windows Docker containers are only supported on Windows. Windows requires the host operating system version to match the container operating system version. You will find further information about Windows container version compatibility here.

With Docker Desktop you can switch the engine either with the tray icon context menu or: $env:ProgramFiles\Docker\Docker\DockerCli.exe -SwitchDaemon or -SwitchLinuxEngine, -SwitchWindowsEngine.

System requirements

Testcontainers requires a Docker-API compatible container runtime. During development, Testcontainers is actively tested against recent versions of Docker on Linux, as well as against Docker Desktop on Mac and Windows. These Docker environments are automatically detected and used by Testcontainers without any additional configuration being necessary.

It is possible to configure Testcontainers to work for other Docker setups, such as a remote Docker host or Docker alternatives. However, these are not actively tested in the main development workflow, so not all Testcontainers features might be available and additional manual configuration might be necessary. If you have further questions about configuration details for your setup or whether it supports running Testcontainers-based tests, please contact the Testcontainers team and other users from the Testcontainers community on Slack.

License

See LICENSE.

Copyright (c) 2019 - 2026 Andre Hofmeister and other authors.

See contributors for all contributors.


Join our Slack Workspace | Testcontainers OSS


Cicd Index (`docs/cicd/index.md`)

Continuous integration

To use Testcontainers in your CI/CD environment, you only require Docker installed. A local installation of Docker is not mandatory; you can also use a remote Docker installation.

Azure Pipelines

Microsoft-hosted agents come with Docker pre-installed, there is no need for any additional configuration. It is important to note that Windows agents use the Docker Windows engine and cannot run Linux containers. If you are using Windows agents, ensure that the image you are using matches the agent's architecture and operating system version 1), 2).

GitHub Actions

GitHub-hosted runners have the same configuration as Microsoft-hosted agents. The configuration is similar to what is described in the section Azure Pipelines.

GitLab CI/CD

To configure the Docker service in GitLab CI (Docker-in-Docker), you need to define the service in your .gitlab-ci.yml file and expose the Docker host address docker:2375 by setting the DOCKER_HOST environment variable.

yaml title=".gitlab-ci.yml file"
services:
  - docker:dind
variables:
  DOCKER_HOST: tcp://docker:2375

Bitbucket Pipelines

Enable Bitbucket Pipelines as usual on the Repository settings → Pipelines → Settings page. After enabling your pipeline, replace the contents of the bitbucket-pipelines.yml file, located at the root of your repository, with the following:

yaml title="bitbucket-pipelines.yml file"
image: mcr.microsoft.com/dotnet/sdk:8.0
options:
  docker: true
pipelines:
  default:
    - step:
      script:
        # Bitbucket Pipelines does not support Ryuk:
        # https://dotnet.testcontainers.org/api/resource_reaper/.
        - export TESTCONTAINERS_RYUK_DISABLED=true
        - dotnet test
      services:
        - docker

Custom Configuration Index (`docs/custom_configuration/index.md`)

Custom configuration

Testcontainers supports various configurations to set up your test environment. It automatically discovers the Docker environment and applies the configuration. You can set or override the default values either with the Testcontainers properties file (~/.testcontainers.properties) or with environment variables. The following configurations are available:

Properties File Environment Variable Description Default
docker.api.version DOCKER_API_VERSION The Docker API version to use. 1.44
docker.config DOCKER_CONFIG The directory path that contains the Docker configuration (config.json) file. ~/.docker/
docker.host DOCKER_HOST The Docker daemon socket to connect to. -
docker.context DOCKER_CONTEXT The Docker context to connect to. -
docker.auth.config DOCKER_AUTH_CONFIG The Docker configuration file content (GitLab: Use statically-defined credentials). -
docker.cert.path DOCKER_CERT_PATH The directory path that contains the client certificate ({ca,cert,key}.pem) files. ~/.docker/
docker.tls DOCKER_TLS Enables TLS. false
docker.tls.verify DOCKER_TLS_VERIFY Enables TLS verify. false
host.override TESTCONTAINERS_HOST_OVERRIDE The host that exposes Docker's ports. -
docker.socket.override TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE The file path to the Docker daemon socket that is used by Ryuk (Resource Reaper). /var/run/docker.sock
ryuk.disabled TESTCONTAINERS_RYUK_DISABLED Disables Ryuk (Resource Reaper). false
ryuk.container.privileged TESTCONTAINERS_RYUK_CONTAINER_PRIVILEGED Runs Ryuk (Resource Reaper) in privileged mode. true
ryuk.container.image TESTCONTAINERS_RYUK_CONTAINER_IMAGE The Ryuk (Resource Reaper) Docker image. testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0
hub.image.name.prefix TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX The name to use for substituting the Docker Hub registry part of the image name. -
wait.strategy.retries TESTCONTAINERS_WAIT_STRATEGY_RETRIES The wait strategy retry count. infinite
wait.strategy.interval TESTCONTAINERS_WAIT_STRATEGY_INTERVAL The wait strategy interval1. 00:00:01
wait.strategy.timeout TESTCONTAINERS_WAIT_STRATEGY_TIMEOUT The wait strategy timeout1. 01:00:00
named.pipe.connection.timeout TESTCONTAINERS_NAMED_PIPE_CONNECTION_TIMEOUT The named pipe connection timeout1. 00:00:01
  1. The value represent the string representation of a TimeSpan, for example, 00:00:01 for 1 second.

Configure remote container runtime

To configure a remote container runtime, Testcontainers provides support for Docker's environment variables in addition to the properties file. During initialization, Testcontainers' auto-discovery feature detect and apply custom configurations including container runtimes. If you are running Docker on a remote host, you can configure it using either of the following methods:

=== "Environment Variable"
shell DOCKER_HOST=tcp://docker:2375

=== "Properties File"
text docker.host=tcp://docker:2375

Use a different context

You can switch between contexts using the properties file or an environment variable. Once the context is set, Testcontainers will connect to the specified endpoint based on the given value.

text title="list available contexts"
PS C:\Sources\dotnet\testcontainers-dotnet> docker context ls
NAME   DESCRIPTION   DOCKER ENDPOINT           ERROR
tcc                  tcp://127.0.0.1:60706/0

Setting the context to tcc in this example will use the Docker host running at 127.0.0.1:60706 to create and run the test resources.

=== "Environment Variable"
shell DOCKER_CONTEXT=tcc

=== "Properties File"
text docker.context=tcc

Substitute image names

Testcontainers supports substituting an image name with an alternative on the fly, for example to pull from a private registry mirror instead of Docker Hub. This is useful if you have complex rules that a simple prefix cannot express, such as a non-deterministic name mapping, rules depending on the developer or environment, or auditing and restricting the images used in a build.

Set TestcontainersSettings.ImageNameSubstitution to a function that receives the original IImage and returns the image to use instead. Return the original image (or null) to leave it unchanged.

Configure the substitution once before any test resources are created. A static class with a [ModuleInitializer] method runs automatically before the first type in the test assembly is used:

csharp
internal static class TestcontainersConfiguration
{
  [ModuleInitializer]
  public static void Initialize()
  {
    TestcontainersSettings.ImageNameSubstitution = image
      => new DockerImage(image.Repository, "registry.mycompany.com", image.Tag, image.Digest, image.Platform);
  }
}

The substitution runs first, then the Docker Hub image name prefix (TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX) is applied to the substituted image. As with any image, the prefix is only added when the image does not already specify a registry, so a substitution that sets a registry (such as the examples above) takes precedence over the prefix.

Automatically modify Docker Hub image names

Testcontainers can automatically add a registry prefix to Docker Hub image names used in your tests. This is handy if you use a private registry that mirrors Docker Hub images with predictable naming.

You can set this up in two ways:

=== "Environment Variable"
shell TESTCONTAINERS_HUB_IMAGE_NAME_PREFIX=registry.mycompany.com/mirror/

=== "Properties File"
text hub.image.name.prefix=registry.mycompany.com/mirror/

Once configured, Testcontainers will rewrite Docker Hub image names by adding the prefix.

For example, the image:

text
testcontainers/helloworld:1.3.0

will automatically become:

text
registry.mycompany.com/mirror/testcontainers/helloworld:1.3.0

Enable logging

In .NET logging usually goes through the test framework. Testcontainers is not aware of the project's test framework and may not forward log messages to the appropriate output stream. The default implementation forwards log messages to the Console (respectively stdout and stderr). The output should at least pop up in the IDE running tests in the Debug configuration. To override the default implementation, use the builder's WithLogger(ILogger) method and provide an ILogger instance to replace the default console logger.

text
[testcontainers.org 00:00:00.34] Connected to Docker:
  Host: tcp://127.0.0.1:60706/
  Server Version: 70+testcontainerscloud
  Kernel Version: 5.10.137
  API Version: 1.41
  Operating System: Ubuntu 20.04 LTS
  Total Memory: 7.23 GB
[testcontainers.org 00:00:00.47] Searching Docker registry credential in CredHelpers
[testcontainers.org 00:00:00.47] Searching Docker registry credential in Auths
[testcontainers.org 00:00:00.47] Searching Docker registry credential in CredsStore
[testcontainers.org 00:00:00.47] Searching Docker registry credential in Auths
[testcontainers.org 00:00:00.51] Docker registry credential https://index.docker.io/v1/ found
[testcontainers.org 00:00:03.16] Docker image testcontainers/ryuk:0.14.0 created
[testcontainers.org 00:00:03.24] Docker container b6fad46fbcec84625281c1401ec91158b25cad6495fa612274af7c920abec14e created
[testcontainers.org 00:00:03.29] Start Docker container b6fad46fbcec84625281c1401ec91158b25cad6495fa612274af7c920abec14e
[testcontainers.org 00:00:06.18] Docker image alpine:latest created
[testcontainers.org 00:00:06.22] Docker container 027af397344d08d5fc174bf5b5d449f6b352a8a506306d3d96390aaa2bb0445d created
[testcontainers.org 00:00:06.26] Start Docker container 027af397344d08d5fc174bf5b5d449f6b352a8a506306d3d96390aaa2bb0445d
[testcontainers.org 00:00:06.64] Delete Docker container 027af397344d08d5fc174bf5b5d449f6b352a8a506306d3d96390aaa2bb0445d

!!! tip

text
These log messages are from the Testcontainers library and contain information about the test resources. They do not include log messages from the containers. To get the container log messages, see: [Getting log messages](https://dotnet.testcontainers.org/api/create_docker_container/#getting-log-messages).

To enable debug log messages in the default implementation, set the property ConsoleLogger.Instance.DebugLogLevelEnabled to true. This will forward messages related to building or pulling Docker images to the output stream.


Dind Index (`docs/dind/index.md`)

Running inside a container

'Docker Wormhole' pattern - sibling Docker containers

Docker-only example

If you choose to run your tests in a Docker Wormhole configuration, which involves using sibling containers, it is necessary to mount Docker's raw socket /var/run/docker.sock.raw. You find more information and an explanation of the Docker bug in this comment.

shell
docker run -v /var/run/docker.sock.raw:/var/run/docker.sock $IMAGE dotnet test

!!! note

text
If you are using Docker Desktop, you need to configure the `TESTCONTAINERS_HOST_OVERRIDE` environment variable to use the special DNS name
`host.docker.internal` for accessing the host from within a container, which is provided by Docker Desktop:
`-e TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal`

Docker Compose example

A minimal docker-compose.yml file that builds a new container image and runs the test inside the container look something like:

yaml
version: "3"
services:
  docker_compose_test:
    build:
      dockerfile: Dockerfile
      context: .
    entrypoint: dotnet
    command: test
    # Uncomment the lines below in the case of Docker Desktop (see note above).
    # TESTCONTAINERS_HOST_OVERRIDE is not needed in the case of Docker Engine.
    # environment:
    #   - TESTCONTAINERS_HOST_OVERRIDE=host.docker.internal
    volumes:
      - /var/run/docker.sock.raw:/var/run/docker.sock

Full Framework Index (`docs/full_framework/index.md`)

.NET Framework

When working with older versions of the .NET Framework (e.g., .NET Framework 4.x), you may encounter issues related to assembly binding conflicts. These conflicts typically occur when your application requires specific versions of assemblies that are different from the versions being loaded at runtime.

To resolve these conflicts and ensure the correct versions of assemblies are used, binding redirects are often necessary. Binding redirects allow you to specify which version of an assembly should be used by the runtime, preventing errors and version mismatches during execution.

Testcontainers for .NET relies on several external dependencies, which may require different versions of assemblies. Legacy applications or projects targeting the full .NET Framework may not automatically resolve these dependencies correctly, and without binding redirects, runtime errors or unexpected behavior may occur.

In executable .NET Framework projects (such as console apps, web apps, etc.), Visual Studio typically handles binding redirects automatically. However, this is not the case for class libraries or test projects.

For test projects, binding redirects are not automatically added by Visual Studio, which means you may need to manually configure them in the App.config file (or enable AutoGenerateBindingRedirects).


Modules Index (`docs/modules/index.md`)

Modules

Modules are great examples of Testcontainers' capabilities. To write tests against real dependencies, you can either choose one of the pre-configurations listed below or create your own implementation.

Modules are standalone dependencies that can be installed from NuGet.org. To use a module in your test project, you need to add it as a dependency first:

shell
dotnet add package Testcontainers.ModuleName

All modules follow the same design and come pre-configured with best practices. Usually, you do not need to worry about configuring them yourself. To create and start a container, all you need is:

csharp
var moduleNameContainer = new ModuleNameBuilder("<repository>:<tag>").Build();
await moduleNameContainer.StartAsync();

!!! note

text
We will add module-specific documentations soon.
Module Image NuGet Source
ActiveMQ Artemis apache/activemq-artemis:2.31.2 NuGet Source
ArangoDB arangodb:3.11.5 NuGet Source
Aspire dashboard mcr.microsoft.com/dotnet/aspire-dashboard:13 NuGet Source
Azure Cosmos DB mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest NuGet Source
Azure Event Hubs mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest NuGet Source
Azure Service Bus mcr.microsoft.com/azure-messaging/servicebus-emulator:latest NuGet Source
Azurite mcr.microsoft.com/azure-storage/azurite:3.24.0 NuGet Source
BigQuery ghcr.io/goccy/bigquery-emulator:0.4 NuGet Source
Bigtable gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
Cassandra library/cassandra:5.0 NuGet Source
ClickHouse clickhouse/clickhouse-server:23.6-alpine NuGet Source
CockroachDB cockroachdb:23.1.13 NuGet Source
Consul consul:1.15 NuGet Source
Couchbase couchbase:community-7.0.2 NuGet Source
CouchDB couchdb:3.3 NuGet Source
Db2 icr.io/db2_community/db2:12.1.0.0 NuGet Source
DynamoDB amazon/dynamodb-local:1.21.0 NuGet Source
Elasticsearch elasticsearch:8.6.1 NuGet Source
EventStoreDB eventstore/eventstore:22.10.1-buster-slim NuGet Source
FakeGcsServer fsouza/fake-gcs-server:1.47 NuGet Source
Firebird jacobalberty/firebird:v4.0 NuGet Source
Firestore gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
Floci floci/floci:1.5.13 NuGet Source
Grafana grafana/grafana:12.2 NuGet Source
InfluxDB influxdb:2.7 NuGet Source
JanusGraph janusgraph/janusgraph:1.0.0 NuGet Source
K3s rancher/k3s:v1.26.2-k3s1 NuGet Source
Kafka confluentinc/cp-kafka:6.1.9 NuGet Source
Keycloak quay.io/keycloak/keycloak:21.1 NuGet Source
KurrentDb kurrentplatform/kurrentdb:25.1 NuGet Source
Kusto emulator mcr.microsoft.com/azuredataexplorer/kustainer-linux:latest NuGet Source
LocalStack localstack/localstack:2.0 NuGet Source
Lowkey Vault nagyesta/lowkey-vault:2.7.1-ubi9-minimal NuGet Source
Mailpit axllent/mailpit:v1.30 NuGet Source
MariaDB mariadb:10.10 NuGet Source
Milvus milvusdb/milvus:v2.3.10 NuGet Source
MinIO minio/minio:RELEASE.2023-01-31T02-24-19Z NuGet Source
MongoDB mongo:6.0 NuGet Source
Mosquitto eclipse-mosquitto:2.0 NuGet Source
MySQL mysql:8.0 NuGet Source
NATS nats:2.9 NuGet Source
Neo4j neo4j:5.4 NuGet Source
Ollama ollama/ollama:0.6.6 NuGet Source
OpenSearch opensearchproject/opensearch:2.12.0 NuGet Source
Oracle gvenzl/oracle-xe:21.3.0-slim-faststart NuGet Source
Papercut changemakerstudiosus/papercut-smtp:latest NuGet Source
Playwright mcr.microsoft.com/playwright:v1.55.1 NuGet Source
PostgreSQL postgres:15.1 NuGet Source
PubSub gcr.io/google.com/cloudsdktool/google-cloud-cli:446.0.1-emulators NuGet Source
Pulsar apachepulsar/pulsar:3.0.6 NuGet Source
Qdrant qdrant/qdrant:v1.13.4 NuGet Source
QuestDB questdb/questdb:10.0.1 NuGet Source
RabbitMQ rabbitmq:3.11 NuGet Source
RavenDB ravendb/ravendb:5.4-ubuntu-latest NuGet Source
Redis redis:7.0 NuGet Source
Redpanda docker.redpanda.com/redpandadata/redpanda:v22.2.1 NuGet Source
Seq datalust/seq:2025.2 NuGet Source
Sftp atmoz/sftp:alpine NuGet Source
SQL Server mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04 NuGet Source
Temporal temporalio/temporal:1.5.1 NuGet Source
Toxiproxy ghcr.io/shopify/toxiproxy:2.12.0 NuGet Source
Typesense typesense/typesense:28.0 NuGet Source
Weaviate semitechnologies/weaviate:1.26.14 NuGet Source
WebDriver selenium/standalone-chrome:110.0 NuGet Source
WireMock - NuGet Source

Implement a module

The Testcontainers for .NET repository contains a .NET template to scaffold advanced modules quickly. To create and add a new module to the Testcontainers solution file, checkout the repository and install the .NET template first:

shell
git clone --branch develop [email protected]:testcontainers/testcontainers-dotnet.git
cd ./testcontainers-dotnet/
dotnet new --install ./src/Templates

The following CLI commands create and add a new PostgreSQL module to the solution file:

shell
dotnet new tcm --name PostgreSql --official-module true --output ./src
dotnet sln add ./src/Testcontainers.PostgreSql/Testcontainers.PostgreSql.csproj

!!! note

text
If you decided to publish a module to [NuGet.org](https://www.nuget.org/profiles/Testcontainers) on your own, you need to set the `--official-module` flag to `false`.

A module typically consists of three classes representing the builder, the configuration and the container. The PostgreSQL module we just created above consists of the PostgreSqlBuilder, PostgreSqlConfiguration and PostgreSqlContainer classes.

  1. The builder class sets the module default configuration and validates it. It extends the Testcontainers builder and adds or overrides members specifically to configure the module. The builder is responsible for creating a valid configuration and container instance.
  2. The configuration class stores optional members to configure the module and interact with the container. Usually, these are properties like a Username or Password that are required sometime later.
  3. Developers interact with the builder the most. It manages the lifecycle and provides module specific members to interact with the container. The result of the builder is an instance of the container class.

Configure a module

The configuration classes in Testcontainers are designed to be immutable, meaning that once an instance of a configuration class has been created, its values cannot be changed. This has a number of benefits, it is more reliable, easier to understand and to share between different use cases like A/B testing.

To set the PostgreSQL module default configuration, override the read-only DockerResourceConfiguration property in PostgreSqlBuilder and set its value in both constructors. The default constructor sets DockerResourceConfiguration to the return value of Init().DockerResourceConfiguration, where the overloaded private constructor just sets the argument value. It receives an updated instance of the immutable Docker resource configuration as soon as a property changes.

The .NET template already includes this configuration, making it easy for developers to quickly get started by simply commenting out the necessary parts:

csharp
public PostgreSqlBuilder(string image)
  : this(new DockerImage(image))
{
}

public PostgreSqlBuilder(IImage image)
  : this(new PostgreSqlConfiguration())
{
  DockerResourceConfiguration = Init().WithImage(image).DockerResourceConfiguration;
}

private PostgreSqlBuilder(PostgreSqlConfiguration resourceConfiguration)
  : base(resourceConfiguration)
{
  DockerResourceConfiguration = resourceConfiguration;
}

protected override PostgreSqlConfiguration DockerResourceConfiguration { get; }

To append the PostgreSQL configurations to the default Testcontainers configurations override or comment out the Init() member and add the necessary configurations and a wait strategy to the base implementation:

csharp
protected override PostgreSqlBuilder Init()
{
  var waitStrategy = Wait.ForUnixContainer().UntilCommandIsCompleted("pg_isready");
  return base.Init().WithPortBinding(5432, true).WithWaitStrategy(waitStrategy);
}

Extend a module

When using the PostgreSQL Docker image, it is required to have a password set in order to run it. To demonstrate how to add a new builder member, we will use this requirement as an example.

First add a new property Password to the PostgreSqlConfiguration class. Add a password argument with a default value of null to the default constructor. This allows the builder to set individual arguments or configurations.

The overloaded PostgreSqlConfiguration(PostgreSqlConfiguration, PostgreSqlConfiguration) constructor takes care of merging the configurations together. The builder will receive and hold an updated instances that contains all information:

csharp
public PostgreSqlConfiguration(string password = null)
{
  Password = password;
}

public PostgreSqlConfiguration(PostgreSqlConfiguration oldValue, PostgreSqlConfiguration newValue)
  : base(oldValue, newValue)
{
  Password = BuildConfiguration.Combine(oldValue.Password, newValue.Password);
}

public string Password { get; }

Since the PostgreSqlConfiguration class is now able to store the password value, we can add a member WithPassword(string) to PostgreSqlBuilder. We not just store the password in the PostgreSqlConfiguration instance to construct the database connection string later, but we also set the necessary environment variable POSTGRES_PASSWORD to run the container:

csharp
public PostgreSqlBuilder WithPassword(string password)
{
  return Merge(DockerResourceConfiguration, new PostgreSqlConfiguration(password: password)).WithEnvironment("POSTGRES_PASSWORD", password);
}

By following this approach, the PostgreSqlContainer class is able to access the configured values, allowing it to provide additional functionalities, such as constructing the database connection string. This enables the class to provide a more streamlined and convenient experience for developers who are working with modules:

csharp
public string GetConnectionString()
{
  var properties = new Dictionary<string, string>();
  properties.Add("Host", Hostname);
  properties.Add("Port", GetMappedPublicPort(5432).ToString());
  properties.Add("Database", "postgres");
  properties.Add("Username", "postgres");
  properties.Add("Password", _configuration.Password);
  return string.Join(";", properties.Select(property => string.Join("=", property.Key, property.Value)));
}

Finally, there are two approaches to ensure that the required password is provided. Either override the Validate() member and check the immutable configuration instance:

csharp
protected override void Validate()
{
  base.Validate();

  _ = Guard.Argument(DockerResourceConfiguration.Password, nameof(PostgreSqlConfiguration.Password))
    .NotNull()
    .NotEmpty();
}

or extend the Init() member as we have already done and add WithPassword(Guid.NewGuid().ToString()) to set a default value.

It is always a good idea to add both approaches. This way, the user can be sure that the module is properly configured, whether by themself or by default. This helps maintain a consistent and reliable experience for the user.

The repository provides reference implementations of modules. This modules are comprehensive examples and can serve as guides for you to get a better understanding of how to implement an entire module including the tests.


Contributing (`docs/contributing.md`)

Contributing

  • Star the project on Github and help spread the word :)
  • Join our Slack Workspace
  • Post an issue if you find any bugs
  • Contribute improvements or fixes using a Pull Request. If you're going to contribute, thank you! Please just be sure to:
    • discuss with the authors on an issue ticket prior to doing anything big.
    • follow the style, naming and structure conventions of the rest of the project.
    • make commits atomic and easy to merge.
    • verify all tests are passing. Build and test the project with ./build.sh or build.ps1 to do this.

Contributing Docs (`docs/contributing_docs.md`)

Contributing to documentation

The Testcontainers for .NET documentation is a static site built with MkDocs.
We use the Material for MkDocs theme, which offers a number of useful extensions to MkDocs.

In addition we use a custom plugin for inclusion of code snippets.

We publish our documentation using Netlify.

Previewing rendered content

Using Python locally

  • Ensure that you have Python 3.11.0 or higher.
  • Set up a virtualenv and run pip install -r requirements.txt in the testcontainers-dotnet root directory.
  • Once Python dependencies have been installed, run mkdocs serve to start a local auto-updating MkDocs server.

Using Docker

The root of the project contains a compose.yml file. Simply run docker compose up and access the docs at: http://localhost:8000.

PR preview deployments

Note that documentation for pull requests will automatically be published by Netlify as 'deploy preview'.
These deployment previews can be accessed via the netlify/testcontainers-dotnet/deploy-preview check that appears for each pull request.


Api Best Practices (`docs/api/best_practices.md`)

Best practices

At times, configuring and launching disposable Docker resources for testing purposes can be initially complex. However, modules have already minimized the amount of configuration required to a bare minimum, requiring only a single line of code to create reliable test environments. Nonetheless, developers should be aware of and adhere to these best practices to write efficient and effective tests:

  1. Avoid assigning static names to Docker resources such as containers, networks, and volumes. Static names may clash with existing resources, causing tests to fail. Instead, use random assigned names. By default, Docker assigns random names.
  2. Avoid assigning static port bindings to containers. Instead, use random assigned host ports _builder.WithPortBinding(ushort, true) and retrieve the mapped port with _container.GetMappedPublicPort(ushort).
  3. Avoid using localhost or 127.0.0.1 to connect to the container. Instead, use the _container.Hostname property to access the container from the test host.
  4. When setting up a container-to-container communication, use network aliases _builder.WithNetworkAliases(string) to connect to the container. Access the service running inside the container through its container port.
  5. Avoid mounting local host paths to containers. Instead, use _container.WithResourceMapping(string, string) or one of its overloaded members to copy dependent files to the container before it starts.
  6. In rare cases, it may be necessary to access the underlying Docker API to configure specific properties that are not exposed by Testcontainers' own API. To get access to all Docker API properties required to create a resource, use _builder.WithCreateParameterModifier(Action<TCreateResourceEntity>).
  7. Do not disable the Resource Reaper, as it cleans up remaining test resources. Disabling it may clutter up the test environment.
  8. Ensure that the image version is overridden and pinned, regardless of whether you are using the generic or module container builder _builder.WithImage("alpine:3.20.0").

Api Builder Configurations (`docs/api/builder_configurations.md`)

Combining builder configurations

Testcontainers for .NET creates every Docker resource through a builder: containers, images, networks, volumes, and Docker Compose projects. All builders, including the ones that modules provide, share the same design. The rules below explain what happens when you call builder methods and how multiple calls combine.

Builders are immutable

Every builder method returns a new builder instance that includes the updated configuration. The existing instance remains unchanged. This lets you share a common configuration and derive different resources from it, for example for A/B testing. Remember to use the returned builder, otherwise your change is lost. See reusing builder configurations for examples.

How builder methods combine

When you call a builder method more than once, or call a method that a module has already called, the configurations combine as follows:

  • A method that sets a single value, such as the image, name, wait strategy, or startup callback, replaces the previously configured value.
  • Methods that configure lists and dictionaries, such as environment variables, labels, port bindings, or mounts, append new values to the existing ones. A dictionary entry with an existing key replaces the previous entry.
  • You cannot remove existing list and dictionary values. The exceptions are builder methods that accept a ComposableEnumerable<T>, which let you decide how new values combine with existing ones, for example to overwrite them. See composing command arguments.

Validation happens on build

Builder methods only record the configuration. The builder validates the final configuration when you call Build(), and throws an exception if a mandatory setting is missing or settings conflict. This is why you might see a configuration error at Build(), even though the WithX call itself succeeded.

Modules are pre-configured

Modules come pre-configured, and that configuration is opinionated. It is tailored to run the service reliably in tests. A module sets up things like the environment variables, the wait strategy, and the startup callback.

A module applies most of its defaults when you create the builder, before any of your builder calls. Some parts of the configuration depend on your input, though. In that case, the module applies them in Build(), once your configuration is final. These values replace anything you configured earlier.

You can still call the generic builder methods on a module builder, but because single values replace previous ones, doing so can override the module's defaults, or be overridden by them. For example, calling WithWaitStrategy or WithStartupCallback can replace the module's implementation, which can leave the container unprovisioned or never ready. Overriding a module's configuration this way is not supported. If you need full control over the configuration, use the generic ContainerBuilder instead.


Api Compose (`docs/api/compose.md`)

Docker Compose

ComposeBuilder and ComposeContainer run a Docker Compose project as part of your tests. Docker Compose runs inside a container (docker compose up / docker compose down), you do not need Docker Compose installed on the test host. Testcontainers copies the Docker Compose files, and the directories they live in, into that container, and mounts the Docker socket so Docker Compose can reach the Docker daemon.

csharp
--8<-- "tests/Testcontainers.Platform.Linux.Tests/ComposeContainerExampleTest.cs:CreateComposeContainer"

Docker Compose files

WithComposeFile(params string[]) accepts one or more Docker Compose file paths. Each file keeps the path it has on the test host, which lets Docker Compose resolve relative references, such as bind mount sources or included Docker Compose files, the same way it would outside a container.

By default, the entire directory of each Docker Compose file is copied into the container. If that directory also contains files you do not want copied, such as build output, restrict the copy with WithCopyFilesInContainer(params string[]). It copies the Docker Compose files and the listed files and directories only, resolved relative to the directory of the first Docker Compose file.

Waiting for services

Use WaitingFor(string, IWaitForContainerOS) to wait for a service without exposing it, and WithExposedService(string, ushort, IWaitForContainerOS) to expose a service port and wait for it at the same time. WithExposedService(string, ushort) exposes a port without an additional wait strategy. The readiness check then only waits until the port accepts connections.

An exposed service port does not need to be published in the Docker Compose file. An ambassador container proxies the port and makes it accessible to the test host, so wait strategies work against Docker Compose services the same way they work against any other container. Resolve the address with GetServiceHost(string, ushort) and GetServicePort(string, ushort).

csharp
--8<-- "tests/Testcontainers.Platform.Linux.Tests/ComposeContainerExampleTest.cs:ConnectToExposedService"

!!! note

text
`docker compose up` already starts the services. The wait strategies of a Docker Compose service run against the already running container. They do not start it.

Getting the service container

GetServiceContainer(string) returns the IContainer that belongs to a Docker Compose service, for example to read its logs or run a command inside it. Docker Compose owns its lifecycle. Stopping or disposing the returned container has no effect. Stop or dispose the ComposeContainer instead.

csharp
--8<-- "tests/Testcontainers.Platform.Linux.Tests/ComposeContainerExampleTest.cs:GetServiceContainer"

Scaled services

WithScaledService(string, ushort) runs more than one container for a Docker Compose service. Each container is one instance, addressed by the service name and an instance number starting at 1. Every *Service member has an *ServiceInstance counterpart that takes the instance number, for example WithExposedServiceInstance(string, ushort, ushort) and GetServiceInstanceContainer(string, ushort). Members without an instance number always address the first instance.

csharp
var composeContainer = new ComposeBuilder("docker:29.8.1-cli")
  .WithComposeFile(composeFilePath)
  .WithScaledService("web", 2)
  .WithExposedServiceInstance("web", 1, 80)
  .WithExposedServiceInstance("web", 2, 80)
  .Build();

Project name

Testcontainers generates a random Docker Compose project name for every ComposeContainer, so concurrent test runs do not collide. Use WithProjectNamePrefix(string) to make the generated name easier to recognize, for example in Docker Desktop or docker compose ls. Testcontainers appends a random suffix to the prefix, and reads it back through ComposeContainer.ProjectName.

csharp
_ = new ComposeBuilder("docker:29.8.1-cli")
  .WithComposeFile(composeFilePath)
  .WithProjectNamePrefix("checkout-service");

Pulling images

Testcontainers pulls the Docker Compose service images from the test host before docker compose up runs, so the Docker credentials and credential helpers of the test host apply. Docker Compose running inside its own container does not have access to them. This is enabled by default. A failing pull does not fail the start. Docker Compose still tries to pull the image itself. Disable it with WithPull(false) if your images are already present on the Docker host.

Command options

WithComposeUpOption(params string[]) and WithComposeDownOption(params string[]) add options to the docker compose up and docker compose down commands. Each option is one command line argument. Pass an option and its value as two arguments, "--pull", "always", or as one argument that contains the assignment, "--pull=always".

csharp
_ = new ComposeBuilder("docker:29.8.1-cli")
  .WithComposeFile(composeFilePath)
  .WithComposeUpOption("--build")
  .WithComposeDownOption("--rmi", "local");

A service that Docker Compose builds needs its build context inside the container that runs the Docker Compose CLI. The directory of the Docker Compose file is copied by default, which includes the build context. If you restrict the copy with WithCopyFilesInContainer(params string[]), list the Dockerfile and the build context as well.

Options that apply to Docker Compose itself instead of to a command, such as --profile or --env-file, are set with their pre-defined environment variable. They apply to every command Testcontainers runs.

csharp
_ = new ComposeBuilder("docker:29.8.1-cli")
  .WithComposeFile(composeFilePath)
  .WithEnvironment("COMPOSE_PROFILES", "debug");

!!! note

text
The Docker Compose services always start detached, and are always removed with their volumes.

!!! note

text
`ComposeContainer` does not support `WithReuse(true)`.

Api Connection String Provider (`docs/api/connection_string_provider.md`)

Connection string provider

The connection string provider API provides a standardized way to access and manage connection information for Testcontainers (modules). It allows developers to customize module-provided connection strings or add their own, and to access module-specific connection strings or endpoints (e.g., database connection strings, HTTP API base addresses) in a uniform way.

!!! note

text
Testcontainers modules do not yet implement this feature. Developers can use the provider to define and manage their own connection strings or endpoints. Providers will be integrated by modules in future releases.

Example

Register a custom connection string provider via the container builder:

csharp
IContainer container = new ContainerBuilder("alpine:3.20.0")
  .WithConnectionStringProvider(new MyProvider1())
  .Build();

// Implicit host connection string (default)
var hostConnectionStringImplicit = container.GetConnectionString();

// Explicit host connection string
var hostConnectionStringExplicit = container.GetConnectionString(ConnectionMode.Host);

// Container-to-container connection string
var containerConnectionString = container.GetConnectionString(ConnectionMode.Container);

Implementing a custom provider

To create a custom provider, implement the generic interface: IConnectionStringProvider<TContainer, TConfiguration>. The Configure(TContainer, TConfiguration) method is invoked after the container has successfully started, ensuring that all runtime-assigned values are available.

=== "Generic Builder"
```csharp
public sealed class MyProvider1 : IConnectionStringProvider<IContainer, IContainerConfiguration>
{
public void Configure(IContainer container, IContainerConfiguration configuration)
{
// Initialize provider with container information.
}

text
public string GetConnectionString(ConnectionMode connectionMode = ConnectionMode.Host)
  {
    // This method returns a default connection string. The connection mode argument
    // lets you choose between a host connection or a container-to-container connection.
    return "...";
  }

  public string GetConnectionString(string name, ConnectionMode connectionMode = ConnectionMode.Host)
  {
    // This method returns a connection string for the given name. Useful for modules
    // with multiple endpoints (e.g., Azurite blob, queue, or table).
    return "...";
  }
}
```

=== "Module Builder"
```csharp
public sealed class MyProvider2 : IConnectionStringProvider<PostgreSqlContainer, PostgreSqlConfiguration>
{
private string _host;

text
private ushort _port;

  public void Configure(PostgreSqlContainer container, PostgreSqlConfiguration configuration)
  {
    // Initialize provider with container information.
    _host = container.Hostname;
    _port = container.GetMappedPublicPort(PostgreSqlBuilder.PostgreSqlPort);
  }

  public string GetConnectionString(ConnectionMode connectionMode = ConnectionMode.Host)
  {
    return $"Host={_host};Port={_port};...";
  }

  public string GetConnectionString(string name, ConnectionMode connectionMode = ConnectionMode.Host)
  {
    return $"Host={_host};Port={_port};...;SSL Mode=Require";
  }
}
```

Api Create Docker Container (`docs/api/create_docker_container.md`)

Creating a container

Testcontainers' generic container support offers the greatest flexibility and makes it easy to use virtually any container image in the context of a temporary test environment. To interact or exchange data with a container, Testcontainers provides ContainerBuilder to configure and create the resource.

Configure container image

To specify the container image, use WithImage(...).

The simplest overload accepts a string:

csharp
_ = new ContainerBuilder()
  .WithImage("postgres:15.1");

For more advanced scenarios, WithImage also supports IImage, giving you more control over how the image is represented and its properties are resolved.

If you need to target a specific platform, the DockerImage implementation provides an overload that lets you explicitly set the platform, such as linux/amd64. By default, the container runtime uses the platform that matches the container host.

csharp
_ = new ContainerBuilder()
  .WithImage(new DockerImage("postgres:15.1", new Platform("linux/amd64")));

!!! tip

text
A specifier has the format `<os>|<arch>|<os>/<arch>[/<variant>]`. The user can provide either the operating system or the architecture or both. For more details, see [containerd/platforms](https://github.com/containerd/platforms).

Configure container start

Both ENTRYPOINT and CMD allows you to configure an executable and parameters, that a container runs at the start. By default, a container will run whatever ENTRYPOINT or CMD is specified in the Docker container image. At least one of both configurations is necessary. The container builder implementation supports WithEntrypoint(params string[]) and WithCommand(params string[]) to set or override the executable. Ideally, the ENTRYPOINT should set the container's executable, whereas the CMD sets the default arguments for the ENTRYPOINT.

Instead of running the NGINX application, the following container configuration overrides the default start procedure of the image and just tests the NGINX configuration file.

csharp
_ = new ContainerBuilder("nginx:1.26.3-alpine3.20")
  .WithEntrypoint("nginx")
  .WithCommand("-t");

Configure container app or service

Apps or services running inside a container are usually configured either with environment variables or configuration files. WithEnvironment(string, string) sets an environment variable, while WithResourceMapping(string, string) copies a file into a container before it starts. This covers common use cases among many .NET applications.

!!! tip

text
The majority of builder methods are overloaded and have different parameters to set configurations.

To configure an ASP.NET Core application, either one or both mechanisms can be used.

csharp
_ = new ContainerBuilder("mcr.microsoft.com/dotnet/aspnet:10.0")
  .WithEnvironment("ASPNETCORE_URLS", "https://+")
  .WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Path", "/app/certificate.pfx")
  .WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Password", "password")
  .WithResourceMapping("certificate.pfx", "/app/");

WithBindMount(string, string) is another option to provide access to directories or files. It mounts a host directory or file into the container. Note, this does not follow our best practices. Host paths differ between environments and may not be available on every system or Docker setup, e.g. CI.

Copying directories or files to the container

Sometimes it is necessary to copy files into the container to configure the services running inside the container in advance, like the appsettings.json or an SSL certificate. The container builder API provides WithResourceMapping(...) overloads that support strongly typed source and target paths via FilePath and DirectoryPath.

csharp title="copying a directory"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithResourceMapping(
    DirectoryPath.Of("."),
    DirectoryPath.Of("/app/"));
csharp title="copying a file"
_ = new ContainerBuilder("alpine:3.20.0")
  // Copy 'appsettings.json' into the '/app' directory.
  .WithResourceMapping(
    FilePath.Of("appsettings.json"),
    DirectoryPath.Of("/app/"))
  // Copy 'appsettings.Container.json' to '/app/appsettings.json'.
  .WithResourceMapping(
    FilePath.Of("appsettings.Container.json"),
    FilePath.Of("/app/appsettings.json"));

You can also copy the contents of a byte array to a specific file path within the container. This can be useful when you already have the file content stored in memory or when you need to dynamically generate the file content before copying it.

csharp title="copying a byte array"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithResourceMapping(
    Encoding.UTF8.GetBytes("{}"),
    FilePath.Of("/app/appsettings.json"));

For remote sources, you can copy a file from a URL to a target directory or file before the container starts.

=== "Copying to a Directory"
csharp _ = new ContainerBuilder("alpine:3.20.0") .WithResourceMapping( new Uri("https://localhost:8080/appsettings.json"), DirectoryPath.Of("/app/"));

=== "Copying to a File"
csharp _ = new ContainerBuilder("alpine:3.20.0") .WithResourceMapping( new Uri("https://localhost:8080/appsettings.json"), FilePath.Of("/app/appsettings.json"));

Specifying file ownership

When copying files into a container, you can specify the user ID (UID) and group ID (GID) to set the correct ownership of the copied files. This is particularly useful when the container runs as a non-root user or when specific file permissions are required for security or application functionality.

csharp title="copying a file with specific uid and gid"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithResourceMapping(
    FilePath.Of("appsettings.json"),
    DirectoryPath.Of("/app/"),
    uid: 1000,
    gid: 1000);

Specifying file permission

When copying files into a container, you can specify the file mode to set the correct permissions for the copied files.

csharp title="copying a script with executable permissions"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithResourceMapping(
    FilePath.Of("docker-entrypoint.sh"),
    DirectoryPath.Of("/app/"),
    fileMode: Unix.FileMode755);

The Unix class provides common permission configurations like FileMode755 (read, write, execute for owner; read, execute for group and others). For individual permission combinations, you can use the UnixFileModes enumeration to create custom configurations.

Copying files to a running container

The same UID, GID, and file mode arguments are also available when copying files to already running containers using the IContainer.CopyAsync(...) APIs.

Reading files from the container

The IContainer interface offers a ReadFileAsync(string, CancellationToken) method to read files from the container. The implementation returns the read bytes. Either process the read bytes in-memory or persist them to the disk.

csharp title="reading a file"
var readBytes = await container.ReadFileAsync("/app/appsettings.json")
  .ConfigureAwait(false);

await File.WriteAllBytesAsync("appsettings.json", readBytes)
  .ConfigureAwait(false);

Canceling a container start

Starting a container or creating a resource (such as a network or a volume) can be canceled by passing a CancellationToken to the member. The following example cancels the container start after one minute if it has not finished before.

csharp title="canceling container start after one minute"
using var timeoutCts = new CancellationTokenSource(TimeSpan.FromMinutes(1));
await _container.StartAsync(timeoutCts.Token)
  .ConfigureAwait(false);

Getting log messages

Testcontainers for .NET provides two approaches for retrieving log messages from containers: GetLogsAsync and WithOutputConsumer. Each method serves different use cases for handling container logs.

The GetLogsAsync method is available through the IContainer interface. It allows you to fetch logs from a container for a specific time range or from the beginning until the present. This approach is useful for retrieving logs after a test has run, especially when troubleshooting issues or failures.

csharp title="getting all log messages"
var (stdout, stderr) = await _container.GetLogsAsync()
  .ConfigureAwait(false);

The WithOutputConsumer method is part of the ContainerBuilder class and is used to continuously forward container log messages to a specified output consumer. This approach provides real-time access to logs as the container runs.

csharp title="forwarding all log messages"
using IOutputConsumer outputConsumer = Consume.RedirectStdoutAndStderrToConsole();

_ = new ContainerBuilder("alpine:3.20.0")
  .WithOutputConsumer(outputConsumer);

The static class Consume offers pre-configured implementations of the IOutputConsumer interface for common use cases. If you need additional functionalities beyond those provided by the default implementations, you can create your own implementations of IOutputConsumer.

Composing command arguments

Testcontainers for .NET provides the WithCommand(ComposableEnumerable<string>) API to give you flexible control over container command arguments. While currently used for container commands, the ComposableEnumerable<T> abstraction is designed to support other builder APIs in the future, allowing similar composition and override functionality.

Because our builders are immutable, this feature allows you to extend or override pre-configured configurations, such as those in Testcontainers modules, without modifying the original builder.

ComposableEnumerable<T> lets you decide how new API arguments should be combined with existing ones. You can choose to append, overwrite, or apply other strategies based on your needs.

If a module applies default commands and you need to override or remove them entirely, you can do this e.g. by explicitly resetting the command list:

csharp title="resetting command arguments"
// Default PostgreSQL builder configuration:
//
// base.Init()
//   ...
//   .WithCommand("-c", "fsync=off")
//   .WithCommand("-c", "full_page_writes=off")
//   .WithCommand("-c", "synchronous_commit=off")
//   ...

var postgreSqlContainer = new PostgreSqlBuilder("postgres:15.1")
  .WithCommand(new OverwriteEnumerable<string>(Array.Empty<string>()))
  .Build();

Using OverwriteEnumerable<string>(Array.Empty<string>()) removes all default command configurations. This is useful when you want full control over the PostgreSQL startup or when the default configurations do not match your requirements.

!!! tip

text
You can create your own `ComposableEnumerable<T>` implementation to control exactly how configuration values are composed or modified.

Reusing builder configurations

Because builders are immutable, you can share a common configuration and derive multiple containers from it, for example for A/B testing:

csharp
var baseBuilder = new PostgreSqlBuilder()
  .WithUsername("Username")
  .WithPassword("Password")
  .WithLabel("Key", "Value");

var postgres15 = baseBuilder
  .WithImage("postgres:15")
  .Build();

var postgres14 = baseBuilder
  .WithImage("postgres:14")
  .Build();

If you configure a builder across multiple statements or conditionally, reassign the return value. Calling WithImage(...) without using the returned builder does not update the existing instance:

csharp
var builder = new PostgreSqlBuilder()
  .WithImage("postgres:15")
  .WithUsername("Username")
  .WithPassword("Password")
  .WithLabel("Key", "Value");

if (Debugger.IsAttached)
{
  builder = builder.WithImage("postgres:14");
}

var container = builder.Build();

Examples

An NGINX container that binds the HTTP port to a random host port and hosts static content. The example connects to the web server and checks the HTTP status code.

csharp
const ushort HttpPort = 80;

var nginxContainer = new ContainerBuilder("nginx:1.26.3-alpine3.20")
  .WithName(Guid.NewGuid().ToString("D"))
  .WithPortBinding(HttpPort, true)
  .Build();

await nginxContainer.StartAsync()
  .ConfigureAwait(false);

using var httpClient = new HttpClient();
httpClient.BaseAddress = new UriBuilder("http", nginxContainer.Hostname, nginxContainer.GetMappedPublicPort(HttpPort)).Uri;

var httpResponseMessage = await httpClient.GetAsync(string.Empty)
  .ConfigureAwait(false);

Assert.Equal(HttpStatusCode.OK, httpResponseMessage.StatusCode);

This example creates and starts a container, that listens for incoming TCP connections and returns the magic number 42.

csharp
const string MagicNumber = "42";

const ushort MagicNumberPort = 80;

var deepThoughtContainer = new ContainerBuilder("alpine:3.20.0")
  .WithName(Guid.NewGuid().ToString("D"))
  .WithPortBinding(MagicNumberPort, true)
  .WithEnvironment("MAGIC_NUMBER", MagicNumber)
  .WithEntrypoint("/bin/sh", "-c")
  .WithCommand($"while true; do echo \"$MAGIC_NUMBER\" | nc -l -p {MagicNumberPort}; done")
  .Build();

await deepThoughtContainer.StartAsync()
  .ConfigureAwait(false);

using var magicNumberClient = new TcpClient(deepThoughtContainer.Hostname, deepThoughtContainer.GetMappedPublicPort(MagicNumberPort));
using var magicNumberReader = new StreamReader(magicNumberClient.GetStream());

var magicNumber = await magicNumberReader.ReadLineAsync()
  .ConfigureAwait(false);

Assert.Equal(MagicNumber, magicNumber);

!!! tip

text
To avoid port conflicts, do not bind a fix host port. Instead, assign a random host port by using `WithPortBinding(80, true)` and retrieve it from the container instance by using `GetMappedPublicPort(80)`.

Supported commands

Builder method Description
DependsOn Sets the dependent resource to resolve and create or start before starting this container configuration.
WithAcceptLicenseAgreement Accepts the license agreement of modules that require one.
WithSsl Enables SSL/TLS using the certificate and private key for modules that support it.
WithDockerEndpoint Sets the Docker daemon socket to connect to.
WithAutoRemove Will remove the stopped container automatically, similar to --rm.
WithCleanUp Will remove the container automatically after all tests have been run.
WithReuse Reuses an existing container instead of creating a new one (experimental).
WithLabel Applies metadata to the container e.g. -l, --label "testcontainers=awesome".
WithImage Specifies an image for which to create the container.
WithImagePullPolicy Specifies an image pull policy to determine when an image is pulled e.g. --pull "always" &vert; "missing" &vert; "never".
WithName Sets the container name e.g. --name "testcontainers".
WithHostname Sets the container hostname e.g. --hostname "testcontainers".
WithMacAddress Sets the container MAC address e.g. --mac-address "00:80:41:ae:fd:7e".
WithWorkingDirectory Specifies or overrides the WORKDIR for the instruction sets.
WithEntrypoint Specifies or overrides the ENTRYPOINT that runs the executable.
WithCommand Specifies or overrides the COMMAND instruction provided in the Dockerfile.
WithEnvironment Sets an environment variable in the container e.g. -e, --env "MAGIC_NUMBER=42".
WithExposedPort Exposes a port inside the container e.g. --expose "80".
WithPortBinding Publishes a container port to the host e.g. -p, --publish "80:80".
WithResourceMapping Copies a file or any binary content into the created container even before it is started.
WithBindMount Binds a path of a file or directory into the container e.g. -v, --volume ".:/tmp".
WithVolumeMount Mounts a managed volume into the container e.g. --mount "type=volume,source=my-vol,destination=/tmp".
WithTmpfsMount Mounts a temporary volume into the container e.g. --mount "type=tmpfs,destination=/tmp".
WithMount Assigns a custom mount configuration to the container.
WithNetwork Assigns a network to the container e.g. --network "bridge".
WithNetworkAliases Assigns a network-scoped aliases to the container e.g. --network-alias "alias".
WithExtraHost Adds a custom host-to-IP mapping to the container's /etc/hosts respectively %WINDIR%\\system32\\drivers\\etc\\hosts e.g. --add-host "host.testcontainers.internal:172.17.0.2".
WithPrivileged Sets the --privileged flag.
WithOutputConsumer Redirects stdout and stderr to capture the container output.
WithWaitStrategy Sets the wait strategy to complete the container start and indicates when it is ready.
WithStartupCallback Sets the startup callback to invoke after the container start.
WithConnectionStringProvider Sets the connection string provider to resolve connection strings for the container.
WithCreateParameterModifier Allows low level modifications of the Docker container create parameter.
WithLogger Sets the logger that replaces the default console logger.

!!! tip

text
Testcontainers for .NET detects your Docker host configuration. You do **not** have to set the Docker daemon socket.

!!! tip

text
Testcontainers for .NET detects private Docker registry configurations and applies the credentials automatically to authenticate against registries.

Api Create Docker Image (`docs/api/create_docker_image.md`)

Creating an image

Testcontainers for .NET uses the builder design pattern to configure, create and delete Docker resources. It prepares and initializes your test environment and disposes of everything after your tests are finished — whether the tests are successful or not. To create a container image from a Dockerfile use ImageFromDockerfileBuilder.

!!! note

text
`ImageFromDockerfileBuilder` builds the image through the Docker Engine API, which does not support BuildKit. As a result, Dockerfile instructions and options that depend on BuildKit cannot be used with it. For more details, see this [discussion](https://github.com/testcontainers/testcontainers-dotnet/discussions/1193#discussioncomment-10315903). Use [`BuildKitImageFromDockerfileBuilder`](#building-with-buildkit) to build such a Dockerfile.

Examples

Builds and tags a new container image. The Dockerfile is located inside the solution (.sln) directory.

csharp
var futureImage = new ImageFromDockerfileBuilder()
  .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty)
  .WithDockerfile("Dockerfile")
  .Build();

await futureImage.CreateAsync()
  .ConfigureAwait(false);

To build a Docker image with Testcontainers, it's important to understand the build context. Testcontainers needs three things:

  1. Docker build context: The directory containing files Docker can use during the build
  2. Dockerfile name: The name of the Dockerfile to use
  3. Dockerfile directory: Where the Dockerfile is located

!!! tip

text
The build context is optional. If you do not specify one, it defaults to the Dockerfile directory.

Testcontainers creates a tarball with all files and subdirectorys in the build context, incl. the Dockerfile. This tarball is sent to the Docker daemon to build the image. The build context acts as the root for all file operations in the Dockerfile, so all paths (like COPY commands) must be relative to it.

For example, if your project looks like this, the build context would be: /Users/testcontainers/WeatherForecast/.

text
/
└── Users/
    └── testcontainers/
        └── WeatherForecast/
            ├── src/
            │   ├── WeatherForecast.Entities/
            │   │   └── WeatherForecast.Entities.csproj
            │   └── WeatherForecast/
            │       └── WeatherForecast.csproj
            ├── tests/
            │   └── WeatherForecast.Tests/
            │       └── WeatherForecast.Tests.csproj
            ├── .dockerignore
            ├── Dockerfile
            └── WeatherForecast.sln

Testcontainers offers convenient features to detect common directories in .NET projects. The build configuration below resolves the directory containing the solution file by traversing up the directory tree from the executing assembly.

csharp
_ = new ImageFromDockerfileBuilder()
  .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty)
  .WithDockerfile("Dockerfile");

As the tarball's content is based on /Users/testcontainers/WeatherForecast/, all paths inside the Dockerfile must be relative to this path. For example, Docker's COPY instruction copies all files inside the WeatherForecast/ directory to the image.

!!! tip

text
To improve the build time and to reduce the size of the image, it is recommended to include only necessary files. Exclude unnecessary files or directories such as `bin/`, `obj/` and `tests/` with the `.dockerignore` file.
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0
ARG SLN_FILE_PATH="WeatherForecast.sln"
COPY . .
RUN dotnet restore $SLN_FILE_PATH
RUN dotnet publish $SLN_FILE_PATH --configuration Release --framework net6.0 --output app
ENTRYPOINT ["dotnet", "/app/WeatherForecast.dll"]

Choosing a build context

You can use WithContextDirectory(string) to set a build context separate from your Dockerfile. This is useful when the Dockerfile is in one directory but the files you want to include are in another.

csharp
_ = new ImageFromDockerfileBuilder()
  .WithContextDirectory("/path/to/build/context")
  .WithDockerfile("Dockerfile")
  .WithDockerfileDirectory("/path/to/dockerfile/directory");

Choosing a platform

You can use WithPlatform(string) to build the image for a platform other than the platform of the Docker host, for example linux/arm64. A build step that executes a target platform binary, such as a RUN instruction, requires emulation, such as QEMU. A Dockerfile that only copies files and sets metadata builds without it.

csharp
_ = new ImageFromDockerfileBuilder()
  .WithDockerfileDirectory("/path/to/dockerfile/directory")
  .WithPlatform("linux/arm64");

Delete multi-stage intermediate layers

A multi-stage Docker image build generates intermediate layers that serve as caches. Testcontainers' Resource Reaper is unable to automatically delete these layers after the test execution. The necessary label is not forwarded by the Docker image build. Testcontainers is unable to track the intermediate layers during the test. To delete the intermediate layers after the test execution, pass the Resource Reaper session to each stage.

The following Dockerfile assigns the org.testcontainers.resource-reaper-session label to each stage.

dockerfile
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env-1
ARG RESOURCE_REAPER_SESSION_ID="00000000-0000-0000-0000-000000000000"
LABEL "org.testcontainers.resource-reaper-session"=$RESOURCE_REAPER_SESSION_ID

FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build-env-2
ARG RESOURCE_REAPER_SESSION_ID="00000000-0000-0000-0000-000000000000"
LABEL "org.testcontainers.resource-reaper-session"=$RESOURCE_REAPER_SESSION_ID

The ImageFromDockerfileBuilder provides a WithBuildArgument(string, string) member that passes a key-value to the Docker image build. We can leverage this mechanism to pass the appropriate Resource Reaper session to the build.

csharp
_ = new ImageFromDockerfileBuilder()
  .WithBuildArgument("RESOURCE_REAPER_SESSION_ID", ResourceReaper.DefaultSessionId.ToString("D"));

Building with BuildKit

BuildKitImageFromDockerfileBuilder builds the image with BuildKit (docker buildx build) instead of the Docker Engine API. Its configuration is the same as the one of ImageFromDockerfileBuilder, plus the members that only BuildKit supports. Use it for a Dockerfile that depends on BuildKit, such as one that contains a here-document, mounts a build secret, or selects a frontend with # syntax=.

csharp
var futureImage = new BuildKitImageFromDockerfileBuilder("docker:29.8.1-cli")
  .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty)
  .WithDockerfile("Dockerfile")
  .Build();

await futureImage.CreateAsync()
  .ConfigureAwait(false);

The Docker CLI runs inside a container. Testcontainers copies the build context into that container, and mounts the Docker socket so the Docker CLI can reach the Docker daemon. You do not need a Docker CLI installation on the test host. The build itself runs in the BuildKit instance of the Docker daemon (the default docker Buildx driver), which is also where the build cache lives. The cache therefore outlives the container that starts the build, and is shared across builds the same way it is when you run docker build yourself.

The image is written to the image store of the Docker daemon (--load), so everything that follows the build behaves as it does with ImageFromDockerfileBuilder, including WithImage(IImage) and the Resource Reaper labels.

The constructor takes the Docker CLI image that runs the build. Pin it to a specific Docker CLI and Buildx version. The image requires the Buildx plugin.

!!! warning

text
The Docker socket is bind-mounted into the Docker CLI container. The Docker daemon resolves the mount source, which is why a Docker daemon that is reached over TCP works too, as long as it listens on a Unix socket as well. A Docker daemon that does not provide a Unix socket at all, such as a Docker daemon that is reached over a Windows named pipe and runs Windows containers, cannot be used. Set `TestcontainersSettings.DockerSocketOverride` (or `TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE`) if the Docker socket is not at `/var/run/docker.sock`, or keep using `ImageFromDockerfileBuilder`.

Secrets

WithSecret(string, string) and WithSecret(string, FilePath) pass a build secret to the build. The Dockerfile mounts it with RUN --mount=type=secret,id=, which makes it available at /run/secrets/ for the duration of that instruction only. BuildKit does not add it to a layer of the built image.

csharp
_ = new BuildKitImageFromDockerfileBuilder("docker:29.8.1-cli")
  .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty)
  .WithSecret("nuget", FilePath.Of("/path/to/nuget.config"));
dockerfile
FROM mcr.microsoft.com/dotnet/sdk:8.0
COPY . .
RUN --mount=type=secret,id=nuget dotnet restore --configfile /run/secrets/nuget

Testcontainers copies the build secret into the Docker CLI container that runs the build. It is not part of the build context, and is not passed as a build argument or an environment variable. The container that runs the build is removed after the build, no matter whether the cleanup of the image is enabled or not.

SSH

WithSsh(string, params string[]) exposes an SSH agent socket or private key to the build. The Dockerfile mounts it with RUN --mount=type=ssh,id=. Use the id default for a mount that does not name an id. Each path is bind-mounted read-only into the Docker CLI container, keeping the path it has on the test host, so the paths must exist on the host that runs the Docker daemon. At least one path is required, because the Docker CLI container does not run an SSH agent that an id without a path could resolve to. A path cannot contain a comma, which the Docker CLI uses to separate the paths of an SSH id.

csharp
_ = new BuildKitImageFromDockerfileBuilder("docker:29.8.1-cli")
  .WithDockerfileDirectory(CommonDirectoryPath.GetSolutionDirectory(), string.Empty)
  .WithSsh("default", Environment.GetEnvironmentVariable("SSH_AUTH_SOCK"));

Multi-platform images

WithPlatform(string) takes a comma-separated value, which builds a manifest list, for example linux/amd64,linux/arm64. Loading one into the image store of the Docker daemon requires the containerd image store. The classic image store takes a single platform only and the build fails.

Supported commands

Builder method Description
WithDockerEndpoint Sets the Docker daemon socket to connect to.
WithCleanUp Will remove the image automatically after all tests have been run.
WithReuse Reuses an existing image instead of building a new one (experimental).
WithLabel Applies metadata to the image e.g. -l, --label "testcontainers=awesome".
WithName Sets the image name e.g. -t, --tag "testcontainers:0.1.0".
WithContextDirectory Sets the Docker build context directory.
WithDockerfile Sets the name of the Dockerfile.
WithDockerfileDirectory Sets the directory path that contains the Dockerfile.
WithTarget Sets the target build stage of a multi-stage Dockerfile e.g. --target "build".
WithImageBuildPolicy Specifies an image build policy to determine when an image is built.
WithDeleteIfExists Will remove the image if it already exists.
WithBuildArgument Sets build-time variables e.g --build-arg "MAGIC_NUMBER=42".
WithPlatform Sets the platform to build the image for e.g. --platform "linux/arm64".
WithCreateParameterModifier Allows low level modifications of the Docker image build parameter.
WithLogger Sets the logger that replaces the default console logger.

BuildKitImageFromDockerfileBuilder supports the same members, and additionally:

Builder method Description
WithSecret Sets a build secret e.g. --secret "id=aws,src=$HOME/.aws/credentials".
WithSsh Sets an SSH agent socket or private key e.g. --ssh "default=$SSH_AUTH_SOCK".

!!! tip

text
Testcontainers for .NET detects your Docker host configuration. You do **not** have to set the Docker daemon socket.

BuildKitImageFromDockerfileBuilder translates the image build parameter (WithCreateParameterModifier) into Docker CLI arguments:

Image build parameter Docker CLI argument
Dockerfile --file
Target --target
Platform --platform
NetworkMode --network
ShmSize --shm-size
NoCache --no-cache
Pull --pull
ExtraHosts --add-host
CacheFrom --cache-from
Tags --tag
BuildArgs --build-arg
Labels --label

!!! note

text
A parameter that the Docker CLI does not provide an equivalent argument for, such as the resource limits of the Docker Engine API image build (`Memory`, `CPUShares`) or `Squash`, is logged as a warning instead of being applied.

Known issues

  • When building an image using Testcontainers for .NET and switching the user's context (USER statement) in a Dockerfile, the user will not automatically become the owner of the working directory, which seems to be the case when building the image from the CLI. If the running process requires write access to the working directory, it is necessary to set the permissions explicitly (the base image in this example already contains the user app):

    dockerfile
    FROM mcr.microsoft.com/dotnet/sdk:8.0
    WORKDIR /app
    RUN chown app:app .
    USER app

Api Create Docker Network (`docs/api/create_docker_network.md`)

Network communication

There are two common cases to set up a communication with containers or between containers. The first case is simple and does not require additional configurations. The host that runs the test (test process) resolves the container address from the running container with the _container.Hostname property.

!!! warning

text
Do not use `localhost`, `127.0.0.1` or any other fix address to access the container. The address varies according to the environment.

To access apps or services running inside the container behind a certain port, expose and bind the container port to a random host port with _builder.WithPortBinding(ushort, bool) and resolve it with _container.GetMappedPublicPort(ushort). It is recommended to use the Hostname property and GetMappedPublicPort(ushort) together when constructing addresses:

csharp
_ = new UriBuilder("tcp", _container.Hostname, _container.GetMappedPublicPort(2375));

This is considered a best practice and prevents port collisions.

Custom network

A more advanced case, places one or more containers on custom networks. The communication between those containers will not require exposing ports through the host anymore. To configure and create Docker networks use NetworkBuilder. The following example creates a network and assigns it to two containers. The second container establishes a network connection to Deep Thought by using its network alias and receives the magic number 42.

csharp
const string MagicNumber = "42";

const string MagicNumberHost = "deep-thought";

const ushort MagicNumberPort = 80;

var network = new NetworkBuilder()
  .WithName(Guid.NewGuid().ToString("D"))
  .Build();

var deepThoughtContainer = new ContainerBuilder("alpine:3.20.0")
  .WithName(Guid.NewGuid().ToString("D"))
  .WithEnvironment("MAGIC_NUMBER", MagicNumber)
  .WithEntrypoint("/bin/sh", "-c")
  .WithCommand($"while true; do echo \"$MAGIC_NUMBER\" | nc -l -p {MagicNumberPort}; done")
  .WithNetwork(network)
  .WithNetworkAliases(MagicNumberHost)
  .Build();

var ultimateQuestionContainer = new ContainerBuilder("alpine:3.20.0")
  .WithName(Guid.NewGuid().ToString("D"))
  .WithEntrypoint("top")
  .WithNetwork(network)
  .Build();

await network.CreateAsync()
  .ConfigureAwait(false);

await Task.WhenAll(deepThoughtContainer.StartAsync(), ultimateQuestionContainer.StartAsync())
  .ConfigureAwait(false);

var execResult = await ultimateQuestionContainer.ExecAsync(new[] { "nc", MagicNumberHost, MagicNumberPort.ToString(CultureInfo.InvariantCulture) })
  .ConfigureAwait(false);

Assert.Equal(MagicNumber, execResult.Stdout.Trim());

Connecting a running container to an existing network

If a container is already running, use IContainer.ConnectAsync(...) to attach it to an existing network.

The network must already exist. You can reference it either by network name or by an INetwork instance:

csharp
var network = new NetworkBuilder()
  .WithName(Guid.NewGuid().ToString("D"))
  .Build();

var container = new ContainerBuilder("alpine:3.20.0")
  .WithEntrypoint("top")
  .Build();

await network.CreateAsync()
  .ConfigureAwait(false);

await container.StartAsync()
  .ConfigureAwait(false);

await container.ConnectAsync(network)
  .ConfigureAwait(false);

// Equivalent when only the network name is available:
await container.ConnectAsync(network.Name)
  .ConfigureAwait(false);

Prefer WithNetwork(...) during container configuration whenever possible. Use ConnectAsync(...) when you explicitly need to attach a running container to an already existing network.

Exposing container ports to the host

It is common to connect to a container from your test process running on your test host. To bind and expose a container port, use the WithPortBinding(ushort, true) container builder member. To retrieve the actual port at runtime, use the container GetMappedPublicPort(ushort) member. Further information on network configurations is included in our best practices.

Exposing host ports to the container

Sometimes containerized tests require access to services running on the test host. Setting up this connection can be very complex. Fortunately, Testcontainers simplifies the configuration to a bare minimum. It utilizing SSH and setting up an encrypted connection that forwards requests from a container to the test host.

In order to expose ports of services that are running on the test host to containers, we need to configure Testcontainers first. It is important to set up this configuration before configuring any container resources, otherwise, we are not able to forward traffic properly.

csharp title="exposing the host port 8080"
await TestcontainersSettings.ExposeHostPortsAsync(8080)
  .ConfigureAwait(false);

From the perspective of a container, the test host is available through the hostname host.testcontainers.internal. Ports are forwarded one-to-one.

csharp title="sending a get request to the test host"
_ = await curlContainer.ExecAsync(new[] { "curl", "http://host.testcontainers.internal:8080" })
  .ConfigureAwait(false);

Supported commands

Builder method Description
WithDockerEndpoint Sets the Docker daemon socket to connect to.
WithCleanUp Will remove the network automatically after all tests have been run.
WithReuse Reuses an existing network instead of creating a new one (experimental).
WithLabel Applies metadata to the network e.g. -l, --label "testcontainers=awesome".
WithName Sets the network name e.g. docker network create "testcontainers".
WithDriver Sets the network driver e.g. -d, --driver "bridge"
WithOption Adds a driver specific option -o, --opt "com.docker.network.driver.mtu=1350"
WithCreateParameterModifier Allows low level modifications of the Docker network create parameter.
WithLogger Sets the logger that replaces the default console logger.

Api Low Level Api Access (`docs/api/low_level_api_access.md`)

Low-level API access

Testcontainers does not expose all available Docker Engine APIs through its container, image, network, and volume builders. In some cases, you may need to access the underlying Docker Engine API to configure specific properties that are not available via Testcontainers' API. To access all available Docker Engine API properties for creating a resource, use the builder method: WithCreateParameterModifier(Action<TCreateResourceEntity>). This method allows you to use a callback to configure the final payload that is sent to the Docker Engine.

csharp title="setting the memory limit to 2gb"
const long TwoGB = 2L * 1024 * 1024 * 1024;
_ = new ContainerBuilder("alpine:3.20.0")
  .WithCreateParameterModifier(parameterModifier => parameterModifier.HostConfig.Memory = TwoGB);

Api Resource Reaper (`docs/api/resource_reaper.md`)

Resource Reaper

Testcontainers automatically assigns a Resource Reaper session id to each Docker resource. After the tests are finished — whether they are successful or not — Moby Ryuk will take care of the remaining Docker resources and removes them. You can change the Resource Reaper session and group Docker resources together. Right now, only Linux containers are supported.

!!! tip

text
Whenever possible, do **not** disable the Resource Reaper. It keeps your machine and CI/CD environment clean. If at all, consider disabling the Resource Reaper only for environments that have a mechanism to cleanup Docker resources, e.g. ephemeral CI nodes.

Moby Ryuk derives its name from the anime character Ryuk and is a fitting choice due to the intriguing narrative of the anime Death Note.

Copy image to a private registry

Testcontainers for .NET pins the Ryuk image to this manifest list (image index) digest:

text
testcontainers/ryuk:0.14.0@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0

If you depend on a private registry, make the image available there either through a registry proxy (pull-through cache) or by copying it from Docker Hub with a tool that preserves the manifest list and all platform variants, for example skopeo:

=== "Linux/macOS"
shell skopeo \ copy \ --all \ --preserve-digests \ docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 \ docker://registry.mycompany.com/testcontainers/ryuk:0.14.0

=== "Windows"
powershell # There's no Skopeo package for Windows. docker run ` --rm ` quay.io/skopeo/stable:v1.22.0 ` copy ` --all ` --preserve-digests ` docker://docker.io/testcontainers/ryuk@sha256:7c1a8a9a47c780ed0f983770a662f80deb115d95cce3e2daa3d12115b8cd28f0 ` docker://registry.mycompany.com/testcontainers/ryuk:0.14.0

!!! warning

text
Pulling, tagging, and pushing the image manually is not sufficient. That workflow creates a new manifest and only includes the platform variant pulled locally, breaking multi-architecture support.

If your registry cannot preserve the digest or you want to use a different image reference, set the TESTCONTAINERS_RYUK_CONTAINER_IMAGE environment variable:

shell
TESTCONTAINERS_RYUK_CONTAINER_IMAGE=registry.mycompany.com/testcontainers/ryuk:0.14.0

Api Resource Reuse (`docs/api/resource_reuse.md`)

Reusing resources

Reuse is an experimental feature designed to simplify and enhance the development experience. Instead of disposing resources after the tests are finished, enabling reuse will retain the resources and reuse them in the next test run. Testcontainers assigns a hash value according to the builder configuration. If it identifies a matching resource, it will reuse this resource instead of creating a new one. Enabling reuse will disable the Resource Reaper, meaning the resource will not be cleaned up.

csharp title="enable container reuse"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithReuse(true);

The reuse implementation does currently not consider (support) all builder APIs when calculating the hash value. Therefore, collisions may occur. To prevent collisions, simply use a distinct label to identify the resource.

csharp title="label container resource to identify it"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithReuse(true)
  .WithLabel("reuse-id", "WeatherForecast");

To take full control over the hash value, pass a custom function to the WithReuse overload. The function receives the resource configuration and returns the hash string used to identify and match the resource.

csharp title="override the reuse hash calculation"
_ = new ContainerBuilder("alpine:3.20.0")
  .WithReuse(_ => "custom-hash");

!!! warning

text
A matching custom hash does not guarantee that the resource is compatible. The default implementation derives the hash from the builder configuration, so a match implies an identical configuration. With a custom hash, it is the caller's responsibility to ensure that the hash uniquely and accurately reflects the intended configuration.

The current implementation considers the following resource configurations and their corresponding builder APIs when calculating the hash value.

!!! note

text
Version 3.8.0 did not include the container configuration's name in the hash value.

By default, all module resource configurations are included. This works well for simple value and reference types that can be serialized and deserialized to JSON without custom converters. However, more complex resource configurations may require a custom converter to properly serialize and deserialize their values.

!!! warning

text
Reuse does not replace singleton implementations to improve test performance. Prefer proper shared instances according to your chosen test framework.

Calling Dispose() on a reusable container will stop it. Testcontainers will automatically start it in the next test run. This will assign a new random host port. Some services (e.g. Kafka) require the random assigned host port on the initial configuration. This may interfere with the new random assigned host port.

Keep in mind that reuse depends on stable builder and resource configurations. If a configuration changes during a test run, the reuse hash will also change, and Testcontainers cannot pick up the resource again. For example, the network and volume builder assign a random name to the resource; this default configuration will not work without overriding and setting the name to a fixed value.

csharp title="override networkbuilder's default random network name configuration"
_ = new NetworkBuilder()
  .WithReuse(true)
  .WithName("WeatherForecast");

Api Wait Strategies (`docs/api/wait_strategies.md`)

Wait strategies

Wait strategies are useful to detect if a container is ready for testing (i.e., if the application inside the container is in a usable state). They check different indicators of readiness of the container and complete as soon as they are fulfilled. Per default, Testcontainers will wait until the container is running. For the most images, that is not enough. You can chain different pre-configured wait strategies together or implement your own by implementing the IWaitUntil interface.

csharp
_ = Wait.ForUnixContainer()
  .UntilInternalTcpPortIsAvailable(80)
  .UntilFileExists("/tmp/foo")
  .UntilFileExists("/tmp/bar")
  .AddCustomWaitStrategy(new MyCustomWaitStrategy());

In some cases, it might be necessary to configure the behavior of a wait strategy further, being able to cancel the readiness check. The API provides a callback that allows setting additional configurations such as Retries, Interval, and Timeout.

csharp title="cancel the readiness check after one minute"
_ = Wait.ForUnixContainer()
  .UntilMessageIsLogged("Server started", o => o.WithTimeout(TimeSpan.FromMinutes(1)));

Besides configuring the wait strategy, cancelling a container start can always be done utilizing a CancellationToken.

Wait strategy modes

Wait strategy modes define how Testcontainers for .NET handles container readiness checks. By default, wait strategies assume the container remains running throughout the startup. If a container exits unexpectedly during startup, Testcontainers for .NET will throw a ContainerNotRunningException containing the exit code and logs.

Some containers are intended to stop after completing short-lived tasks like migrations or setup scripts. In these cases, the container exit is expected, not a failure. Use WaitStrategyMode.OneShot to treat a normal exit as successful rather than throwing an exception.

csharp
_ = Wait.ForUnixContainer()
  .UntilMessageIsLogged("Migration completed", o => o.WithMode(WaitStrategyMode.OneShot));

Wait until an HTTP(S) endpoint is available

You can choose to wait for an HTTP(S) endpoint to return a particular HTTP response status code or to match a predicate. The default configuration tries to access the HTTP endpoint running inside the container. Chose ForPort(ushort) or ForPath(string) to adjust the endpoint or UsingTls() to switch to HTTPS. When using UsingTls() port 443 is used as a default. If your container exposes a different HTTPS port, make sure that the correct waiting port is configured accordingly.

Waiting for HTTP response status code _200 OK_ on port 80

csharp
_ = Wait.ForUnixContainer()
  .UntilHttpRequestIsSucceeded(request => request
    .ForPath("/"));

Waiting for HTTP response status code _200 OK_ or _301 Moved Permanently_

csharp
_ = Wait.ForUnixContainer()
  .UntilHttpRequestIsSucceeded(request => request
    .ForPath("/")
    .ForStatusCode(HttpStatusCode.OK)
    .ForStatusCode(HttpStatusCode.MovedPermanently));

Waiting for the HTTP response status code to match a predicate

csharp
_ = Wait.ForUnixContainer()
  .UntilHttpRequestIsSucceeded(request => request
    .ForPath("/")
    .ForStatusCodeMatching(statusCode => statusCode >= HttpStatusCode.OK && statusCode < HttpStatusCode.MultipleChoices));

Wait until a TCP port is available

Testcontainers provides two distinct strategies for waiting until a TCP port becomes available, each serving different purposes depending on your testing needs.

Wait until an internal TCP port is available

UntilInternalTcpPortIsAvailable(int) checks if a service inside the container is listening on the specified port by testing connectivity from within the container itself. This strategy verifies that your application or service has actually started and is ready to accept connections.

csharp
_ = Wait.ForUnixContainer()
  .UntilInternalTcpPortIsAvailable(8080);

!!! note

text
Just because a service is listening on the internal TCP port does not necessarily mean it is fully ready to handle requests. Often, wait strategies such as checking for specific log messages or verifying a health endpoint provide more reliable confirmation that the service is operational.

Wait until an external TCP port is available

UntilExternalTcpPortIsAvailable(int) checks if the port is accessible from the test host to the container. This verifies that the port mapping has been established and the port is reachable externally.

csharp
_ = Wait.ForUnixContainer()
  .UntilExternalTcpPortIsAvailable(8080);

!!! note

text
External TCP port availability does not guarantee that the actual service inside the container is ready to handle requests. It only confirms that the port mapping is established and a connection can be made to the host-side proxy.

Wait until the container is healthy

If the Docker image supports Dockers's HEALTHCHECK feature, like the following configuration:

dockerfile
FROM alpine
HEALTHCHECK --interval=1s CMD test -e /healthcheck

You can leverage the container's health status as your wait strategy to report readiness of your application or service:

csharp
_ = new ContainerBuilder("alpine:3.20.0")
  .WithWaitStrategy(Wait.ForUnixContainer().UntilContainerIsHealthy());

Examples Aspnet (`docs/examples/aspnet.md`)

ASP.NET Core

No matter if your tests require databases, message brokers, your own services or even a running instance of your entire application, leveraging Testcontainers in your tests means you can set up the infrastructure fast and reliably. You can also run tests in parallel against multiple lightweight or a single shared heavyweight instance, depending on the use case. xUnit.net's shared context and other test frameworks offers several methods to access resources efficiently among different tests and scopes.

Utilizing WebApplicationFactory

ASP.NET Core offers the WebApplicationFactory<TEntryPoint> class for bootstrapping and creating a TestServer dedicated to integration tests. There are several methods to integrate Testcontainers and dependent app services into ASP.NET Core integration tests.

One approach extends or overrides the application's configuration section to set up the connection string before creating the TestServer. This approach requires manually starting the dependent service in advance.

csharp title="set redis connection string"
private sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
  protected override void ConfigureWebHost(IWebHostBuilder builder)
  {
    builder.UseSetting("ConnectionStrings:RedisCache", _redisContainer.GetConnectionString());
  }
}

A slightly more complex approach involves implementing the IConfigurationSource interface and the ConfigurationProvider class. This approach adds a new configuration source and automatically starts the dependent service before creating the TestServer.

csharp title="add redis configuration source"
private sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
  protected override void ConfigureWebHost(IWebHostBuilder builder)
  {
    builder.ConfigureAppConfiguration(configure =>
    {
      configure.Add(new RedisConfigurationSource());
    });
  }
}

private sealed class RedisConfigurationSource : IConfigurationSource
{
  public IConfigurationProvider Build(IConfigurationBuilder builder)
  {
    return new RedisConfigurationProvider();
  }
}

private sealed class RedisConfigurationProvider : ConfigurationProvider
{
  private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

  public override void Load()
  {
    // Until the asynchronous configuration provider is available,
    // we use the TaskFactory to spin up a new task that handles the work:
    // https://github.com/dotnet/runtime/issues/79193
    // https://github.com/dotnet/runtime/issues/36018
    TaskFactory.StartNew(LoadAsync)
      .Unwrap()
      .ConfigureAwait(false)
      .GetAwaiter()
      .GetResult();
  }

  public async Task LoadAsync()
  {
    var redisContainer = new RedisBuilder("redis:7.0").Build();

    await redisContainer.StartAsync()
      .ConfigureAwait(false);

    Set("ConnectionStrings:RedisCache", redisContainer.GetConnectionString());
  }
}

WeatherForecast example

The following example adds tests to an ASP.NET Core Blazor application. The tests cover the web front-end including the REST API of a weather forecast application. Testcontainers builds and ships our app in a Docker image, runs it in a Docker container, orchestrates the necessary resources, and executes the tests against it. This setup includes a Microsoft SQL Server to persist data and covers a common use case among many productive .NET applications. You find the entire example in the testcontainers-dotnet repository.

To use Testcontainers' pre-configured Microsoft SQL Server module, add the Testcontainers.MsSql NuGet dependency to your test project:

shell
dotnet add package Testcontainers.MsSql --version 3.0.0

!!! note

text
When running on Macs with Apple Silicon, make sure that you are using Docker Desktop for macOS version 4.16 or newer, and that the Virtualization Framework is enabled, with Rosetta 2 installed to run [x86-64](https://techcommunity.microsoft.com/t5/sql-server-blog/azure-sql-edge-update-september-2023/ba-p/3930827) based containers.

The WeatherForecastContainer class configures in the default constructor all dependencies to start the container that hosts our application.

csharp
const string weatherForecastStorage = "weatherForecastStorage";

const string connectionString = $"server={weatherForecastStorage};user id={MsSqlBuilder.DefaultUsername};password={MsSqlBuilder.DefaultPassword};database={MsSqlBuilder.DefaultDatabase}";

_weatherForecastNetwork = new NetworkBuilder()
  .Build();

_msSqlContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04")
  .WithNetwork(_weatherForecastNetwork)
  .WithNetworkAliases(weatherForecastStorage)
  .Build();

_weatherForecastContainer = new ContainerBuilder(Image)
  .WithNetwork(_weatherForecastNetwork)
  .WithPortBinding(WeatherForecastImage.HttpsPort, true)
  .WithEnvironment("ASPNETCORE_URLS", "https://+")
  .WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Path", WeatherForecastImage.CertificateFilePath)
  .WithEnvironment("ASPNETCORE_Kestrel__Certificates__Default__Password", WeatherForecastImage.CertificatePassword)
  .WithEnvironment("ConnectionStrings__DefaultConnection", connectionString)
  .WithWaitStrategy(Wait.ForUnixContainer().UntilInternalTcpPortIsAvailable(WeatherForecastImage.HttpsPort))
  .Build();

First, the class configures the Docker network. The application and database use this private network to communicate with each other. Followed by that, the class sets up the configuration of the Microsoft SQL Server container. Once all the dependencies are in place, the example configures the weather forecast application conveniently with environment variables. xUnit.net calls IAsyncLifetime.InitializeAsync immediately after the class has been created. The test project uses this mechanism to create and start all dependencies before any test run:

csharp
await Image.InitializeAsync()
  .ConfigureAwait(false);

await _weatherForecastNetwork.CreateAsync()
  .ConfigureAwait(false);

await _msSqlContainer.StartAsync()
  .ConfigureAwait(false);

await _weatherForecastContainer.StartAsync()
  .ConfigureAwait(false);

xUnit.net passes the WeatherForecastContainer class fixture instance to the test class WeatherForecastTest. Each test collection Api and Web creates a new instance of WeatherForecastContainer and spins up an isolated environment. Even multiple test sessions do not interfere with each other. To run tests against a single shared heavyweight instance (collection fixture), add all dependencies to a collection definition. This works not only for containers, but also for any other Docker resource like images, networks or volumes.

As soon as the container is up and the application is running, each test sends an HTTP request to the weather forecast application and validates the REST or web front-end response. To visualize the web front-end, Selenium takes a screenshot right before and after the test.

Testcontainers not only works great for testing an application or service out-of-process, as shown in the example above, but it is probably even better for testing it in-process as shown in this example. When used in conjunction with the ASP.NET WebApplicationFactory<TEntryPoint> class, Testcontainers spins up the dependent container together with the application, resulting in much faster tests. Depending on the application or service configuration, adding Testcontainers to bootstrap dependent services significantly improves the development experience. There is no longer a need to ensure that dependent services are running and wired up correctly on the development machine or CI environment.


Modules Call Out Test Projects (`docs/modules/_call_out_test_projects.txt`)

!!! tip

text
For the complete source code of this example and additional information, please refer to our [test projects](https://github.com/testcontainers/testcontainers-dotnet/tree/develop/tests).

Modules Activemq (`docs/modules/activemq.md`)

Apache ActiveMQ Artemis

Apache ActiveMQ Artemis is an open source project to build a multi-protocol, embeddable, very high performance, clustered, asynchronous messaging system.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.ActiveMq

You can start an Apache ActiveMQ Artemis container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Base Test Class"
csharp --8<-- "tests/Testcontainers.ActiveMq.Tests/ArtemisContainerTest.cs:UseArtemisContainer" }
=== "Without Auth"
csharp --8<-- "tests/Testcontainers.ActiveMq.Tests/ArtemisContainerTest.cs:UseArtemisContainerNoAuth"
=== "Default Credentials"
csharp --8<-- "tests/Testcontainers.ActiveMq.Tests/ArtemisContainerTest.cs:UseArtemisContainerDefaultAuth"
=== "Custom Credentials"
csharp --8<-- "tests/Testcontainers.ActiveMq.Tests/ArtemisContainerTest.cs:UseArtemisContainerCustomAuth"

Connect to the container and produce a message:

=== "Establish Connection"
csharp --8<-- "tests/Testcontainers.ActiveMq.Tests/ArtemisContainerTest.cs:EstablishConnection"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.ActiveMq.Tests/Testcontainers.ActiveMq.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Aspire Dashboard (`docs/modules/aspire-dashboard.md`)

Aspire dashboard

The Aspire dashboard is a standalone tool for viewing the logs, traces, and metrics that your apps emit over the OpenTelemetry Protocol (OTLP). It provides a web interface to explore telemetry during local development.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.AspireDashboard

You can start an Aspire dashboard container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.AspireDashboard.Tests/AspireDashboardContainerTest.cs:UseAspireDashboardContainer"

The test example exports a trace to the dashboard over OTLP and queries the dashboard's telemetry API to confirm that the span was ingested. Use GetOtlpGrpcAddress() or GetOtlpHttpAddress() to configure the OpenTelemetry exporter in your .NET service, and GetDashboardAddress() to open the dashboard's web interface.

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.AspireDashboard.Tests/Testcontainers.AspireDashboard.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Azurite (`docs/modules/azurite.md`)

Azurite

Azurite is an open-source emulator for Azure Blob, Queue and Table storage that provides a local environment for developing and testing Azure Storage applications.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Azurite

You can start an Azurite container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Azurite.Tests/AzuriteContainerTest.cs:CreateAzuriteContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Azurite.Tests/AzuriteContainerTest.cs:UseAzuriteContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Azurite.Tests/Testcontainers.Azurite.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Access Azurite

csharp title="gets the azurite connection string"
string connectionString = _azuriteContainer.GetConnectionString();
csharp title="gets the blob, queue and table endpoints"
string blobEndpoint = _azuriteContainer.GetBlobEndpoint();
string queueEndpoint = _azuriteContainer.GetQueueEndpoint();
string tableEndpoint = _azuriteContainer.GetTableEndpoint();

Configure SSL

To enable HTTPS, use the following container builder method to configure Azurite with an SSL certificate and private key. The connection string and the Blob, Queue and Table endpoints then use the https scheme:

=== "Configure the SSL Certificate"
csharp --8<-- "tests/Testcontainers.Azurite.Tests/AzuriteContainerTest.cs:ConfigureAzuriteContainerCertificate"

!!! note

text
Please ensure that both the certificate and private key are provided in PEM format.

The client must trust the server certificate. In this example, the Azure client validates the SSL certificate against the CA certificate that signed it. The transport is assigned to the client options, as shown in the usage example:

=== "Configure the Azure Client"
csharp --8<-- "tests/Testcontainers.Azurite.Tests/AzuriteContainerTest.cs:ConfigureAzuriteClientCertificate"

Enable in-memory persistence

By default, Azurite persists its data to disk. If your tests do not need the data after the container stops, use the following configuration to keep it in memory instead:

=== "In-Memory Persistence Configuration"
csharp --8<-- "tests/Testcontainers.Azurite.Tests/AzuriteContainerTest.cs:InMemoryContainerConfiguration"

The in-memory storage is limited to 50% of the total memory of the container by default. To set a different limit, pass the limit in megabytes to WithInMemoryPersistence.


Modules Cassandra (`docs/modules/cassandra.md`)

Apache Cassandra

Apache Cassandra is a powerful, open-source, distributed NoSQL database that is highly available and fault-tolerant, used to store, manage, and retrieve structured data.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Cassandra

You can start an Apache Cassandra container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start a Cassandra Container"
csharp var cassandraContainer = new CassandraBuilder("cassandra:5.0").Build(); await cassandraContainer.StartAsync();

The following example utilizes the xUnit.net module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Cassandra.Tests/CassandraContainerTest.cs:UseCassandraContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Cassandra.Tests/Testcontainers.Cassandra.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Clickhouse (`docs/modules/clickhouse.md`)

ClickHouse

ClickHouse is a high-performance, column-oriented SQL database management system (DBMS) for online analytical processing (OLAP).

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.ClickHouse

You can start a ClickHouse container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Test Class"
csharp --8<-- "tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.docs.cs:UseClickHouseContainer" }

Connect to the container:

=== "Establish Connection"
csharp --8<-- "tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.docs.cs:EstablishConnection"

Execute a SQL script:

=== "Run SQL Script"
csharp --8<-- "tests/Testcontainers.ClickHouse.Tests/ClickHouseContainerTest.docs.cs:RunSQLScript"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.ClickHouse.Tests/Testcontainers.ClickHouse.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Db2 (`docs/modules/db2.md`)

Db2

Db2 is a relational database engine developed by IBM.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Db2

!!! warning

text
The Linux client dependency, [Net.IBM.Data.Db2-lnx](https://www.nuget.org/packages/Net.IBM.Data.Db2-lnx), requires additional configurations. We use the [Testcontainers.Db2.Tests.targets](https://github.com/testcontainers/testcontainers-dotnet/blob/develop/tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.targets) file to configure the environment variables: `LD_LIBRARY_PATH`, `PATH`, `DB2_CLI_DRIVER_INSTALL_PATH`, at runtime.

You can start a Db2 container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start a Db2 Container"
csharp var db2Container = new Db2Builder("icr.io/db2_community/db2:12.1.0.0").Build(); await db2Container.StartAsync();

The following example utilizes the xUnit.net module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Db2.Tests/Db2ContainerTest.cs:UseDb2Container"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Db2.Tests/Testcontainers.Db2.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Elasticsearch (`docs/modules/elasticsearch.md`)

Elasticsearch

Elasticsearch is a distributed, RESTful search and analytics engine capable of addressing a growing number of use cases. As the heart of the Elastic Stack, it centrally stores data for lightning fast search, fine‑tuned relevancy, and powerful analytics that scale with ease.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Elasticsearch

You can start an Elasticsearch container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Elasticsearch.Tests/ElasticsearchContainerTest.cs:CreateElasticsearchContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Elasticsearch.Tests/ElasticsearchContainerTest.cs:UseElasticsearchContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Elasticsearch.Tests/Testcontainers.Elasticsearch.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

OpenTelemetry (OTLP)

Elasticsearch 9.5 and later accept OTLP over HTTP. ElasticsearchContainer.GetOtlpEndpoint() returns the base endpoint the exporter sends the telemetry data to. If the endpoint is set via OTEL_EXPORTER_OTLP_ENDPOINT, the exporter appends the signal path, such as v1/metrics. If the endpoint is set via OtlpExporterOptions.Endpoint, the exporter uses it as is, and the signal path must be appended manually. In contrast to the connection string, the endpoint does not contain the credentials. Clients must send them in the Authorization header:

=== "Export Telemetry Data"
csharp --8<-- "tests/Testcontainers.Elasticsearch.Tests/ElasticsearchContainerOtlpTest.cs:UseElasticsearchOtlpEndpoint"

A note to developers

The Testcontainers module creates a container that listens to requests over HTTPS. Elasticsearch generates a self-signed certificate authority (CA) during the startup that signs the HTTP certificate. ElasticsearchContainer.GetCertificateAsync() reads this certificate authority (CA) from the container. Configure the client to trust it, otherwise .NET will reject the certificate coming from the container.

Besides the Elasticsearch client, any other client can be configured to trust the certificate authority (CA) too. The example below uses the CertificateValidations.AuthorityIsRoot(X509Certificate) helper from Elastic.Transport. Without it, build an X509Chain with X509ChainTrustMode.CustomRootTrust and add the certificate authority (CA) to ChainPolicy.CustomTrustStore:

=== "Trust the Certificate Authority"
csharp --8<-- "tests/Testcontainers.Elasticsearch.Tests/ElasticsearchContainerTest.cs:UseElasticsearchCertificate"


Modules Eventhubs (`docs/modules/eventhubs.md`)

Azure Event Hubs

Azure Event Hubs emulator⁠ is designed to offer a local development experience for Azure Event Hubs⁠, enabling you to develop and test code against the service in isolation, free from cloud interference.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.EventHubs

You can start an Azure Event Hubs container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.EventHubs.Tests/EventHubsContainerTest.cs:CreateEventHubsContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.EventHubs.Tests/EventHubsContainerTest.cs:UseEventHubsContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.EventHubs.Tests/Testcontainers.EventHubs.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Use a custom Azurite instance

The Event Hubs module depends on an Azurite container instance. The module automatically creates and configures the necessary resources and connects them. If you prefer to use your own instance, you can use the following method to configure the builder accordingly:

=== "Reuse Existing Resources"
csharp --8<-- "tests/Testcontainers.EventHubs.Tests/EventHubsContainerTest.cs:ReuseExistingAzuriteContainer"


Modules Garnet (`docs/modules/garnet.md`)

Garnet

Garnet is a high-performance, Redis-compatible in-memory data store.

The Testcontainers Redis module is compatible with the Garnet container image. You can use the existing Testcontainers.Redis package without requiring a separate Garnet module.

!!! note

text
If Garnet introduces features or configuration options that require dedicated support in the future, we can introduce a separate module at that time.

Usage

Add the Redis module:

shell title="nuget"
dotnet add package Testcontainers.Redis

Start a Garnet container using the Redis builder:

csharp
var garnetContainer = new RedisBuilder("ghcr.io/microsoft/garnet:1.0.99").Build();
await garnetContainer.StartAsync();

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Grafana (`docs/modules/grafana.md`)

Grafana

Grafana is an open-source platform for monitoring, visualization, and analytics. It allows you to query, visualize, alert on, and explore metrics, logs, and traces from various data sources through customizable dashboards.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Grafana

You can start a Grafana container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs:CreateGrafanaContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Grafana.Tests/GrafanaContainerTest.cs:UseGrafanaContainer"

The test example queries the Grafana API endpoint GET /api/org/ to retrieve the current organization. This API endpoint requires authentication, so the HTTP client's authorization header is set with Basic authentication using the username and password configured through the Grafana builder. The default configuration uses the username admin and password admin.

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Grafana.Tests/Testcontainers.Grafana.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Enable anonymous access

Developers can enable anonymous access using the Grafana builder API WithAnonymousAccessEnabled(). This will enable anonymous access and no authentication is necessary to access Grafana:

csharp
GrafanaContainer _grafanaContainer = new GrafanaBuilder("grafana/grafana:12.2").WithAnonymousAccessEnabled().Build();

Modules Mongodb (`docs/modules/mongodb.md`)

MongoDB

MongoDB is a cross-platform document-oriented database. MongoDB's document model is simple for developers to use within their applications, while still providing all the complex capabilities of traditional relational databases.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.MongoDb

You can start a MongoDB container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs:CreateMongoDbContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs:UseMongoDbContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.MongoDb.Tests/Testcontainers.MongoDb.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

MongoDB replica set

By default, MongoDB runs as a standalone instance. If your tests require a MongoDB replica set, use the following configuration which will initialize a single-node replica set:

=== "Replica Set Configuration"
csharp --8<-- "tests/Testcontainers.MongoDb.Tests/MongoDbContainerTest.cs:ReplicaSetContainerConfiguration"


Modules Mssql (`docs/modules/mssql.md`)

Microsoft SQL Server

Microsoft SQL Server, also known as MSSQL, is a relational database engine developed by Microsoft and is a popular choice in enterprise systems.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.MsSql

You can start an MSSQL container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start an MSSQL Container"
csharp var msSqlContainer = new MsSqlBuilder("mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04").Build(); await msSqlContainer.StartAsync();

The following example utilizes the xUnit.net module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.MsSql.Tests/MsSqlContainerTest.cs:UseMsSqlContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.MsSql.Tests/Testcontainers.MsSql.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

A note to developers

Once Testcontainers creates a server instance, developers may use the connection string with any of the popular data-access technologies found in the .NET ecosystem. Some of these libraries include Entity Framework Core, Dapper, and NHibernate. At which point, developers can execute database migrations and SQL scripts.


Modules Neo4j (`docs/modules/neo4j.md`)

Neo4j

Neo4j is a graph database designed to work with nodes and edges. It is an ACID-compliant transactional graph database engine, and developers can communicate with it using the HTTP endpoint or by using the Bolt protocol.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Neo4j

You can start an Neo4j container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Neo4j.Tests/Neo4jContainerTest.cs:CreateNeo4jContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Neo4j.Tests/Neo4jContainerTest.cs:UseNeo4jContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Neo4j.Tests/Testcontainers.Neo4j.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Opensearch (`docs/modules/opensearch.md`)

OpenSearch

OpenSearch is an open-source, enterprise-grade search and observability suite that brings order to unstructured data at scale.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.OpenSearch

You can start an OpenSearch container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start an OpenSearch Container"
csharp var openSearchContainer = new OpenSearchBuilder("opensearchproject/opensearch:2.12.0").Build(); await openSearchContainer.StartAsync();

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Base Test Class"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:BaseClass" }
=== "Insecure No Auth"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:InsecureNoAuth"
=== "SSL Default Credentials"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:SslBasicAuthDefaultCredentials"
=== "SSL Custom Credentials"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:SslBasicAuthCustomCredentials"

How to check if the client has established a connection:

=== "Ping Example"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:PingExample"

Creating an index and alias:

=== "Create Index and Alias"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:CreateIndexAndAlias"
=== "Create Index Implementation"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:CreateIndexImplementation"

Indexing and searching a document:

=== "Indexing Document"
csharp --8<-- "tests/Testcontainers.OpenSearch.Tests/OpenSearchContainerTest.cs:IndexingDocument"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.OpenSearch.Tests/Testcontainers.OpenSearch.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Playwright (`docs/modules/playwright.md`)

Playwright

Playwright is a framework for web testing and automation. It allows testing across all modern rendering engines including Chromium, WebKit, and Firefox with a single API. This module provides pre-configured browser containers for automated testing.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Playwright

You can start a Playwright container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start a Playwright Container"
csharp var playwrightContainer = new PlaywrightBuilder("mcr.microsoft.com/playwright:v1.55.1").Build(); await playwrightContainer.StartAsync();

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

This example demonstrates the Playwright container accessing a web site running inside another container (using the testcontainers/helloworld image). Both containers are assigned to a shared network (see the Network configuration section) to enable communication between them.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Playwright.Tests/PlaywrightContainerTest.cs:UsePlaywrightContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Playwright.Tests/Testcontainers.Playwright.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Network configuration

The Playwright container is configured with a network that can be shared with other containers. This is useful when testing applications that need to communicate with other services. Use the GetNetwork() method to access the container's network:

csharp
IContainer helloWorldContainer = new ContainerBuilder("testcontainers/helloworld:1.3.0")
    .WithNetwork(_playwrightContainer.GetNetwork())
    .Build();

Modules Postgres (`docs/modules/postgres.md`)

PostgreSQL

PostgreSQL is a powerful, open-source relational database management system (RDBMS) used to store, manage, and retrieve structured data.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.PostgreSql

You can start a PostgreSQL container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start a PostgreSQL Container"
csharp var postgreSqlContainer = new PostgreSqlBuilder("postgres:15.1").Build(); await postgreSqlContainer.StartAsync();

The following example utilizes the xUnit.net module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs:UsePostgreSqlContainer"

SSL

Use WithSsl to enable TLS and map the server certificates. Configure the client connection string with SslMode and (for validation) the CA certificate.

!!! note

text
When SSL is enabled, Testcontainers does not set the SSL mode for the connection string. You will need to choose the `SslMode` and configure it yourself.

!!! note

text
When SSL is enabled, Testcontainers overrides the entrypoint to copy the certificates for the user that runs PostgreSQL and then runs `docker-entrypoint.sh`, not an entrypoint defined by a custom image. If you set an entrypoint with `WithEntrypoint`, Testcontainers does not override it. You will need to copy the certificates from `/etc/ssl/postgresql` to `/var/run/postgresql/ssl` yourself and make sure only the PostgreSQL user can access them.
csharp
--8<-- "tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs:PostgreSqlSslBuilder"
csharp
--8<-- "tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs:PostgreSqlSslConnectionString"

VerifyFull and SANs

SslMode=VerifyFull additionally validates that the host in the connection string matches a subject alternative name (SAN) of the server certificate. Testcontainers usually resolves the container host to the IP address 127.0.0.1. The certificate must include a matching IP SAN, for example IP:127.0.0.1:

csharp
--8<-- "tests/Testcontainers.PostgreSql.Tests/PostgreSqlContainerTest.cs:PostgreSqlSslVerifyFull"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.PostgreSql.Tests/Testcontainers.PostgreSql.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Pulsar (`docs/modules/pulsar.md`)

Apache Pulsar

Apache Pulsar is a distributed messaging and event streaming platform designed for high-throughput, low-latency data processing across multiple topics.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Pulsar

You can start an Apache Pulsar container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Pulsar.Tests/PulsarContainerTest.cs:CreatePulsarContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Pulsar.Tests/PulsarContainerTest.cs:UsePulsarContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Pulsar.Tests/Testcontainers.Pulsar.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Access Apache Pulsar

csharp title="gets the pulsar broker url"
string pulsarBrokerUrl = _pulsarContainer.GetPulsarBrokerUrl();
csharp title="gets the pulsar service url"
string pulsarServiceUrl = _pulsarContainer.GetHttpServiceUrl();

Enable token authentication

If you need to use token authentication, use the following builder configuration to enable authentication:

csharp
PulsarContainer _pulsarContainer = new PulsarBuilder("apachepulsar/pulsar:3.0.9").WithTokenAuthentication().Build();

Start the container and obtain an authentication token with a specified expiration time

csharp
var authToken = await container.CreateAuthenticationTokenAsync(TimeSpan.FromHours(1))
    .ConfigureAwait(false);

Alternatively, set the token to never expire

csharp
var authToken = await container.CreateAuthenticationTokenAsync(Timeout.InfiniteTimeSpan)
    .ConfigureAwait(false);

Enable Pulsar Functions

If you need to use Pulsar Functions, use the following builder configuration to enable it:

csharp
PulsarContainer _pulsarContainer = new PulsarBuilder("apachepulsar/pulsar:3.0.9").WithFunctions().Build();

Modules Qdrant (`docs/modules/qdrant.md`)

Qdrant

Qdrant is an open source vector database designed for scalable and efficient similarity search and nearest neighbor retrieval. It provides both RESTful and gRPC APIs, making it easy to integrate with various applications, including search, recommendation, AI, and machine learning systems.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Qdrant

You can start a Qdrant container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Qdrant.Tests/QdrantDefaultContainerTest.cs:UseQdrantContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Qdrant.Tests/Testcontainers.Qdrant.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Configure API key

To set and configure an API key, use the following container builder method:

=== "Configure the API Key"
csharp --8<-- "tests/Testcontainers.Qdrant.Tests/QdrantSecureContainerTest.cs:ConfigureQdrantContainerApiKey"

Make sure the underlying Qdrant HTTP or gRPC client adds the API key to the HTTP header or gRPC metadata:

=== "Configure the Qdrant Client"
csharp --8<-- "tests/Testcontainers.Qdrant.Tests/QdrantSecureContainerTest.cs:ConfigureQdrantClientApiKey"

Configure TLS

The following example generates a self-signed certificate and configures the module to use TLS with the certificate and private key:

!!! note

text
Please ensure that both the certificate and private key are provided in PEM format.

=== "Configure the TLS Certificate"
csharp --8<-- "tests/Testcontainers.Qdrant.Tests/QdrantSecureContainerTest.cs:ConfigureQdrantContainerCertificate"

The Qdrant client is configured to validate the TLS certificate using its thumbprint:

=== "Configure the Qdrant Client"
```csharp
--8<-- "tests/Testcontainers.Qdrant.Tests/QdrantSecureContainerTest.cs:ConfigureQdrantClientCertificate-1"

text
--8<-- "tests/Testcontainers.Qdrant.Tests/QdrantSecureContainerTest.cs:ConfigureQdrantClientCertificate-2"
```

A note to developers

The module creates a container that listens to requests over HTTP. The official Qdrant client uses the gRPC APIs to communicate with Qdrant. .NET Core and .NET support the above example with no additional configuration. However, .NET Framework has limited supported for gRPC over HTTP/2, but it can be enabled by:

  1. Configuring the module to use TLS.
  2. Configuring server certificate validation.
  3. Reference System.Net.Http.WinHttpHandler version 6.0.1 or later, and configure WinHttpHandler as handler for GrpcChannelOptions in the Qdrant client.

Refer to the official Qdrant .NET SDK for more information.


Modules Questdb (`docs/modules/questdb.md`)

QuestDB

QuestDB is a high-performance, open-source time-series database designed for fast ingestion and low-latency SQL queries. It exposes its SQL interface over the PostgreSQL wire protocol and ingests time-series data over the InfluxDB Line Protocol (ILP).

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.QuestDb

You can start a QuestDB container instance from any .NET application. To create and start a container instance with the default configuration, use the module-specific builder as shown below:

=== "Start a QuestDB Container"
csharp var questDbContainer = new QuestDbBuilder("questdb/questdb:10.0.1").Build(); await questDbContainer.StartAsync();

The following example utilizes the xUnit.net module to reduce overhead by automatically managing the lifecycle of the dependent container instance. It creates and starts the container using the module-specific builder and injects it as a shared class fixture into the test class.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.QuestDb.Tests/QuestDbContainerTest.cs:UseQuestDbContainer"

The test example creates a table and queries it over the PostgreSQL wire protocol, and ingests a record over ILP. Use GetConnectionString() to configure a PostgreSQL client such as Npgsql, and GetIlpAddress() to configure the QuestDB .NET client. The REST API and the Web Console are available at GetBaseAddress().

The default configuration uses the username quest and password quest. Use WithUsername(string) and WithPassword(string) to configure different credentials.

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.QuestDb.Tests/Testcontainers.QuestDb.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Rabbitmq (`docs/modules/rabbitmq.md`)

RabbitMQ

RabbitMQ is a message broker that enables reliable communication between distributed applications by managing and routing messages between them.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.RabbitMq

You can start a RabbitMQ container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.RabbitMq.Tests/RabbitMqContainerTest.cs:UseRabbitMqContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.RabbitMq.Tests/Testcontainers.RabbitMq.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Redis (`docs/modules/redis.md`)

Redis

Redis is an in-memory key–value database, used as a distributed cache and message broker, with optional durability.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Redis

You can start a Redis container instance from any .NET application. This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Redis.Tests/RedisContainerTest.cs:UseRedisContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Redis.Tests/Testcontainers.Redis.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Servicebus (`docs/modules/servicebus.md`)

Azure Service Bus

Azure Service Bus emulator⁠ is designed to offer a local development experience for Azure Service Bus⁠, enabling you to develop and test code against the service in isolation, free from cloud interference.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.ServiceBus

You can start an Azure Service Bus container instance from any .NET application. Here, we create different container instances and pass them to the base test class. This allows us to test different configurations.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs:CreateServiceBusContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the container. The container is started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the container is removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs:UseServiceBusContainer"

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.ServiceBus.Tests/Testcontainers.ServiceBus.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"

Use a custom MSSQL instance

The Service Bus module depends on an MSSQL container instance. The module automatically creates and configures the necessary resources and connects them. If you prefer to use your own instance, you can use the following method to configure the builder accordingly:

=== "Reuse Existing Resources"
csharp --8<-- "tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs:ReuseExistingMsSqlContainer"

Use a custom Config.json

The Azure Service Bus Emulator provides a default configuration. If a custom configuration is desired, you can use the following method to provide a custom JSON file to the builder accordingly:

=== "Use Custom Configuration"
csharp --8<-- "tests/Testcontainers.ServiceBus.Tests/ServiceBusContainerTest.cs:UseCustomConfiguration"


Modules Toxiproxy (`docs/modules/toxiproxy.md`)

Toxiproxy

Toxiproxy is a proxy to simulate network and system conditions for chaos and resiliency testing. It can simulate latency, timeouts, bandwidth limits, and connection issues between services.

Add the following dependency to your project file:

shell title="nuget"
dotnet add package Testcontainers.Toxiproxy

You can start a Toxiproxy container instance from any .NET application. This example demonstrates how to test network conditions by proxying traffic through Toxiproxy to a Redis container. The test creates both containers in the same network and configures Toxiproxy to redirect traffic from the test host to the Redis container.

=== "Create Container Instance"
csharp --8<-- "tests/Testcontainers.Toxiproxy.Tests/ToxiproxyContainerTest.cs:CreateToxiproxyContainer"

This example uses xUnit.net's IAsyncLifetime interface to manage the lifecycle of the containers. Both the Redis and Toxiproxy containers are started in the InitializeAsync method before the test method runs, ensuring that the environment is ready for testing. After the test completes, the containers are removed in the DisposeAsync method.

=== "Usage Example"
csharp --8<-- "tests/Testcontainers.Toxiproxy.Tests/ToxiproxyContainerTest.cs:UseToxiproxyContainer"

How it works

To test network conditions with Toxiproxy, you need to configure the communication between your test host, Toxiproxy, and the service you want to test:

  1. Place both containers in the same network so the Toxiproxy container and the service container (Redis in this example) can communicate with each other.

  2. Configure the Toxiproxy proxy to redirect traffic from the test host to the service container. The proxy's Listen address specifies where Toxiproxy listens for incoming connections, and the Upstream address specifies the target service.

    === "Proxy Configuration"
    csharp --8<-- "tests/Testcontainers.Toxiproxy.Tests/ToxiproxyContainerTest.cs:ProxyConfiguration"

  3. Connect through Toxiproxy using its hostname and one of its proxied ports. The Toxiproxy module initializes 32 ports starting from 8666 that can be used to configure proxies and redirect traffic. In the example, the Redis connection uses the Toxiproxy container's hostname and port instead of connecting directly to Redis.

    === "Connect Through Toxiproxy"
    csharp --8<-- "tests/Testcontainers.Toxiproxy.Tests/ToxiproxyContainerTest.cs:ConnectThroughToxiproxy"

  4. Apply toxics to the proxy to simulate network conditions. For example, a latency toxic to the downstream traffic.

    === "Toxic Configuration"
    csharp --8<-- "tests/Testcontainers.Toxiproxy.Tests/ToxiproxyContainerTest.cs:ToxicConfiguration"

!!! tip

text
Toxiproxy allows you to configure the `Toxicity` property, which determines the probability (0.0 to 1.0) that a toxic will be applied to the connection. A value of 1.0 means the toxic is applied 100% of the time, while 0.5 would apply it to approximately 50% of requests.

The test example uses the following NuGet dependencies:

=== "Package References"
xml --8<-- "tests/Testcontainers.Toxiproxy.Tests/Testcontainers.Toxiproxy.Tests.csproj:PackageReferences"

To execute the tests, use the command dotnet test from a terminal.

--8<-- "docs/modules/_call_out_test_projects.txt"


Modules Valkey (`docs/modules/valkey.md`)

Valkey

Valkey is an open-source, Redis-compatible in-memory data store.

The Testcontainers Redis module is compatible with the Valkey container image. You can use the existing Testcontainers.Redis package without requiring a separate Valkey module.

!!! note

text
If Valkey introduces features or configuration options that require dedicated support in the future, we can introduce a separate module at that time.

Usage

Add the Redis module:

shell title="nuget"
dotnet add package Testcontainers.Redis

Start a Valkey container using the Redis builder:

csharp
var valkeyContainer = new RedisBuilder("valkey/valkey:9.0-alpine").Build();
await valkeyContainer.StartAsync();

--8<-- "docs/modules/_call_out_test_projects.txt"


Test Frameworks Xunit Net (`docs/test_frameworks/xunit_net.md`)

Testing with xUnit.net

The Testcontainers.Xunit and Testcontainers.XunitV3 packages simplify writing tests with containers in xUnit.net. By leveraging xUnit.net's shared context, this package automates the setup and teardown of test resources, creating and disposing of containers as needed. This reduces repetitive code and avoids common patterns that developers would otherwise need to implement repeatedly.

To get started, add the following dependency to your project file for xUnit.net v3:

shell title="nuget"
dotnet add package Testcontainers.XunitV3

Or, if you're using xUnit.net v2:

shell title="nuget"
dotnet add package Testcontainers.Xunit

Creating an isolated test context

To create a new test resource instance for each test, inherit from the ContainerTest<TBuilderEntity, TContainerEntity> class. Each test resource instance is isolated and not shared across other tests, making this approach ideal for destructive operations that could interfere with other tests. You can access the generic TContainerEntity container instance through the Container property.

The example below demonstrates how to override the Configure(TBuilderEntity) method and pin the image version. This method allows you to configure the container instance specifically for your test case, with all container builder methods available. If your tests rely on a Testcontainers' module, the module's default configurations will be applied.

=== "Configure a Redis Container"
csharp --8<-- "tests/Testcontainers.Xunit.Tests/RedisContainerTest`1.cs:ConfigureRedisContainer"

!!! tip

text
Always pin the image version to avoid flakiness. This ensures consistency and prevents unexpected behavior, as the `latest` tag may pointing to a new version.

The base class also receives an instance of xUnit.net's ITestOutputHelper to capture and forward log messages to the running test.

Considering that xUnit.net runs tests in a deterministic natural sort order (like Test1, Test2, etc.), retrieving the Redis (string) value in the second test will always return null since a new test resource instance (Redis container) is created for each test.

=== "Run Tests"
csharp --8<-- "tests/Testcontainers.Xunit.Tests/RedisContainerTest`1.cs:RunTests"

If you check the output of docker ps, you will notice that three container instances in total are run, with two of them being Redis instances.

text title="list running containers"
PS C:\Sources\dotnet\testcontainers-dotnet> docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED
be115f3df138   redis:7.0                   "docker-entrypoint.s…"   3 seconds ago
59349127f8c0   redis:7.0                   "docker-entrypoint.s…"   4 seconds ago
45fa02b3e997   testcontainers/ryuk:0.14.0   "/bin/ryuk"             4 seconds ago

Creating a shared test context

Sometimes, creating and disposing of a test resource can be an expensive operation that you do not want to repeat for every test. By inheriting from the ContainerFixture<TBuilderEntity, TContainerEntity> class, you can share the test resource instance across all tests within the same test class, or even within the assembly, if you use xUnit.net v3 or a compatible v2 extension like the one from the AssemblyFixtureExample repository.

xUnit.net's fixture implementation does not rely on the ITestOutputHelper interface to capture and forward log messages; instead, it expects an implementation of IMessageSink. Make sure your fixture's default constructor accepts the interface implementation and forwards it to the base class.

=== "Configure a Redis Container"
csharp --8<-- "tests/Testcontainers.Xunit.Tests/RedisContainerTest`2.cs:ConfigureRedisContainer"

This ensures that the fixture is created only once for the entire test class, which also improves overall test performance. You must implement the IClassFixture<TFixture> interface with the previously created container fixture type in your test class and add the type as argument to the default constructor.

=== "Inject Redis Container"
csharp --8<-- "tests/Testcontainers.Xunit.Tests/RedisContainerTest`2.cs:InjectContainerFixture"

In this case, retrieving the Redis (string) value in the second test will no longer return null. Instead, it will return the value added in the first test.

=== "Run Tests"
csharp --8<-- "tests/Testcontainers.Xunit.Tests/RedisContainerTest`2.cs:RunTests"

The output of docker ps shows that, instead of two Redis containers, only one runs.

text title="list running containers"
PS C:\Sources\dotnet\testcontainers-dotnet> docker ps
CONTAINER ID   IMAGE                       COMMAND                  CREATED
d29a393816ce   redis:7.0                   "docker-entrypoint.s…"   3 seconds ago
e878f0b8f4bc   testcontainers/ryuk:0.14.0   "/bin/ryuk"             3 seconds ago

To use a single container fixture across all tests of a test-assembly (using xUnit.net v3 or an extension), add the [assembly: AssemblyFixture(typeof(TContainerFixtureEntity))] annotation referring to your subclass to the assembly. The fixture can then be injected into test class constructors (as shown above), or accessed using the TestContext.Current.GetFixture<TContainerFixtureEntity>() method (v3 only) in any test.

Testing ADO.NET services

In addition to the two mentioned base classes, the package contains two more classes: DbContainerTest and DbContainerFixture, which behave identically but offer additional convenient features when working with services accessible through an ADO.NET provider.

Inherit from either the DbContainerTest or DbContainerFixture class and override the Configure(TBuilderEntity) method to configure your database service.

In this example, we use the default configuration of the PostgreSQL module. The container image capabilities are used to instantiate the database, schema, and test data. During startup, the PostgreSQL container runs SQL scripts placed under the /docker-entrypoint-initdb.d/ directory automatically.

=== "Configure PostgreSQL Container"

csharp
--8<-- "tests/Testcontainers.Xunit.Tests/PostgreSqlContainer.cs:ConfigurePostgreSqlContainer"

Inheriting from the database container test or fixture class requires you to implement the abstract DbProviderFactory property and resolve a compatible DbProviderFactory according to your ADO.NET service.

=== "Configure DbProviderFactory"

csharp
--8<-- "tests/Testcontainers.Xunit.Tests/PostgreSqlContainer.cs:ConfigureDbProviderFactory"

!!! note

text
Depending on how you initialize and access the database, it may be necessary to override the `ConnectionString` property and replace the default database name with the one actual in use.

After configuring the dependent ADO.NET service, you can add the necessary tests. In this case, we run an SQL SELECT statement to retrieve the first record from the album table.

=== "Run Tests"

csharp
--8<-- "tests/Testcontainers.Xunit.Tests/PostgreSqlContainer.cs:RunTests"

--8<-- "docs/modules/_call_out_test_projects.txt"


--- METRICS ---