testcontainers/testcontainers-dotnet
A library to support tests with throwaway instances of Docker containers for all compatible .NET Standard versions.
Healthy across the board
Permissive license, no critical CVEs, actively maintained — safe to depend on.
Has a license, tests, and CI — clean foundation to fork and modify.
Documented and popular — useful reference codebase to read through.
No critical CVEs, sane security posture — runnable as-is.
- ✓Last commit 1d ago
- ✓25+ active contributors
- ✓MIT licensed
Show 3 more →Show less
- ✓CI configured
- ✓Tests present
- ⚠Concentrated ownership — top contributor handles 67% of recent commits
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Healthy" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/testcontainers/testcontainers-dotnet)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/testcontainers/testcontainers-dotnet on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: testcontainers/testcontainers-dotnet
Generated by RepoPilot · 2026-05-10 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/testcontainers/testcontainers-dotnet shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
GO — Healthy across the board
- Last commit 1d ago
- 25+ active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Concentrated ownership — top contributor handles 67% of recent commits
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live testcontainers/testcontainers-dotnet
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/testcontainers/testcontainers-dotnet.
What it runs against: a local clone of testcontainers/testcontainers-dotnet — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in testcontainers/testcontainers-dotnet | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch develop exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 31 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of testcontainers/testcontainers-dotnet. If you don't
# have one yet, run these first:
#
# git clone https://github.com/testcontainers/testcontainers-dotnet.git
# cd testcontainers-dotnet
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of testcontainers/testcontainers-dotnet and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "testcontainers/testcontainers-dotnet(\\.git)?\\b" \\
&& ok "origin remote is testcontainers/testcontainers-dotnet" \\
|| miss "origin remote is not testcontainers/testcontainers-dotnet (artifact may be from a fork)"
# 2. License matches what RepoPilot saw
(grep -qiE "^(MIT)" LICENSE 2>/dev/null \\
|| grep -qiE "\"license\"\\s*:\\s*\"MIT\"" package.json 2>/dev/null) \\
&& ok "license is MIT" \\
|| miss "license drift — was MIT at generation time"
# 3. Default branch
git rev-parse --verify develop >/dev/null 2>&1 \\
&& ok "default branch develop exists" \\
|| miss "default branch develop no longer exists"
# 4. Critical files exist
test -f "Testcontainers.sln" \\
&& ok "Testcontainers.sln" \\
|| miss "missing critical file: Testcontainers.sln"
test -f "Directory.Build.props" \\
&& ok "Directory.Build.props" \\
|| miss "missing critical file: Directory.Build.props"
test -f "Directory.Packages.props" \\
&& ok "Directory.Packages.props" \\
|| miss "missing critical file: Directory.Packages.props"
test -f "build/Program.cs" \\
&& ok "build/Program.cs" \\
|| miss "missing critical file: build/Program.cs"
test -f ".github/workflows/cicd.yml" \\
&& ok ".github/workflows/cicd.yml" \\
|| miss "missing critical file: .github/workflows/cicd.yml"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 31 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~1d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/testcontainers/testcontainers-dotnet"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
Testcontainers for .NET is a library that provides lightweight Docker container management for .NET integration tests, allowing developers to spin up ephemeral database, cache, and service containers during test execution. It wraps the Docker Remote API and supports all .NET Standard versions, eliminating the need for manual Docker orchestration in test suites. Monolithic .NET solution (Testcontainers.sln with Testcontainers.slnx) organized by container type: core abstractions in the root src/, specialized modules for databases/services in separate projects. Build automation lives in build/ (Build.csproj + Program.cs), and documentation is in docs/ generated via MkDocs (mkdocs.yml integration visible in dependencies).
👥Who it's for
.NET developers and QA engineers writing integration tests who need isolated, disposable Docker containers (PostgreSQL, MySQL, Redis, Kafka, etc.) to run alongside their test code without manual container lifecycle management or external Docker Compose files.
🌱Maturity & risk
Production-ready and actively maintained. The project has NuGet distribution, comprehensive CI/CD via GitHub Actions (cicd.yml, codeql-analysis.yml), SonarCloud integration for code quality, and evidence of active development with versioning tracked in CHANGELOG.md and release-drafter.yml. The codebase is substantial (1.7M lines of C#) with structured build pipeline (build/ directory with Build.csproj).
Low risk for a mature OSS library. The project has strong governance (CODEOWNERS, security policy in SECURITY.md), automated testing, and dependency management (Directory.Packages.props for centralized versioning via Dependabot). Primary risk is Docker daemon availability—tests require a running Docker Engine or Testcontainers Cloud, which could fail silently in CI environments without proper setup.
Active areas of work
Active development cycle with automated release drafting (release-drafter.yml), dependency updates via Dependabot (.github/dependabot.yml), and multi-platform CI testing. Test reporting workflow (test-report.yml) and code quality scanning (CodeQL, Scorecard) are in place, indicating ongoing maintenance and security hardening.
🚀Get running
Clone and build: git clone https://github.com/testcontainers/testcontainers-dotnet.git && cd testcontainers-dotnet && ./build.sh (Linux/Mac) or build.ps1 (Windows). The build system is PowerShell/Shell-based (build.sh, build.ps1 in root). Requires Docker daemon running and .NET SDK matching version constraints in Directory.Build.props.
Daily commands:
For development: ./build.sh (or build.ps1 on Windows) triggers the build system defined in build/Program.cs. Tests are likely run via dotnet test against projects in the solution. Dev container available (.devcontainer/devcontainer.json) for GitHub Codespaces with pre-configured Docker.
🗺️Map of the codebase
Testcontainers.sln— Root solution file defining all projects and their dependencies—every contributor must understand the project structure.Directory.Build.props— Centralized MSBuild properties controlling build configuration, package metadata, and versioning across all projects.Directory.Packages.props— Centralized NuGet dependency management ensuring all projects reference consistent package versions.build/Program.cs— Nuke build orchestration entry point defining all CI/CD tasks, versioning, and release workflows..github/workflows/cicd.yml— Primary GitHub Actions workflow triggering builds, tests, and deployments—critical for understanding test execution and release process.README.md— Project overview and getting-started guide that frames the library's purpose, capabilities, and primary use cases.
🧩Components & responsibilities
- ContainerBuilder<T> — Fluent API for configuring container images, environment variables, port bindings, and wait strategies; accumulates configuration before Build().
🛠️How to make changes
Add a new service module (e.g., for a new database or cache)
- Create a new project in src/Testcontainers.{ServiceName} mirroring existing module structure (e.g., Testcontainers.Redis). (
Testcontainers.sln) - Define a container builder class inheriting from ContainerBuilder<T> with fluent configuration methods. (
src/Testcontainers/{ServiceName}/{ServiceName}Container.cs) - Implement wait strategies and health checks specific to the service. (
src/Testcontainers/{ServiceName}/Wait/WaitStrategy.cs) - Add connection string provider to generate dynamic URIs from container port mappings. (
src/Testcontainers/{ServiceName}/ConnectionString.cs) - Create integration tests in test/Testcontainers.{ServiceName}.Tests validating container startup and connectivity. (
test/Testcontainers.{ServiceName}.Tests/{ServiceName}ContainerTests.cs) - Document the module with configuration examples and supported environment variables. (
docs/modules/{servicename}.md)
Add a new wait strategy for custom container readiness logic
- Understand existing wait strategy patterns by reviewing docs/api/wait_strategies.md and core implementations. (
docs/api/wait_strategies.md) - Create a new class implementing IWaitForContainerOS or inheriting from WaitStrategy base class. (
src/Testcontainers/Containers/WaitStrategy/{StrategyName}.cs) - Implement the UntilAsync method to poll the container until the readiness condition is met. (
src/Testcontainers/Containers/WaitStrategy/{StrategyName}.cs) - Add unit and integration tests verifying the wait strategy detects readiness and handles timeouts. (
test/Testcontainers.Tests/Containers/WaitStrategy/{StrategyName}Tests.cs)
Modify build process or add new CI/CD task
- Review the Nuke build structure and existing tasks in build/Program.cs and build/Tasks.cs. (
build/Program.cs) - Add or modify a task method in build/Tasks.cs following Nuke conventions with Target attributes. (
build/Tasks.cs) - Update build/Program.cs to wire the task into the dependency graph or workflow trigger. (
build/Program.cs) - If adding a GitHub Actions workflow, define it in .github/workflows/{workflow-name}.yml following existing patterns in cicd.yml. (
.github/workflows/cicd.yml)
🔧Why these technologies
- Docker Remote API via Docker.DotNet SDK — Enables programmatic container lifecycle management (create, start, stop, remove) across Windows, macOS, and Linux without shell scripting.
- async/await with .NET Standard — Allows non-blocking I/O during container startup waits and health checks; compatible with all .NET versions.
- Nuke build system — Type-safe, IDE-friendly build orchestration replacing shell scripts; integrates easily with CI/CD and local development.
- GitHub Actions + Dependabot — Automates testing, versioning, NuGet publishing, and dependency updates without external CI infrastructure.
- MkDocs with Material theme — Generates searchable, modern documentation from Markdown with minimal overhead.
⚖️Trade-offs already made
-
Ship pre-built modules (PostgreSQL, Redis, MongoDB, etc.) instead of auto-discovery
- Why: Provides type-safe, fluent APIs and sensible defaults optimized per service; reduces boilerplate for common scenarios.
- Consequence: Adding support for a new service requires upfront module implementation; cannot auto-configure arbitrary Docker images.
-
Pluggable wait strategies over a single built-in health check
- Why: Different services have different readiness signals (TCP ports, HTTP endpoints, log lines); flexibility accommodates heterogeneous deployments.
- Consequence: Developers must select or implement appropriate wait strategies; no one-size-fits-all default.
-
Resource reaper runs as a background JVM container instead of explicit cleanup in tests
- Why: Ensures containers are cleaned up even if tests crash or throw uncaught exceptions; decouples resource lifecycle from test execution.
- Consequence: Adds overhead and complexity; extra Docker container; harder to debug orphaned containers during development.
-
Centralized dependency management via Directory.Packages.props
- Why: Prevents version conflicts and ensures all projects reference consistent transitive dependencies.
- Consequence: Requires discipline to keep versions in sync; any version bump affects the entire solution.
🚫Non-goals (don't propose these)
- Does not provide Kubernetes orchestration; focused exclusively on Docker containers.
- Does not handle authentication or networking setup beyond port mapping; assumes Docker daemon is accessible and properly configured.
- Does not replace Docker Compose for complex multi-container scenarios; optimized for test isolation, not production deployments.
- Does not provide container image building; assumes pre-built images exist in registries.
🪤Traps & gotchas
Docker daemon must be running (DOCKER_HOST env var can override default socket location on Linux). Tests may hang or timeout if Docker is unavailable—no graceful degradation. Some modules may require specific Docker versions or features (e.g., Docker Compose v2 syntax in compose.yml). PowerShell execution policy may block build.ps1 on Windows (run Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope CurrentUser or use build.sh via WSL). .editorconfig enforces specific C# style rules; merges may fail if your IDE settings differ.
🏗️Architecture
💡Concepts to learn
- Docker Remote API — testcontainers-dotnet communicates with the Docker daemon via HTTP API (not Docker CLI), enabling precise container lifecycle control and programmatic inspection
- IAsyncDisposable Pattern — Core containers implement IAsyncDisposable for async cleanup (stopping/removing containers), critical for proper resource management in async test flows
- Fluent Builder API — testcontainers-dotnet exposes container configuration via method chaining (e.g.,
.WithExposedPort(5432).WithEnvironment()) for readable, chainable test setup - Port Mapping & Container Networking — Tests must map ephemeral host ports to container ports and resolve container DNS names; testcontainers abstracts this complexity for local and cloud Docker environments
- Connection String Provider Pattern — Database containers expose connection strings dynamically based on assigned port/IP; connection string providers abstract database-specific URI formatting (JDBC, ADO.NET, etc.)
- Docker Compose Orchestration — testcontainers can parse and launch multi-container setups from docker-compose.yml files (compose.yml present in repo), enabling complex integration test topologies
- .NET Standard Multi-targeting — Library targets multiple .NET Standard versions (net6.0, net7.0, net8.0, etc.) to maximize compatibility; Directory.Build.props and CI matrix testing ensure cross-version correctness
🔗Related repos
testcontainers/testcontainers-java— Original Testcontainers implementation for Java—defines the API contract and patterns that the .NET port emulatestestcontainers/testcontainers-go— Go language binding for Testcontainers, demonstrates cross-language ecosystem consistency and shared container module patternstestcontainers/testcontainers-python— Python implementation sharing the same core design philosophy and container definitions as testcontainers-dotnetdotnet/testcontainers-dotnet-examples— Companion repository with runnable examples for each container type, directly referenced in onboarding and user documentationmoby/moby— Docker daemon and Remote API implementation—the underlying engine that testcontainers-dotnet orchestrates
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add comprehensive documentation for wait strategies with real-world examples
The docs/api/wait_strategies.md file exists but likely lacks detailed examples for different container types (databases, web services, etc.). This is a high-value addition since wait strategies are critical for test reliability and developers frequently struggle with timeout configurations. Adding specific examples for PostgreSQL, MySQL, Redis, and HTTP-based containers would significantly improve adoption.
- [ ] Review current docs/api/wait_strategies.md for gaps
- [ ] Add practical examples for at least 5 common container types
- [ ] Include troubleshooting section for common wait strategy timeout issues
- [ ] Add performance comparison between different wait strategy approaches
- [ ] Link from docs/api/best_practices.md to wait strategy patterns
Expand connection string provider documentation with provider-specific guides
The docs/api/connection_string_provider.md exists but the file structure suggests incomplete coverage. Testcontainers users need clear guidance on extracting and using connection strings across different database and service modules. Adding module-specific guides (e.g., PostgreSQL, SQL Server, MongoDB, RabbitMQ) with code examples would be high-impact for reducing support issues.
- [ ] Review current docs/api/connection_string_provider.md
- [ ] Create docs/api/modules/*.md files for at least 5 popular container types
- [ ] Include before/after code examples showing connection string usage
- [ ] Document environment variable patterns for different services
- [ ] Add validation examples for verifying correct connection strings
Add Docker-in-Docker (DinD) troubleshooting documentation and test cases
The docs/dind/index.md file exists but likely lacks comprehensive troubleshooting guidance. Based on the compose.yml suggesting multi-container setup and the devcontainer.json, this repo supports DinD scenarios. New contributors could expand this with common failure modes, networking issues, volume mounting problems, and add integration tests validating DinD scenarios work correctly in CI/CD.
- [ ] Review docs/dind/index.md for completeness
- [ ] Document common DinD errors (socket permissions, image pull timeouts, network isolation)
- [ ] Add step-by-step debugging guide for DinD setup issues
- [ ] Create integration test in appropriate test project validating DinD scenarios
- [ ] Document socket mounting best practices for different platforms (Windows/Linux/Mac)
🌿Good first issues
- Add missing documentation examples in docs/api/ for less common container types—check which modules exist in src/ but lack corresponding docs/*.md files. Start with docs/api/best_practices.md and mirror its structure for a new module.
- Write integration tests for edge cases in Docker network creation (docs/api/create_docker_network.md mentions this feature but test coverage is sparse). Add tests to existing test projects verifying network isolation and DNS resolution between containers.
- Implement connection string provider support for a database not yet covered—review existing modules in src/, pick one with minimal implementation, and add connection string builders for additional database variants (MariaDB, CockroachDB, etc.).
⭐Top contributors
Click to expand
Top contributors
- @HofmeisterAn — 67 commits
- @0xced — 4 commits
- @digital88 — 3 commits
- @dependabot[bot] — 2 commits
- @NelsonBN — 2 commits
📝Recent commits
Click to expand
Recent commits
16cabf1— feat: Ignore port-forwarding extra host in reuse hash (#1689) (HofmeisterAn)444958d— feat: Allow devs to override the reuse hash calculation (#1688) (HofmeisterAn)c9db4a9— chore(deps): Bump the actions group with 5 updates (#1687) (dependabot[bot])0b7530b— chore(deps): Bump Docker.DotNet from 4.1.0 to 4.2.0 (#1686) (HofmeisterAn)490b673— docs: Extend WithCommand(params string[]) documentation (#1685) (HofmeisterAn)e72bacc— fix: Trim tar record padding to avoid broken-pipe failure on Podman (#1684) (artiomchi)fd87bb8— fix(Nats): Use healthz API for readiness probe (#1679) (eriblo01)a2f5717— chore(deps): Bump the actions group with 5 updates (#1676) (dependabot[bot])470e975— chore(deps): Bump Docker.DotNet from 4.0.2 to 4.1.0 (#1674) (HofmeisterAn)3d42f9b— fix(servicebus): Replace amqp protocol by sb for connection string (#1675) (Greybird)
🔒Security observations
The testcontainers-dotnet repository demonstrates a reasonably secure posture with proper vulnerability reporting channels (GitHub private vulnerability reporting in SECURITY.md), active CI/CD pipelines with CodeQL analysis, and use of dependency management tools (Dependabot). However, there are minor concerns: Python dependencies in the documentation toolchain should be regularly audited, build-time credential handling (Credentials.cs) requires verification to ensure no secrets are hardcoded, and PowerShell scripts in CI/CD should be reviewed for injection vulnerabilities. Overall, the project follows security best practices with no critical vulnerabilities detected from static analysis of the visible file structure.
- Medium · Outdated MkDocs Dependencies —
Dependencies/Package file (mkdocs, mkdocs-material, mkdocs-markdownextradata-plugin). The dependencies file specifies mkdocs==1.6.1, mkdocs-markdownextradata-plugin==0.2.6, and mkdocs-material==9.7.2. These pinned versions may contain known vulnerabilities. Regular dependency updates are recommended. Fix: Regularly audit and update dependencies to their latest stable versions. Use tools like pip-audit or safety to check for known vulnerabilities in Python dependencies. - Low · Build Script Credentials File Present —
build/Credentials.cs. The build directory contains a 'Credentials.cs' file, which suggests credential handling in the build process. While not necessarily a vulnerability, this requires careful review to ensure no hardcoded secrets are committed. Fix: Ensure Credentials.cs does not contain hardcoded secrets. Use environment variables, secure vaults, or CI/CD secrets management instead. Add this file to .gitignore if it contains local secrets. - Low · PowerShell Scripts in CI/CD Pipeline —
.github/scripts/Collect-TestProjects.ps1, .github/scripts/Filter-TestProjects.ps1. PowerShell scripts are used in the GitHub Actions workflow (.github/scripts/). PowerShell scripts can be vulnerable to injection if they process untrusted input without proper validation. Fix: Review PowerShell scripts for input validation, avoid dynamic code execution, and use parameterized approaches. Ensure scripts follow security best practices and are regularly audited. - Low · Dependabot Configuration Present —
.github/dependabot.yml. While Dependabot configuration (.github/dependabot.yml) is present (which is good), ensure it is properly configured to check all package ecosystems used in the project, including NuGet packages for the .NET codebase. Fix: Verify that Dependabot is configured to monitor NuGet packages (packages defined in Directory.Packages.props and project files). Ensure automated dependency updates are enabled and reviewed regularly.
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.