Yes, we're now running our Black Friday Sale. All Access and Pro are 33% off until 2nd December, 2025:
Configuring Testcontainers to Work with Podman
Last updated: November 20, 2025
1. Overview
In recent years, Testcontainers has become a go-to library for Java integration testing, enabling us to spin up real containers during our test lifecycle. Such containers represent external dependencies, such as databases, message brokers, and other components. The official library page describes it as “an open source library for providing throwaway, lightweight instances…that run in a Docker container.”
At the same time, many teams are exploring or migrating to alternative container solutions. Podman is one of those alternatives, if not the most common. The reason for that is licensing changes or a preference for rootless engines. So the natural question arises: Can we use Podman instead of Docker for Testcontainers in a Java project?
In this tutorial, we’ll walk through how to get Testcontainers working with Podman, outline what’s officially supported, show the required configuration, and highlight pitfalls.
2. Why Consider Using Podman Instead of Docker?
As mentioned, Docker has made a few changes to its licensing in the past, but here are a few more reasons:
- On Linux, Podman offers a daemonless, rootless container engine that can be simpler and more secure in some environments
- In CI/CD environments (especially on Linux), running Podman can align with existing infrastructure and reduce reliance on the Docker Engine
- Teams using Apple Silicon Macs may prefer Podman’s podman machine mode over Docker Desktop
That said, for the Testcontainers-based tests, we need a Docker API-compatible container runtime, as the maintainer states that container runtimes are not actively tested in the main development workflow.
3. Configuring Podman for Testcontainers
Let’s walk through the steps required to configure Podman so that Testcontainers can use it as a container runtime. The setup presented in this article was tested only on Linux and Mac machines. It may be possible to use the WSL2 Linux distribution for Windows, but it has not been tested.
3.1. Installing Podman
First, let’s install Podman on our local environments following the instructions on the official page. This will enable us to run a container with the Podman container engine.
3.2. Enabling the Podman Socket
Testcontainers needs to communicate with the container engine through a socket that implements the Docker API.
On Linux, we can enable the socket for our users like this:
systemctl --user enable --now podman.socket
Then, we can confirm that the socket is active:
ls -la /run/user/$UID/podman/podman.sock
On Mac, the socket is available inside the Podman VM. We can check its path using:
podman machine start
podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}'
The last command prints the full path to the Podman Docker API socket on your Mac host. We will use that path in the next section to configure DOCKER_HOST.
3.3. Setting the Environment Variables
Next, we need to instruct Testcontainers on how to connect to the Podman socket. This can be done by setting the DOCKER_HOST environment variable.
On Linux:
export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock
export TESTCONTAINERS_RYUK_DISABLED=true
On macOS:
export DOCKER_HOST=unix://$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}')
export TESTCONTAINERS_RYUK_DISABLED=true
By default, Docker setups use Testcontainers’ helper container, “Ryuk,” to automatically clean up resources (containers and networks). With Podman support, this may not always work (especially in rootless mode or macOS VM scenarios). The workaround is to turn it off using TESTCONTAINERS_RYUK_DISABLED=true.
4. Known Limitations & Caveats
It’s important to highlight the things that may still cause friction when running Testcontainers with Podman:
| Feature gaps | The official statement is that alternative container runtimes are not actively tested by the Testcontainers team, so “not all features might be available.” |
| Ryuk channel issues | As mentioned, container cleanup via Ryuk may fail or require privileged mode or disabling. That means potential leftover containers, networks, and volumes, if cleanup is not handled. |
| Socket/permissions issues | In rootless mode, Podman listens on unix:///run/user/$UID/podman/podman.sock. Our test environment must have permissions to that socket; on MacOS, the VM and host mapping might cause path mismatches. |
| Host networking & mapping quirks | Because Podman uses a different default network mode (for instance, sometimes podman network instead of Docker’s bridge), specific advanced networking configurations (custom networks, aliasing, host port binding) may not work identically to Docker |
| Image pull/short‐name lookup | On some distributions, Podman may prompt for a registry when using short image names (e.g., busybox) unless unqualified-search-registries is configured. If Testcontainers pulls and encounters a prompt, tests may hang. |
| CI/agent environment variability | If we build agents that vary (some use Docker, some Podman), we may introduce subtle differences. We must test both paths. |
| Compatibility with third-party extensions | If you rely on frameworks (for instance, Quarkus Dev Services) that assume Docker, their Podman support may be less mature. |
In short, yes, we can use Testcontainers with Podman for Java tests, as long as we configure the socket and environment variables and consider the cleanup strategy. So it’s not plug-and-play. We need to test the configuration path early (especially on Mac or ARM) and monitor for leftover containers, socket permissions issues, or feature mismatches.
5. Testing Setup
With the environment configured, we can write integration tests just as we do with Docker. For example:
@Test
void whenSettingValue_thenCanGetItBack() {
try (RedisContainer redis = new RedisContainer("redis:7-alpine").withExposedPorts(6379)) {
redis.start();
String host = redis.getHost();
int port = redis.getFirstMappedPort();
try (Jedis jedis = new Jedis(host, port)) {
jedis.set("greeting", "hello");
String value = jedis.get("greeting");
Assertions.assertEquals("hello", value);
}
}
}
This test is a simple integration test using a Redis container, where the test only sets a value and retrieves it using the Jedis APIs. This is a straightforward test, but it is enough to confirm our Podman setup.
Next, let’s also use another very common database, which is frequently used on Java projects.
@Test
void whenQueryingDatabase_thenReturnsOne() throws Exception {
try (MySQLContainer mysql = new MySQLContainer("mysql:8.4")) {
mysql.start();
try (Connection conn = DriverManager
.getConnection(mysql.getJdbcUrl(), mysql.getUsername(), mysql.getPassword());
Statement st = conn.createStatement()) {
st.execute("CREATE TABLE t(id INT PRIMARY KEY)");
st.execute("INSERT INTO t VALUES (1)");
ResultSet rs = st.executeQuery("SELECT COUNT(*) FROM t");
rs.next();
Assertions.assertEquals(1, rs.getInt(1));
}
}
}
Finally, let’s use a message broker with a setup a bit more complicated but still using a single container.
@Test
void whenProducingMessage_thenConsumerReceivesIt() {
DockerImageName image = DockerImageName.parse("confluentinc/cp-kafka:7.6.1");
try (KafkaContainer kafka = new KafkaContainer(image)) {
kafka.start();
String bootstrap = kafka.getBootstrapServers();
String topic = "hello";
Properties prodProps = new Properties();
prodProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrap);
prodProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
prodProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
try (KafkaProducer<String, String> producer = new KafkaProducer<>(prodProps)) {
producer.send(new ProducerRecord<>(topic, "key", "hello")).get();
} catch (Exception e) {
throw new RuntimeException(e);
}
Properties consProps = new Properties();
consProps.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrap);
consProps.put(ConsumerConfig.GROUP_ID_CONFIG, "test-group");
consProps.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
consProps.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
consProps.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
try (KafkaConsumer<String, String> consumer = new KafkaConsumer<>(consProps)) {
consumer.subscribe(Collections.singletonList(topic));
ConsumerRecords<String, String> records = consumer.poll(Duration.ofSeconds(10));
ConsumerRecord<String, String> first = records.iterator().next();
Assertions.assertEquals("hello", first.value());
}
}
}
Using these integration tests, we can confirm the container’s compatibility, verify that our Podman setup is functional, and enable us to utilize a new runtime engine.
6. Conclusion
In this article, we provide a clear path to adopting Podman with Testcontainers in our Java projects and help us avoid the common pitfalls many developers encounter.
Switching the container runtime from Docker to Podman for Testcontainers in Java isn’t just about replacing an alias. It requires awareness of socket configuration, cleanup mechanisms, permissions, and underlying differences. But with the correct setup, we can be confident our integration tests run reliably, regardless of the container engine.















