testcontainers/testcontainers-java
Testcontainers is a Java library that supports JUnit tests, providing lightweight, throwaway instances of common databases, Selenium web browsers, or anything else that can run in a Docker container.
Healthy across all four use cases
weakest axisPermissive 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 today
- ✓4 active contributors
- ✓MIT licensed
Show all 7 evidence items →Show less
- ✓CI configured
- ✓Tests present
- ⚠Small team — 4 contributors active in recent commits
- ⚠Concentrated ownership — top contributor handles 63% 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-java)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-java on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: testcontainers/testcontainers-java
Generated by RepoPilot · 2026-05-09 · 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-java 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 all four use cases
- Last commit today
- 4 active contributors
- MIT licensed
- CI configured
- Tests present
- ⚠ Small team — 4 contributors active in recent commits
- ⚠ Concentrated ownership — top contributor handles 63% 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-java
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/testcontainers/testcontainers-java.
What it runs against: a local clone of testcontainers/testcontainers-java — 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-java | Confirms the artifact applies here, not a fork |
| 2 | License is still MIT | Catches relicense before you depend on it |
| 3 | Default branch main exists | Catches branch renames |
| 4 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 5 | Last commit ≤ 30 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of testcontainers/testcontainers-java. If you don't
# have one yet, run these first:
#
# git clone https://github.com/testcontainers/testcontainers-java.git
# cd testcontainers-java
#
# 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-java and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "testcontainers/testcontainers-java(\\.git)?\\b" \\
&& ok "origin remote is testcontainers/testcontainers-java" \\
|| miss "origin remote is not testcontainers/testcontainers-java (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 main >/dev/null 2>&1 \\
&& ok "default branch main exists" \\
|| miss "default branch main no longer exists"
# 4. Critical files exist
test -f "core/src/main/java/org/testcontainers/DockerClientFactory.java" \\
&& ok "core/src/main/java/org/testcontainers/DockerClientFactory.java" \\
|| miss "missing critical file: core/src/main/java/org/testcontainers/DockerClientFactory.java"
test -f "core/src/main/java/org/testcontainers/containers/Container.java" \\
&& ok "core/src/main/java/org/testcontainers/containers/Container.java" \\
|| miss "missing critical file: core/src/main/java/org/testcontainers/containers/Container.java"
test -f "core/src/main/java/org/testcontainers/containers/GenericContainer.java" \\
&& ok "core/src/main/java/org/testcontainers/containers/GenericContainer.java" \\
|| miss "missing critical file: core/src/main/java/org/testcontainers/containers/GenericContainer.java"
test -f "core/src/main/java/org/testcontainers/Testcontainers.java" \\
&& ok "core/src/main/java/org/testcontainers/Testcontainers.java" \\
|| miss "missing critical file: core/src/main/java/org/testcontainers/Testcontainers.java"
test -f "build.gradle" \\
&& ok "build.gradle" \\
|| miss "missing critical file: build.gradle"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 30 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~0d)"
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-java"
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 is a Java library that provisions lightweight, ephemeral Docker containers for integration testing—databases, message brokers, Selenium browsers, or custom services. It eliminates the need to mock external dependencies in tests by spinning up real, isolated instances that are automatically cleaned up after each test, integrating seamlessly with JUnit via simple annotations and fluent APIs. Gradle monorepo: root coordinates multiple subprojects via settings.gradle. Core testcontainers module in testcontainers/ provides base GenericContainer, DatabaseContainer abstractions. Specialized modules (testcontainers-postgresql/, testcontainers-mysql/, etc.) extend core for specific services. .github/workflows/ orchestrates CI; annotations/ contains Google's @Nullable/@NotNull shims for nullability annotations.
👥Who it's for
Java developers writing integration tests who need real database instances (PostgreSQL, MySQL, MongoDB, etc.) or services (Redis, Kafka, Vault) without maintaining test infrastructure. Contributors are primarily maintainers of the testcontainers-java organization and community contributors adding new container modules.
🌱Maturity & risk
Highly mature and production-ready. The project has 2.3MB of Java code, comprehensive CI/CD pipelines (.github/workflows/ci.yml, CircleCI), active dependabot integration, and organized issue templates. Multiple container modules (databases, message queues, web drivers) indicate broad adoption. Active development evidenced by release-drafter.yml and update workflows suggesting regular releases.
Standard open source risks apply.
Active areas of work
Active maintenance visible via workflows: release-drafter.yml preparing releases, update-gradle-wrapper.yml keeping build tooling current, moby-latest.yml testing against latest Docker versions, CI running on multiple platforms (ci.yml, ci-windows.yml, ci-rootless.yml, ci-docker-wormhole.yml). Dependabot actively managing dependencies (.github/dependabot.yml).
🚀Get running
git clone https://github.com/testcontainers/testcontainers-java.git && cd testcontainers-java && ./gradlew build. Requires Docker daemon running locally. Use .sdkmanrc (SDK Manager config) to ensure correct Java version; dev environment provided via .devcontainer/devcontainer.json for GitHub Codespaces.
Daily commands: Run full test suite: ./gradlew build. Run specific module: ./gradlew :testcontainers-postgresql:test. Run with Gradle build cache enabled: ./gradlew build --build-cache. Tests require Docker daemon; CI validates against rootless Docker (ci-rootless.yml) and standard Docker (ci.yml).
🗺️Map of the codebase
core/src/main/java/org/testcontainers/DockerClientFactory.java— Factory for creating and managing Docker client instances; critical entry point for all container operations and Docker connectivity.core/src/main/java/org/testcontainers/containers/Container.java— Core container abstraction interface that defines lifecycle and execution contracts all containers must implement.core/src/main/java/org/testcontainers/containers/GenericContainer.java— Primary generic container implementation providing common Docker container functionality used by all module-specific containers.core/src/main/java/org/testcontainers/Testcontainers.java— Public API entry point and facade exposing core Testcontainers functionality to test authors.build.gradle— Root Gradle build configuration defining multi-module structure, shared dependencies, and publishing configuration.bom/build.gradle— Bill of Materials (BOM) configuration ensuring consistent versioning across all Testcontainers modules.
🛠️How to make changes
Add a new container module for a database/service
- Create new module directory (e.g., 'mysql', 'postgresql') following naming convention matching parent folder structure. (
{moduleName}/build.gradle) - Extend GenericContainer or Container base class in main source tree implementing service-specific initialization and wait logic. (
{moduleName}/src/main/java/org/testcontainers/{moduleName}/{ServiceName}Container.java) - Implement WaitStrategy subclass for service-specific readiness checks (e.g., port availability, health endpoint). (
{moduleName}/src/main/java/org/testcontainers/{moduleName}/wait/{ServiceName}WaitStrategy.java) - Add module to root build.gradle as subproject and update bom/build.gradle to include new module in dependency management. (
build.gradle) - Create integration tests in src/test demonstrating container startup and basic operations. (
{moduleName}/src/test/java/org/testcontainers/{moduleName}/{ServiceName}ContainerTest.java)
Add a new wait strategy for container readiness
- Create new class extending WaitStrategy interface in appropriate module's wait package. (
core/src/main/java/org/testcontainers/containers/wait/{StrategyName}WaitStrategy.java) - Implement waitUntilReady(WaitStrategyTarget target) method with polling/checking logic. (
core/src/main/java/org/testcontainers/containers/wait/{StrategyName}WaitStrategy.java) - Add builder method to GenericContainer to enable fluent API for applying the new strategy. (
core/src/main/java/org/testcontainers/containers/GenericContainer.java) - Create unit tests verifying strategy behavior under success/timeout scenarios. (
core/src/test/java/org/testcontainers/containers/wait/{StrategyName}WaitStrategyTest.java)
Add support for a new container capability (volume mount, environment, port binding)
- Add builder method to GenericContainer or container-specific subclass following fluent builder pattern. (
core/src/main/java/org/testcontainers/containers/GenericContainer.java) - Store configuration in container state; integrate with Docker API client in DockerClientFactory or container's start logic. (
core/src/main/java/org/testcontainers/DockerClientFactory.java) - Add integration test demonstrating the new capability working end-to-end with real Docker. (
core/src/test/java/org/testcontainers/containers/GenericContainerTest.java)
🔧Why these technologies
- Docker API (Java Docker client library) — Core dependency for programmatic Docker container lifecycle management; enables cross-platform container orchestration from JVM tests.
- JUnit integration hooks (TestWatcher, Lifecycle) — Seamless integration with JUnit test lifecycle ensures containers are properly initialized before tests and cleaned up after, reducing boilerplate.
- Gradle multi-module build system — Modular architecture allows independent container modules (MySQL, PostgreSQL, MongoDB, etc.) with shared core without monolithic dependencies.
- Docker Compose support via containerized docker-compose — Enables multi-container orchestration in tests without requiring docker-compose installation on host, improving portability.
⚖️Trade-offs already made
-
Require Docker to be pre-installed on test machine
- Why: Avoids complexity of embedding/provisioning Docker; assumes standard CI/CD infrastructure.
- Consequence: Tests cannot run in completely isolated environments; requires Docker daemon access, limiting some cloud sandbox scenarios.
-
Generic container implementation before specialized modules
- Why: Allows any Docker image to work immediately while module authors build specialized abstractions.
- Consequence: Developers must implement wait strategies and health checks manually for unsupported databases; higher friction for first-time use.
-
Compose container wraps docker-compose CLI rather than Docker API
- Why: Matches user mental models of docker-compose files; simpler to support arbitrary compose features.
- Consequence: Additional shell execution overhead; less granular control over individual service lifecycle compared to native Docker API.
-
No built-in container pooling/reuse across tests
- Why: Simplifies test isolation guarantees; each test gets fresh containers.
- Consequence: Higher startup overhead in large test suites; trade-off is test reliability and simplicity over performance.
🚫Non-goals (don't propose these)
- Kubernetes orchestration (focuses on Docker; K8s has separate testcontainers implementations)
- Container image building/image registry management
- Production deployment or container management (test-scoped only)
- Cross-process communication between host and container beyond port/volume binding
- Windows native containers (targets Docker for Linux primarily; limited Windows support)
🪤Traps & gotchas
Docker daemon must be accessible (DOCKER_HOST environment variable or Unix socket at /var/run/docker.sock); tests fail silently if Docker is unavailable. Some modules require specific Docker image versions (e.g., PostgreSQL container pulls 'postgres:latest' by default, which may differ from expected version). Windows CI is separate workflow (ci-windows.yml) because Docker behavior differs. Rootless Docker tests (ci-rootless.yml) reveal platform-specific socket permission issues. Container lifecycle spans entire test class; resource exhaustion possible if many containers start simultaneously.
🏗️Architecture
💡Concepts to learn
- Container lifecycle management — Understanding start, wait-for-readiness, and teardown patterns is essential for reliable test isolation; Testcontainers abstracts Docker lifecycle complexity into testable methods
- Wait strategies — Tests fail unpredictably if containers aren't truly ready (e.g., database accepting connections); Testcontainers provides composable wait strategies (LogMessageWaitStrategy, HttpWaitStrategy) to solve race conditions
- Networking and port mapping — Containers run in isolated networks; understanding how Testcontainers maps random host ports to container ports is critical for connecting tests to services
- Resource reuse and shared containers — Spinning up a database per test is slow; Testcontainers supports @Container (JUnit 5) with static/instance scopes to share containers across tests, trading isolation for performance
- TestcontainersCloud and remote Docker — Not all CI environments run Docker locally; Testcontainers supports remote Docker daemons and cloud providers (Testcontainers Cloud), visible in ci-docker-wormhole.yml and workflows
- Module plugins and auto-configuration — Testcontainers discovers container modules at runtime via classpath; understanding the plugin architecture (ServiceLoader pattern in Java) explains how adding a dependency auto-enables features
🔗Related repos
testcontainers/testcontainers-go— Official Testcontainers port for Go; same library, ecosystem-specific implementation for developers in other languagestestcontainers/testcontainers-python— Official Testcontainers port for Python; parallel project maintaining feature parity across ecosystemsmoby/moby— The Docker daemon (Moby) that Testcontainers orchestrates; understanding Docker's Go API helps debug container lifecycle issuesjunit-team/junit5— JUnit Jupiter framework that Testcontainers integrates with via @Testcontainers annotation; core test execution enginedocker-java/docker-java— Java Docker API client library that testcontainers-java likely uses for container provisioning and lifecycle management
🪄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 Windows-specific integration tests for Docker socket handling
The repo has CI workflows for Windows (ci-windows.yml, ci-windows-trigger.yml) and Docker Wormhole support (ci-docker-wormhole.yml), but there's no dedicated test suite validating Windows-specific Docker connectivity edge cases. Given the complexity of Docker on Windows (WSL2, Hyper-V, named pipes), a new test module would prevent regressions and improve contributor confidence when modifying Docker client initialization code.
- [ ] Create new test module: modules/docker/src/test/java/org/testcontainers/dockerclient/WindowsDockerClientTest.java
- [ ] Test Docker socket detection on Windows with WSL2 backend
- [ ] Test fallback behavior when Docker daemon is unreachable
- [ ] Add test execution to ci-windows.yml workflow to catch platform-specific failures early
- [ ] Document Windows-specific test setup in CONTRIBUTING.md
Create GitHub Action for automated dependency scanning and BOM validation
The BOM (Bill of Materials) in bom/build.gradle dynamically includes all subprojects. Currently, there's no CI check validating that the BOM stays in sync with published artifacts or that transitive dependencies don't create conflicts. A new workflow would catch dependency version mismatches before release and validate BOM correctness.
- [ ] Create .github/workflows/validate-bom.yml workflow
- [ ] Add Gradle task in buildSrc/src/main/groovy/org/testcontainers/build to validate BOM against all subproject versions
- [ ] Include dependency conflict detection using gradle-dependency-check or similar tool
- [ ] Run workflow on PRs modifying build.gradle or bom/build.gradle files
- [ ] Document BOM validation process in RELEASING.md
Add comprehensive test suite for custom Gradle build plugins in buildSrc
The buildSrc directory contains custom Gradle plugins (ComparePOMWithLatestReleasedTask.groovy, DelombokArgumentProvider.groovy) that are critical to the build process, but have no unit tests. These plugins are error-prone when modified and lack test coverage, risking build failures for all contributors.
- [ ] Create buildSrc/src/test/groovy directory structure
- [ ] Add ComparePOMWithLatestReleasedTaskTest.groovy testing version comparison logic
- [ ] Add DelombokArgumentProviderTest.groovy testing annotation processor argument generation
- [ ] Mock external gradle project artifacts and dependencies in tests
- [ ] Run buildSrc tests in ci.yml workflow before main build step
🌿Good first issues
- Add integration tests for testcontainers-vault module interactions with HashiCorp Vault APIs in src/test/java/ (module exists per RELEASING.md mention but test coverage visibility unclear from file structure)
- Implement structured logging in GenericContainer lifecycle methods (start, stop, waitingFor) to help users debug container startup failures—currently may lack diagnostic output
- Create Docker Compose integration support (testcontainers-docker-compose module) to allow .yml-based multi-container setups, bridging gap between single-container GenericContainer and complex test environments
⭐Top contributors
Click to expand
Top contributors
- @eddumelendez — 63 commits
- @dependabot[bot] — 34 commits
- @github-actions[bot] — 2 commits
- @haider2122 — 1 commits
📝Recent commits
Click to expand
Recent commits
3878311— Remove unused gradle plugin (eddumelendez)6299b4f— Polish neo4j-container build file (eddumelendez)adbea9d— Remove duplicated org.junit.platform:junit-platform-launcher dependency (eddumelendez)afd28bb— [create-pull-request] automated change (#11721) (github-actions[bot])09752a8— [create-pull-request] automated change (#11720) (github-actions[bot])5c44820— Fix typo (#11717) (haider2122)84b1c3a— Add getHttpPort and getGrpcPort methods in WeaviateContainer (#11712) (eddumelendez)a412b8e— Use weaviate client v6 (#11711) (eddumelendez)058fa82— Combined dependencies PR (#11710) (eddumelendez)773f344— Merge remote-tracking branch 'origin/dependabot/gradle/modules/hivemq/ch.qos.logback-logback-classic-1.5.32' into combin (eddumelendez)
🔒Security observations
The Testcontainers codebase demonstrates generally good security practices with proper use of build tools, no hardcoded secrets detected in the provided files, and appropriate CI/CD configuration. The main security concerns are related to the development docker-compose configuration which exposes unnecessary ports and uses generic base image tags. The codebase itself (Java/Gradle-based) follows standard practices for dependency management through BOM files. The primary recommendations focus on tightening the development environment configuration and ensuring specific, patched container image versions are used. No critical vulnerabilities or injection risks were identified in the analyzed file structures.
- Medium · Exposed Documentation Service Port —
docker-compose.yml - docs service port binding. The docker-compose.yml exposes port 8000 for the MkDocs documentation service (0.0.0.0:8000). This makes the development documentation service accessible from any network interface, which could expose internal documentation and development details to unauthorized parties if this configuration is used in non-local environments. Fix: Restrict the port binding to localhost only by changing '0.0.0.0:8000' to '127.0.0.1:8000', or ensure this configuration is only used in isolated development environments. For production, use a proper reverse proxy with authentication. - Low · Python Base Image Without Specific Version Tag —
docker-compose.yml - docs service image. The docker-compose.yml uses 'python:3.8' without a specific minor version tag. This could lead to inconsistent builds as the image tag '3.8' may point to different patch versions over time, potentially including versions with known vulnerabilities. Fix: Use a specific version tag like 'python:3.8.18-slim' or specify a more recent Python version with security patches. Consider using Alpine-based images (python:3.x-alpine) for reduced attack surface. - Low · Missing Security Headers in Documentation Build —
docker-compose.yml - docs service command. The MkDocs serve command is exposed without any security middleware (e.g., rate limiting, authentication, security headers). While this is a development server, unauthorized access could be exploited to gather information about the project. Fix: Add authentication to the development documentation server or restrict network access. For development purposes, ensure the service is only accessible within trusted networks. Consider adding a reverse proxy with security headers.
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.