Module
Core
Testcontainers version
2.0.4
Using the latest Testcontainers version?
Yes
What happened?
GenericContainer.start() guards against double-start with if (containerId != null) return, but start() is not synchronized. When two threads call start() on the same container concurrently, both can pass the guard before either sets containerId, creating two Docker containers for one logical dependency.
This can happen when @Testcontainers is used and the container is also started from another context. For example, custom test infrastructure that handles @Container annotations alongside the JUnit extension:
@Testcontainers
class MyTest {
// Custom infrastructure starts this container during context setup.
// The @Testcontainers extension also starts it via Startables.deepStart().
// Both run concurrently - the second start() should be a no-op, but without
// synchronization both threads pass the containerId == null check.
@Container
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
}
The workaround is to not annotate dependencies with @Container when custom infrastructure already handles them, but this is not obvious to developers and error-prone.
I can't provide the exact scenario because we faced this in a closed-source project. The example above illustrates the general pattern.
Additional Information
I submitted a PR with a fix: #11702
Module
Core
Testcontainers version
2.0.4
Using the latest Testcontainers version?
Yes
What happened?
GenericContainer.start()guards against double-start withif (containerId != null) return, butstart()is not synchronized. When two threads callstart()on the same container concurrently, both can pass the guard before either setscontainerId, creating two Docker containers for one logical dependency.This can happen when
@Testcontainersis used and the container is also started from another context. For example, custom test infrastructure that handles@Containerannotations alongside the JUnit extension:The workaround is to not annotate dependencies with
@Containerwhen custom infrastructure already handles them, but this is not obvious to developers and error-prone.I can't provide the exact scenario because we faced this in a closed-source project. The example above illustrates the general pattern.
Additional Information
I submitted a PR with a fix: #11702