At the moment current documentation recommends this approach to configure PodMan:

tasks.named<BootBuildImage>("bootBuildImage") {
    docker {
        host.set("unix:///run/user/1000/podman/podman.sock")
        bindHostToBuilder.set(true)
    }
}

Where 1000 is an uid of a user, which is usually a 1000, but might be different in some cases.

So, more generic approach to configure PodMan(in theory it's also compatible with Docker) would be:

tasks.named<BootBuildImage>("bootBuildImage") {
    docker {
        fun String.runCommand(
            workingDir: File = File("."),
            timeoutAmount: Long = 60,
            timeoutUnit: TimeUnit = TimeUnit.SECONDS
        ): String? = runCatching {
            ProcessBuilder("\\s".toRegex().split(this))
                .directory(workingDir)
                .redirectOutput(ProcessBuilder.Redirect.PIPE)
                .redirectError(ProcessBuilder.Redirect.PIPE)
                .start().also { it.waitFor(timeoutAmount, timeoutUnit) }
                .inputStream.bufferedReader().readText().trim()
        }.onFailure { it.printStackTrace() }.getOrNull()

        val uid = "id -u".runCommand() ?: 0;
        val socketPath = "/run/user/$uid/podman/podman.sock";
        if (file(socketPath).exists()) {
            host.set("unix://$socketPath")
            bindHostToBuilder.set(true)
        }
    }
}

Which is ugly. It would be nice to move this logic inside of Spring Boot Gradle(and probably maven) plugins to eliminate that boilerplate from configs.

Comment From: mhalbritter

On my system, an env variable UID is populated, which can be used with Gradle. Would that be an acceptable workaround?

Comment From: andrej-urvantsev

There is something special about that variable:

# In terminal
echo ${UID}                            
1000

but System.getenv("UID") in java returns null.

I mean that it's possible to come up with some workarounds, but it would be much better if Spring Boot plugin could auto-detect PodMan.

Comment From: mhalbritter

Huh, you're right. It's not shown in env output, either. TIL.

Comment From: andrej-urvantsev

I think it's set in shell(in bash or zsh for example) - not globally.

Comment From: scottfrederick

With podman installed, you can run the command podman info -f "{{.Host.RemoteSocket.Path}}") to get the socket that should be used to connect to it. With this, you can set the DOCKER_HOST environment variable that many tools will use to connect (for example, on Linux export DOCKER_HOST="unix://$(podman info -f "{{.Host.RemoteSocket.Path}}")").

That's enough for Spring Boot to use podman for downloading the builder and run images, as well as any configured buildpack images, since the Boot plugins will honor the DOCKER_HOST environment variable. The other part is that the processes running inside the builder image need to connect to the podman socket, which is what the docker.bindHostToBuilder option does. If you can set the DOCKER_HOST environment variable, then this would be as simple as checking for that in your build script and setting bindHostToBuilder accordingly (as this user is doing).

@lazystone Does this technique suit your needs?

Comment From: andrej-urvantsev

Ok, that's way better than mine approach. But what would be a downside to do following by default inside the plugin?

val dockerHost = System.getenv("DOCKER_HOST")
if (dockerHost != null) {
    docker {
        host.set(dockerHost)
        bindHostToBuilder.set(true)
    }
}

I guess if DOCKER_HOST environment variable is defined, then it should be used by all sub-processes anyway?

Comment From: scottfrederick

I guess if DOCKER_HOST environment variable is defined, then it should be used by all sub-processes anyway?

That's not necessarily true. In the minikube example, it is necessary to set the address of the Docker host as a remote address (not a socket), but bindHostToBuilder is not required because minikube exposes the default /var/run/docker.sock socket location inside of running containers. This is dependent on how each Docker-like daemon chooses to implement things.

I think it would be good to add a note about the podman info command to the Boot documentation to make it more clear how you should get the value to provide to docker.host, but I'm not sure we can assume anything more than we currently do.

Comment From: andrej-urvantsev

I'm not sure we can assume anything more than we currently do

I understand. The only problem as I see it: bootBuildImage "just works" with Docker, but for PodMan it requires a little bit of more effort, because docker integration looks for unix:///var/run/docker.sock by default but does not look for unix:///run/user/${UID}/podman/podman.sock as a fallback.

That could be not a problem in Spring Boot gradle/maven plugin per se, but in whatever library is used for docker integration.

My point is that bootBuildImage should "just work" with both Docker and PodMan - because if we don't look into special cases(like DOCKER_HOST is defined or minikube), then the only difference is that unix socket path.

But I don't insist - I can live with that workaround :)

Comment From: scottfrederick

When discussing the idea of testing for fallback connections to a Docker-compatible daemon, we should also consider the Colima project, which provides another alternative similar in some ways to podman. Colima does not provide a CLI, but it's expected that the docker CLI is used with Colima. On Linux, the socket provided by Colima for connection to the daemon can be discovered using the command docker context inspect colima -f '{{.Endpoints.docker.Host}}'. A typical path for the socket is unix:///$HOME/.colima/docker.sock.

Setting the DOCKER_HOST environment variable to the socket location provided by Colima is sufficient for Spring Boot to interact with the Colima daemon (provided the daemon API is not secured with SSL/TLS), or the socket can be set explicitly in the build configuration with the docker.host parameter.