{"id":19854006,"url":"https://github.com/eugene-khyst/podman-testcontainers","last_synced_at":"2025-05-02T01:30:27.182Z","repository":{"id":53833956,"uuid":"469439846","full_name":"eugene-khyst/podman-testcontainers","owner":"eugene-khyst","description":"The example of using Podman with Testcontainers in Java projects, that use Gradle on Ubuntu Linux and MacOS (both x86_64 and Apple silicon).","archived":true,"fork":false,"pushed_at":"2023-03-22T15:02:04.000Z","size":85,"stargazers_count":44,"open_issues_count":5,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-06T20:25:03.718Z","etag":null,"topics":["apple-silicon","docker","java","junit","linux","mac-m1","macos","podman","podman-machine","testcontainers","testcontainers-junit-5"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/eugene-khyst.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null}},"created_at":"2022-03-13T17:14:08.000Z","updated_at":"2025-04-06T10:48:53.000Z","dependencies_parsed_at":"2023-08-11T16:28:26.038Z","dependency_job_id":null,"html_url":"https://github.com/eugene-khyst/podman-testcontainers","commit_stats":null,"previous_names":["eugene-khyst/podman-testcontainers"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eugene-khyst%2Fpodman-testcontainers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eugene-khyst%2Fpodman-testcontainers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eugene-khyst%2Fpodman-testcontainers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/eugene-khyst%2Fpodman-testcontainers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/eugene-khyst","download_url":"https://codeload.github.com/eugene-khyst/podman-testcontainers/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251969236,"owners_count":21673180,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["apple-silicon","docker","java","junit","linux","mac-m1","macos","podman","podman-machine","testcontainers","testcontainers-junit-5"],"created_at":"2024-11-12T14:08:19.742Z","updated_at":"2025-05-02T01:30:26.855Z","avatar_url":"https://github.com/eugene-khyst.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca id=\"0\"\u003e\u003c/a\u003eTestcontainers with Podman\n\n- [What is Testcontainers](#1)\n- [Why replace Docker with Podman?](#2)\n- [Install Podman](#3)\n  - [Linux](#3-1)\n    - [Ubuntu 20.04](#3-1-1)\n    - [Ubuntu 20.10 and newer](#3-1-2)\n  - [MacOS](#3-2)\n- [Verify that Podman is installed correctly](#4)\n- [Enable the Podman service](#5)\n  - [Linux](#5-1)\n  - [MacOS](#5-2)\n- [Configure Testcontainers](#6)\n  - [Configure Gradle build script](#6-1)\n  - [Pass the environment variables](#6-2)\n    - [Linux](#6-2-1)\n    - [MacOS](#6-2-2)\n- [Create a base test class](#7)\n- [Implement test classes](#8)\n\n\u003c!-- Table of contents is made with https://github.com/evgeniy-khist/markdown-toc --\u003e\n\n## \u003ca id=\"1\"\u003e\u003c/a\u003eWhat is Testcontainers\n\n\u003e [Testcontainers](https://www.testcontainers.org/) 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.\n\nWith Testcontainers, your JUnit tests can use PostgreSQL to run in a Docker container instead of an embedded H2\nDatabase.\n\n## \u003ca id=\"2\"\u003e\u003c/a\u003eWhy replace Docker with Podman?\n\nDocker changed the Docker Desktop terms in 2021. Docker Desktop is not free for everyone anymore:\n\n* [Docker is Updating and Extending Our Product Subscriptions](https://www.docker.com/blog/updating-product-subscriptions/)\n* [Do the New Terms of Docker Desktop Apply If You Don’t Use the Docker Desktop UI?](https://www.docker.com/blog/do-the-new-terms-of-docker-desktop-apply-if-you-dont-use-the-docker-desktop-ui/)\n\nOn Linux you can still use the Docker CLI and Docker Engine for free. On Windows you could install and run Docker CLI\nand Engine inside [WSL2](https://docs.microsoft.com/en-us/windows/wsl/) (Windows Subsystem for Linux). On MacOS you can\ninstall Docker CLI and Engine inside a virtual machine.\n\nDocker can be replaced with an open-source alternative called [Podman](https://podman.io/) maintained by\nthe [containers](https://github.com/containers) organization.\n\n\u003e Podman is a daemonless container engine for developing, managing, and running OCI Containers on your Linux System. Containers can either be run as root or in rootless mode. Simply put: `alias docker=podman`.\n\nThis example shows how to use Podman with [Testcontainers](https://www.testcontainers.org/) in Java projects that use\nGradle on Ubuntu Linux and MacOS (both x86_64 and Apple silicon).\n\n## \u003ca id=\"3\"\u003e\u003c/a\u003eInstall Podman\n\n### \u003ca id=\"3-1\"\u003e\u003c/a\u003eLinux\n\nSee https://podman.io/getting-started/installation#linux-distributions\n\n#### \u003ca id=\"3-1-1\"\u003e\u003c/a\u003eUbuntu 20.04\n\nSet up the **stable** repository and install the podman package:\n\n```bash\nsource /etc/os-release\necho \"deb https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/ /\" | sudo tee /etc/apt/sources.list.d/devel:kubic:libcontainers:stable.list\ncurl -L https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/xUbuntu_${VERSION_ID}/Release.key | sudo apt-key add -\nsudo apt-get update\nsudo apt-get -y upgrade\nsudo apt-get -y install podman\n```\n\nVerify the installation:\n\n```bash\npodman info\n```\n\n#### \u003ca id=\"3-1-2\"\u003e\u003c/a\u003eUbuntu 20.10 and newer\n\nThe podman package is available in the official repositories for Ubuntu 20.10 and newer.\n\nInstall the podman package:\n\n```bash\nsudo apt-get -y update\nsudo apt-get -y install podman\n```\n\nVerify the installation:\n\n```bash\npodman info\n```\n\n### \u003ca id=\"3-2\"\u003e\u003c/a\u003eMacOS\n\nSee https://podman.io/getting-started/installation#macos\n\nPodman is a tool for running Linux containers. Podman includes a command, `podman machine` that automatically manages\nLinux VM’s on MacOS.\n\nInstall Podman Machine and Remote Client:\n\n```bash\nbrew install podman\n```\n\nStart the Podman-managed VM:\n\n```bash\npodman machine init\npodman machine start\n```\n\nVerify the installation:\n\n```bash\npodman info\n```\n\n## \u003ca id=\"4\"\u003e\u003c/a\u003eVerify that Podman is installed correctly\n\nRun the `busybox` or other image to verify that Podman is installed correctly:\n\n```bash\npodman run --rm busybox echo \"hello-world\"\n```\n\nIf you have the following error:\n\n```\nError: short-name \"busybox\" did not resolve to an alias and no unqualified-search registries are defined in \"/etc/containers/registries.conf\"\n```\n\nConfigure `unqualified-search-registries`:\n\n```bash\necho \"unqualified-search-registries = [\\\"docker.io\\\"]\" | sudo tee -a /etc/containers/registries.conf\n```\n\nSee https://www.redhat.com/sysadmin/container-image-short-names\n\n## \u003ca id=\"5\"\u003e\u003c/a\u003eEnable the Podman service\n\nTestcontainers library communicates with Podman using socket file.\n\n### \u003ca id=\"5-1\"\u003e\u003c/a\u003eLinux\n\nStart Podman service for a regular user (rootless) and make it listen to a socket:\n\n```bash\nsystemctl --user enable --now podman.socket\n```\n\nCheck the Podman service status:\n\n```bash\nsystemctl --user status podman.socket\n```\n\nCheck the socket file exists:\n\n```bash\nls -la /run/user/$UID/podman/podman.sock\n```\n\n### \u003ca id=\"5-2\"\u003e\u003c/a\u003eMacOS\n\nPodman socket file `/run/user/1000/podman/podman.sock` can be found inside the Podman-managed Linux VM. A local socket\non MacOS can be forwarded to a remote socket on Podman-managed VM using SSH tunneling.\n\nThe port of the Podman-managed VM can be found with the command `podman system connection list --format=json`.\n\nInstall [jq](https://stedolan.github.io/jq/) to parse JSON:\n\n```bash\nbrew install jq\n```\n\nCreate a shell alias to forward the local socket `/tmp/podman.sock` to the remote socket `/run/user/1000/podman/podman.sock`:\n\n```bash\necho \"alias podman-sock=\\\"rm -f /tmp/podman.sock \u0026\u0026 ssh -i ~/.ssh/podman-machine-default -p \\$(podman system connection list --format=json | jq '.[0].URI' | sed -E 's|.+://.+@.+:([[:digit:]]+)/.+|\\1|') -L'/tmp/podman.sock:/run/user/1000/podman/podman.sock' -N core@localhost\\\"\" \u003e\u003e ~/.zprofile\nsource ~/.zprofile\n```\n\nOpen an SSH tunnel:\n\n```bash\npodman-sock\n```\n\nMake sure the SSH tunnel is open before executing tests using Testcontainers.\n\n## \u003ca id=\"6\"\u003e\u003c/a\u003eConfigure Testcontainers\n\nTestcontainers library loads configuration from multiple locations, including environment variables.\n\n### \u003ca id=\"6-1\"\u003e\u003c/a\u003eConfigure Gradle build script\n\nI recommended configuring Testcontainers in a Gradle build script.\n\n[`build.gradle`](build.gradle)\n\n```groovy\ntest {\n    OperatingSystem os = DefaultNativePlatform.currentOperatingSystem;\n    if (os.isLinux()) {\n        def uid = [\"id\", \"-u\"].execute().text.trim()\n        environment \"DOCKER_HOST\", \"unix:///run/user/$uid/podman/podman.sock\"\n    } else if (os.isMacOsX()) {\n        environment \"DOCKER_HOST\", \"unix:///tmp/podman.sock\"\n    }\n    environment \"TESTCONTAINERS_RYUK_DISABLED\", \"true\"\n}\n```\n\nSet `DOCKER_HOST` environment variable to Podman socket file depending on the operating system.\n\nDisable Ryuk with the environment variable `TESTCONTAINERS_RYUK_DISABLED`.\n\n\u003e [Moby Ryuk](https://github.com/testcontainers/moby-ryuk) helps you to remove containers/networks/volumes/images by given filter after specified delay.\n\nRyuk is a technology for Docker and doesn't support Podman. See https://github.com/testcontainers/moby-ryuk/issues/23\n\nTestcontainers library uses Ruyk to remove containers. Instead of relying on Ryuk to implicitly remove containers, we\nwill explicitly remove containers with a JVM shutdown hook:\n\n```java\nRuntime.getRuntime().addShutdownHook(new Thread(container::stop));\n```\n\n### \u003ca id=\"6-2\"\u003e\u003c/a\u003ePass the environment variables\n\nAs an alternative to configuring Testcontainers in a Gradle build script, you can pass the environment variables to Gradle.\n\n#### \u003ca id=\"6-2-1\"\u003e\u003c/a\u003eLinux\n\n```bash\nDOCKER_HOST=\"unix:///run/user/$UID/podman/podman.sock\" \\\nTESTCONTAINERS_RYUK_DISABLED=\"true\" \\\n./gradlew clean build -i\n```\n\n#### \u003ca id=\"6-2-2\"\u003e\u003c/a\u003eMacOS\n\n```bash\nDOCKER_HOST=\"unix:///tmp/podman.sock\" \\\nTESTCONTAINERS_RYUK_DISABLED=\"true\" \\\n./gradlew clean build -i\n```\n\n## \u003ca id=\"7\"\u003e\u003c/a\u003eCreate a base test class\n\nIt is useful to define a container that is only started once for all (or several) test classes. Starting a database\ncontainer for each test class is a big overhead.\n\nContainers should be JVM singletons and not a Spring singletons. Sometimes Spring can't reuse an already existing\ncontext, for example\nwhen [`@MockBean`](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/mock/mockito/MockBean.html)\nor [`@DirtiesContext`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/annotation/DirtiesContext.html)\nare used. This means you get multiple Spring contexts in integration tests.\n\nTestcontainers library supports singleton containers pattern.\nSee https://www.testcontainers.org/test_framework_integration/manual_lifecycle_control/#singleton-containers\n\nCreate a base test class and implement a singleton container pattern.\n\n[`BaseIntegrationTest.java`](src/test/java/com/example/podman/testcontainers/BaseIntegrationTest.java)\n\n```java\n\n@SpringBootTest\npublic abstract class BaseIntegrationTest {\n\n    public static final int REDIS_PORT = 6379;\n\n    private static final PostgreSQLContainer\u003c?\u003e POSTGRES;\n    private static final GenericContainer\u003c?\u003e REDIS;\n\n    static {\n        POSTGRES = createPostgresContainer();\n        REDIS = createRedisContainer();\n    }\n\n    private static PostgreSQLContainer\u003c?\u003e createPostgresContainer() {\n        PostgreSQLContainer\u003c?\u003e postgres =\n                new PostgreSQLContainer\u003c\u003e(DockerImageName.parse(\"postgres\").withTag(\"14-alpine\"));\n        postgres.start();\n        Runtime.getRuntime().addShutdownHook(new Thread(postgres::stop));\n        return postgres;\n    }\n\n    private static GenericContainer\u003c?\u003e createRedisContainer() {\n        GenericContainer\u003c?\u003e redis =\n                new GenericContainer\u003c\u003e(DockerImageName.parse(\"redis\").withTag(\"6-alpine\"))\n                        .withExposedPorts(REDIS_PORT);\n        redis.start();\n        Runtime.getRuntime().addShutdownHook(new Thread(redis::stop));\n        return redis;\n    }\n\n    @DynamicPropertySource\n    static void registerPostgresProperties(DynamicPropertyRegistry registry) {\n        registry.add(\"spring.r2dbc.url\", BaseIntegrationTest::getPostgresR2bcUrl);\n        registry.add(\"spring.r2dbc.username\", POSTGRES::getUsername);\n        registry.add(\"spring.r2dbc.password\", POSTGRES::getPassword);\n        registry.add(\"spring.flyway.url\", POSTGRES::getJdbcUrl);\n        registry.add(\"spring.flyway.user\", POSTGRES::getUsername);\n        registry.add(\"spring.flyway.password\", POSTGRES::getPassword);\n    }\n\n    @DynamicPropertySource\n    static void registerRedisProperties(DynamicPropertyRegistry registry) {\n        registry.add(\"spring.redis.host\", REDIS::getContainerIpAddress);\n        registry.add(\"spring.redis.port\", () -\u003e REDIS.getMappedPort(REDIS_PORT));\n    }\n\n    private static String getPostgresR2bcUrl() {\n        return \"r2dbc:postgresql://\"\n                + POSTGRES.getContainerIpAddress()\n                + \":\"\n                + POSTGRES.getMappedPort(PostgreSQLContainer.POSTGRESQL_PORT)\n                + \"/\"\n                + POSTGRES.getDatabaseName();\n    }\n}\n```\n\nThe singleton container is started only once when the base class is loaded. The container can then be used by all\ninheriting test classes. At the end of the tests the JVM shutdown hook will take care of stopping the singleton\ncontainer.\n\n[`@DynamicPropertySource`](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/test/context/DynamicPropertySource.html)\nannotation and its supporting infrastructure allows properties from Testcontainers based tests to be exposed easily to\nSpring integration tests.\n\n## \u003ca id=\"8\"\u003e\u003c/a\u003eImplement test classes\n\nImplement test classes by inheriting the base\nclass [`BaseIntegrationTest.java`](src/test/java/com/example/podman/testcontainers/BaseIntegrationTest.java).\n\n[`PersonRepositoryTests`](src/test/java/com/example/podman/testcontainers/PersonRepositoryTests.java)\n\n```java\n\n@Slf4j\nclass PersonRepositoryTests extends BaseIntegrationTest {\n\n    @Autowired\n    PersonRepository personRepository;\n\n    @Test\n    void shouldFindByLastName() {\n        personRepository.save(new Person(null, \"Harry\", \"Callahan\")).subscribe();\n\n        personRepository\n                .findByLastName(\"Callahan\")\n                .doOnNext(person -\u003e log.info(\"Person found with findByLastName(\\\"Callahan\\\"): {}\", person))\n                .as(StepVerifier::create)\n                .expectNextMatches(\n                        person -\u003e\n                                person.id() != null\n                                        \u0026\u0026 \"Harry\".equals(person.firstName())\n                                        \u0026\u0026 \"Callahan\".equals(person.lastName()))\n                .verifyComplete();\n    }\n}\n```\n\n[`RedisIncrementTests`](src/test/java/com/example/podman/testcontainers/RedisIncrementTests.java)\n\n```java\npublic class RedisIncrementTests extends BaseIntegrationTest {\n\n    @Autowired\n    ReactiveRedisTemplate\u003cString, Long\u003e redisTemplate;\n\n    @Test\n    void shouldIncrementKey() {\n        Flux.range(0, 3)\n                .flatMap(i -\u003e redisTemplate.opsForValue().increment(\"mykey\"))\n                .as(StepVerifier::create)\n                .expectNext(1L)\n                .expectNext(2L)\n                .expectNext(3L)\n                .verifyComplete();\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feugene-khyst%2Fpodman-testcontainers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Feugene-khyst%2Fpodman-testcontainers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Feugene-khyst%2Fpodman-testcontainers/lists"}