I've been banging my head against a wall trying to get Podman to work with Jenkins in an easy way. With RHEL moving to Podman over Docker, this opens up some unusual problems in the Jenkins space. There exists a Docker Pipeline plugin (docker-workflow-plugin) that suits the bill perfectly, but alas, it requires Docker to run. Or does it? In this guide, we'll look at getting Podman working in lieu of Docker, with the Docker plugin still working fine on the controller. Please note that this was tested on CloudBees CI with a RHEL 8 static agent, but should still work the same on OSS Jenkins. There is no guarantee of this working in any sense, so please use at your own risk and test thoroughly!

Take this example Declarative Pipeline using the Docker plugin:

pipeline {
    agent {
        docker {
            label 'podman'
            image 'maven'
        }
    }
    stages {
        stage('checkout') {
            steps {
                checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'https://github.com/jenkins-docs/simple-java-maven-app.git']])
            }
        }
        stage('run') {
            steps {
                sh "mvn clean package"
                archiveArtifacts artifacts: 'target/*.jar', followSymlinks: false
            }
        }
    }
}
Declarative Pipeline to run a Docker container and execute commands on it.

This pipeline uses the docker { } block to spin up a container on an agent with the label podman and the image of maven. It then executes commands against the container (in this case mvn clean package) and then archives the built JAR. When running this against Podman with no modifications, obviously it won't work as it's kicking off docker commands against the system. This should be fairly easily remedied when looking at the Podman article "Emulating Docker CLI with Podman". Since we're not utilizing the plugin as a Cloud (which would require the Docker API), it'll just execute the commands directly against the host OS of the Agent. Theoretically, this should work just fine.

First, create the /usr/local/bin/docker script:

#!/usr/bin/sh
[ -e /etc/containers/nodocker ] || \
echo "Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg." >&2
exec podman "$@"

Next, we need to make the /etc/containers/nodocker file to suppress the warning that it's running as Podman instead of Docker:

touch /etc/containers/nodocker

Next we need to make the docker script executable:

chmod +x /usr/local/bin/docker

By default, the Docker Plugin is going to attempt to run a command similar to this on the backend:

docker run -t -d -u 1000:1000 -w /var/lib/jenkins/workspace/podman-example-customer -v /var/lib/jenkins/workspace/podman-example-customer:/var/lib/jenkins/workspace/podman-example-customer:rw,z -v /var/lib/jenkins/workspace/podman-example-customer@tmp:/var/lib/jenkins/workspace/podman-example-customer@tmp:rw,z -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** -e ******** maven cat

This should work, but unfortunately, it hard-codes the user entry with -u 1000:1000. This is going to cause problems running Podman rootless, as it doesn't like being told which user to run as. We need to trim that, or we'll see errors similar to below in ours logs:

sh: 1: cannot create /var/lib/jenkins/workspace/podman-example-customer@tmp/durable-d61eb9e3/jenkins-log.txt: Permission denied

This occurs because the user, in this case 1000:1000, doesn't have rights to modify files on the filesystem that are mounted (in this case, the workspace and workspace tmp directories). So, how do we stop it from doing that? Since it seems it's hard-coded to use -u 1000:1000, let's trim that from our parameters being passed into the docker script. We'll have it check if it's the first instance of -u or 1000:1000 and kill them off from the list of params, and then continue as normal.

Here's a finalized script that worked for me. Note that I changed to using bash as the shell since most of this isn't POSIX compliant:

#!/usr/bin/bash
[ -e /etc/containers/nodocker ] || \
echo "Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg." >&2

declare -a finalopts
finalopts=()

USERCHECK=0
USERIDCHECK=0
for o in "$@"; do
    if [[ "$o" = "-u" && $USERCHECK = 0 ]] ; then
	    USERCHECK=1
        continue
    elif [[ "$o" = "1000:1000" && $USERIDCHECK = 0 ]] ; then
	    USERIDCHECK=1
	    continue
    fi

    finalopts+=("$o")
done

exec podman "${finalopts[@]}"

And voila! Using Maven, our build output now looks as expected!

Successfully run Maven Package command from Pipeline

Since all of these changes take place by mucking with the Docker command on the backend, no changes should need to be made to Pipelines using the Docker plugin to continue using Podman!

Using Podman with Docker Pipeline Plugin in Jenkins