DevOps

How to Configure Testcontainers to Run on Podman

Testcontainers is a popular Java library that simplifies integration testing by providing lightweight, disposable containers for databases, message brokers, and other services. Traditionally, Testcontainers relies on Docker to manage these containers. However, many developers are adopting Podman as a Docker alternative because of its daemonless architecture, rootless containers, and compatibility with Docker CLI commands. Configuring Testcontainers to work with Podman requires some additional setup. This article provides a guide on how to do just that.

1. What Is Podman and Why Use It with Testcontainers?

Podman is a container engine that runs without a central daemon, making it more secure and lightweight compared to Docker. It supports running containers in both root and rootless modes and implements much of Docker’s command-line interface, allowing developers to use familiar commands. Importantly for Testcontainers, Podman can provide a Docker-compatible API socket, enabling Testcontainers to communicate with it just as it would with a Docker daemon.

2. Prerequisites

  • Before configuring Testcontainers to work with Podman, ensure that Podman is installed on your system, which can be confirmed by running the podman --version command.
  • Podman socket is enabled: Testcontainers communicates with Podman through a Docker-compatible API socket.
  • Java and Maven/Gradle are set up: Testcontainers is a Java library, so a working Java environment is necessary.

3. Enabling the Podman Docker-Compatible Socket

Testcontainers communicates with a container engine via a Docker API socket. With Podman, this socket must be explicitly enabled:

On Linux, start the Podman service and enable its socket:

systemctl --user enable --now podman.socket

Verify the socket is running:

ls -la /run/user/$UID/podman/podman.sock

This socket file implements a Docker-compatible API that Testcontainers can communicate with.

On macOS, Podman runs containers inside a lightweight virtual machine rather than directly on the host. Before Testcontainers can interact with Podman, this virtual machine must be created and its internal Docker-compatible socket exposed to the host system.

Start by initializing a Podman machine, which will serve as the environment where containers are executed

podman machine init

Once initialized, start the virtual machine:

podman machine start

After the machine is running, verify its connectivity and inspect the available Podman connections:

podman system connection list

The output information provides two critical details: the SSH port used to connect to the Podman machine and the path to the internal podman.sock file. With these values, the socket can be forwarded to the host.

Alternatively, you can directly inspect the Podman machine to determine the path of the Docker-compatible socket. After ensuring the Podman machine is running:

podman machine start
podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}'

This command outputs the exact location of the podman.sock file inside the Podman virtual machine, which can then be used when exposing the socket to the host for Testcontainers integration.

4. Configuring Testcontainers for Podman

Setting Environment Variables

Testcontainers discovers the container runtime configuration through environment variables. To point Testcontainers at Podman’s socket, set the DOCKER_HOST environment variable to the Podman socket path. For example:

Linux:

export DOCKER_HOST=unix://${XDG_RUNTIME_DIR}/podman/podman.sock
export TESTCONTAINERS_RYUK_DISABLED=true

macOS (using forwarded socket):

export DOCKER_HOST=unix://$(podman machine inspect --format '{{.ConnectionInfo.PodmanSocket.Path}}')
export TESTCONTAINERS_RYUK_DISABLED=true
Tip
The TESTCONTAINERS_RYUK_DISABLED flag is often necessary because the Ryuk container cleanup component used by Testcontainers for Docker does not always work with Podman, especially in rootless setups

5. Writing Tests with Testcontainers and Podman

Once Testcontainers is configured to communicate with Podman through a Docker-compatible socket, writing tests is identical to a standard Docker-based setup. The container lifecycle, port mappings, and connection details are handled transparently by Testcontainers.

Singleton PostgreSQL Container

When using Podman, especially with Ryuk disabled, it is common to manage container lifecycles explicitly. A singleton container pattern ensures the database starts only once and is reused across multiple test classes.

public class PostgreSQLTestContainer {

    private static final PostgreSQLContainer<?> INSTANCE
            = new PostgreSQLContainer<>("postgres:15-alpine")
                    .withDatabaseName("testdb")
                    .withUsername("test")
                    .withPassword("test");

    static {
        INSTANCE.start();
        Runtime.getRuntime().addShutdownHook(new Thread(INSTANCE::stop));
    }

    public static PostgreSQLContainer<?> getInstance() {
        return INSTANCE;
    }
}

Integration Test Using the Container

The test below connects to the PostgreSQL container, creates a table, inserts data, and verifies the result.

class PostgreSQLIntegrationTest {

    private final PostgreSQLContainer<?> postgres = PostgreSQLTestContainer.getInstance();

    @Test
    void shouldInsertAndQueryData() throws Exception {
        try (Connection connection = DriverManager.getConnection(
                postgres.getJdbcUrl(),
                postgres.getUsername(),
                postgres.getPassword()
        )) {
            Statement statement = connection.createStatement();

            statement.execute("""
                CREATE TABLE users (
                    id SERIAL PRIMARY KEY,
                    name VARCHAR(100)
                )
            """);

            statement.execute("INSERT INTO users (name) VALUES ('Benjamin')");

            ResultSet resultSet
                    = statement.executeQuery("SELECT COUNT(*) FROM users");

            resultSet.next();
            int count = resultSet.getInt(1);

            assertEquals(1, count);
        }
    }
}

From the test’s perspective, there is no difference between Docker and Podman. Testcontainers communicates with the container runtime through the configured socket, and Podman responds using a Docker-compatible API. As long as the DOCKER_HOST environment variable points to the Podman socket and Ryuk is disabled when required, Testcontainers manages containers normally.

6. Conclusion

In this article, we explored how to configure Testcontainers to work with Podman by exposing a Docker-compatible Podman socket and directing Testcontainers to use it. We covered the concepts behind Podman’s execution model, walked through platform-specific setup steps, and demonstrated how to write and manage integration tests using Testcontainers with Podman. This approach allows us to continue using familiar testing tools while benefiting from a daemonless, secure container runtime in both local development and CI environments.

This article explained how to configure Testcontainers to work with Podman in Java projects.

Omozegie Aziegbe

Omos Aziegbe is a technical writer and web/application developer with a BSc in Computer Science and Software Engineering from the University of Bedfordshire. Specializing in Java enterprise applications with the Jakarta EE framework, Omos also works with HTML5, CSS, and JavaScript for web development. As a freelance web developer, Omos combines technical expertise with research and writing on topics such as software engineering, programming, web application development, computer science, and technology.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button