@ServiceConnection on a PostgreSQLContainer produces JdbcConnectionDetails but not R2dbcConnectionDetails
Spring Boot version: 4.0.0
Testcontainers version: reproduced on both 2.0.4 and 1.21.2
Java: 25 · Kotlin: 2.4.10 · Build: Gradle, io.spring.dependency-management
Docker: 28.3.2 (macOS, aarch64)
Summary
A PostgreSQLContainer @Bean annotated @ServiceConnection in a @TestConfiguration supplies connection
details to Flyway (JDBC) but not to R2DBC. Flyway connects to the container and migrates successfully, proving
JdbcConnectionDetails is published. In the same context, the application's ConnectionFactory ignores the
container and uses whatever spring.r2dbc.url resolves to, which means R2dbcConnectionDetails is absent.
Because the fallback is a working property rather than an error, this fails as an obscure connection error to
whatever host the property names, rather than as a missing-bean or misconfiguration message.
Reproduction
@TestConfiguration(proxyBeanMethods = false)
class ContainerConfiguration {
@Bean
@ServiceConnection
fun postgres(): PostgreSQLContainer = PostgreSQLContainer("postgres:17")
}
@SpringBootApplication
@Import(ContainerConfiguration::class)
class TestApplication
@SpringBootTest(classes = [TestApplication::class])
@ActiveProfiles("test")
class SomeIntegrationTest { /* autowires a repository backed by R2DBC */ }
application-test.yaml:
spring:
r2dbc:
url: r2dbc:postgresql://ignored.invalid:5432/ignored # deliberately unreachable
pool:
enabled: true
Expected: the container's R2dbcConnectionDetails takes precedence and the application connects to the
container, as it does for Flyway over JDBC.
Actual:
org.springframework.dao.DataAccessResourceFailureException: Failed to obtain R2DBC Connection
Caused by: io.r2dbc.postgresql.PostgresqlConnectionFactory$PostgresConnectionException:
[08003] Cannot connect to ignored.invalid/<unresolved>:5432
Caused by: java.net.UnknownHostException: ignored.invalid
The container is running and healthy throughout.
Evidence that R2dbcConnectionDetails is genuinely absent
R2dbcAutoConfiguration.propertiesR2dbcConnectionDetails is annotated
@ConditionalOnMissingBean(R2dbcConnectionDetails.class). The properties-backed implementation is therefore
created only when no other R2dbcConnectionDetails bean exists. Since the application demonstrably uses the
property value, the container's bean is not present.
Cross-check: removing spring.r2dbc.url from the test profile and giving the placeholder in the main
application.yaml an unreachable default made the application connect to that default instead — again showing
the property is being consulted rather than overridden.
Flyway, in the same context and from the same container bean, connects correctly and migrates the full schema.
So @ServiceConnection processing is working; only the R2DBC side produces nothing.
What was ruled out
Each of these was changed independently and the behaviour did not change:
| variable |
values tried |
| Testcontainers version |
2.0.4 (org.testcontainers.postgresql.PostgreSQLContainer) and 1.21.2 (org.testcontainers.containers.PostgreSQLContainer) |
| container image |
custom image built via ImageFromDockerfile + asCompatibleSubstituteFor("postgres"), and stock postgres:17 |
| where the configuration is imported |
@Import on the @SpringBootApplication class named in @SpringBootTest(classes = …), and @Import directly on the test class |
| connection name |
default, and explicit @ServiceConnection("postgresql") |
| Flyway |
enabled and disabled, to remove it as a factor |
The factory itself appears correctly wired:
org.springframework.boot.r2dbc.testcontainers.PostgresR2dbcContainerConnectionDetailsFactory is present in
spring-boot-r2dbc-4.0.0.jar
- it is registered in that jar's
META-INF/spring.factories
- its generic parameter is
org.testcontainers.postgresql.PostgreSQLContainer — the exact class of the bean
- its required class,
io.r2dbc.spi.ConnectionFactoryOptions, is on the test runtime classpath
(io.r2dbc:r2dbc-spi:1.0.0.RELEASE, org.postgresql:r2dbc-postgresql:1.1.1.RELEASE)
Note the jar also ships DeprecatedPostgresR2dbcContainerConnectionDetailsFactory, whose generic parameter is the
1.x org.testcontainers.containers.PostgreSQLContainer<?>. Neither path produced details in our testing.
spring-boot-testcontainers-4.0.0.jar itself contains no Postgres classes; the factories live in the
technology-specific modules, which may be relevant to how they are discovered.
Workaround
Publishing the connection as system properties from the container works, since system properties outrank
configuration files:
init {
CONTAINER.start()
System.setProperty("spring.r2dbc.url",
"r2dbc:postgresql://${CONTAINER.host}:${CONTAINER.firstMappedPort}/$DATABASE_NAME")
System.setProperty("spring.r2dbc.username", CONTAINER.username)
System.setProperty("spring.r2dbc.password", CONTAINER.password)
}
Notes
- The application is WebFlux with
spring.main.web-application-type: none in the test profile.
- Dependency versions are managed by
io.spring.dependency-management; the org.testcontainers coordinates are
not managed by it under Boot 4 and carry explicit versions.
- We did not test the static-field /
@ImportTestcontainers style of declaring the container, only the
@Bean + @ServiceConnection style shown above.
@ServiceConnectionon aPostgreSQLContainerproducesJdbcConnectionDetailsbut notR2dbcConnectionDetailsSpring Boot version: 4.0.0
Testcontainers version: reproduced on both 2.0.4 and 1.21.2
Java: 25 · Kotlin: 2.4.10 · Build: Gradle,
io.spring.dependency-managementDocker: 28.3.2 (macOS, aarch64)
Summary
A
PostgreSQLContainer@Beanannotated@ServiceConnectionin a@TestConfigurationsupplies connectiondetails to Flyway (JDBC) but not to R2DBC. Flyway connects to the container and migrates successfully, proving
JdbcConnectionDetailsis published. In the same context, the application'sConnectionFactoryignores thecontainer and uses whatever
spring.r2dbc.urlresolves to, which meansR2dbcConnectionDetailsis absent.Because the fallback is a working property rather than an error, this fails as an obscure connection error to
whatever host the property names, rather than as a missing-bean or misconfiguration message.
Reproduction
application-test.yaml:Expected: the container's
R2dbcConnectionDetailstakes precedence and the application connects to thecontainer, as it does for Flyway over JDBC.
Actual:
The container is running and healthy throughout.
Evidence that
R2dbcConnectionDetailsis genuinely absentR2dbcAutoConfiguration.propertiesR2dbcConnectionDetailsis annotated@ConditionalOnMissingBean(R2dbcConnectionDetails.class). The properties-backed implementation is thereforecreated only when no other
R2dbcConnectionDetailsbean exists. Since the application demonstrably uses theproperty value, the container's bean is not present.
Cross-check: removing
spring.r2dbc.urlfrom the test profile and giving the placeholder in the mainapplication.yamlan unreachable default made the application connect to that default instead — again showingthe property is being consulted rather than overridden.
Flyway, in the same context and from the same container bean, connects correctly and migrates the full schema.
So
@ServiceConnectionprocessing is working; only the R2DBC side produces nothing.What was ruled out
Each of these was changed independently and the behaviour did not change:
org.testcontainers.postgresql.PostgreSQLContainer) and 1.21.2 (org.testcontainers.containers.PostgreSQLContainer)ImageFromDockerfile+asCompatibleSubstituteFor("postgres"), and stockpostgres:17@Importon the@SpringBootApplicationclass named in@SpringBootTest(classes = …), and@Importdirectly on the test class@ServiceConnection("postgresql")The factory itself appears correctly wired:
org.springframework.boot.r2dbc.testcontainers.PostgresR2dbcContainerConnectionDetailsFactoryis present inspring-boot-r2dbc-4.0.0.jarMETA-INF/spring.factoriesorg.testcontainers.postgresql.PostgreSQLContainer— the exact class of the beanio.r2dbc.spi.ConnectionFactoryOptions, is on the test runtime classpath(
io.r2dbc:r2dbc-spi:1.0.0.RELEASE,org.postgresql:r2dbc-postgresql:1.1.1.RELEASE)Note the jar also ships
DeprecatedPostgresR2dbcContainerConnectionDetailsFactory, whose generic parameter is the1.x
org.testcontainers.containers.PostgreSQLContainer<?>. Neither path produced details in our testing.spring-boot-testcontainers-4.0.0.jaritself contains no Postgres classes; the factories live in thetechnology-specific modules, which may be relevant to how they are discovered.
Workaround
Publishing the connection as system properties from the container works, since system properties outrank
configuration files:
Notes
spring.main.web-application-type: nonein the test profile.io.spring.dependency-management; theorg.testcontainerscoordinates arenot managed by it under Boot 4 and carry explicit versions.
@ImportTestcontainersstyle of declaring the container, only the@Bean+@ServiceConnectionstyle shown above.