Skip to content

Commit 1f420b3

Browse files
committed
RFCv3: Add support for Podman as container engine alternative to Docker
This RFC implements Podman support for Armbian builds to address mounting issues experienced with Podman while maintaining Docker compatibility. Key changes: - Auto-detect container engine: prefers Docker, falls back to Podman - Use sudo with Podman (runs in root mode - not more secure than Docker) - Add required mount options (suid,dev) and --network host for Podman - Update all docker commands to use dynamic DOCKER_COMMAND variable - Add hint for Fedora to install binfmt dependencies - Updated README.md Technical notes: - Podman runs with sudo/root privileges, so security model matches Docker - Requires sudo access when using Podman (security consideration) - Solves Podman-specific mount permission and networking issues - Maintains full backward compatibility with existing Docker workflows - May need documentation updates
1 parent 9e251e8 commit 1f420b3

File tree

1 file changed

+50
-31
lines changed

1 file changed

+50
-31
lines changed

lib/functions/host/docker.sh

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -42,49 +42,68 @@ function get_docker_info_once() {
4242
if [[ -z "${DOCKER_INFO}" ]]; then
4343
declare -g DOCKER_INFO
4444
declare -g DOCKER_IN_PATH="no"
45-
declare -g DOCKER_IS_PODMAN
46-
47-
# if "docker" is in the PATH...
45+
declare -g DOCKER_IS_PODMAN=""
46+
declare -g DOCKER_COMMAND="docker"
47+
declare -g DOCKER_NETWORK=""
48+
49+
# Detect container engine - prefer Docker for compatibility, but support Podman
50+
# Podman support requires additional host-side setup on some distributions:
51+
#
52+
# For Fedora/RHEL/derivatives, install QEMU user-mode emulation on the HOST:
53+
# sudo dnf install qemu-user-binfmt qemu-user-static-aarch64 \
54+
# qemu-user-static-arm qemu-user-static-riscv
55+
#
56+
# This is required because Podman relies on host binfmt_misc handlers with the "F" (Fix) flag
57+
# for cross-architecture builds. Docker can work with QEMU inside containers, but host installation
58+
# is recommended for both engines.
4859
if [[ -n "$(command -v docker)" ]]; then
4960
display_alert "Docker is in the path" "Docker in PATH" "debug"
61+
DOCKER_COMMAND="docker"
5062
DOCKER_IN_PATH="yes"
63+
# Check if 'docker' is actually podman (e.g., podman-docker package)
64+
if docker --version | grep -q podman; then
65+
DOCKER_IS_PODMAN="yes"
66+
display_alert "Podman detected" "docker command is podman alias" "info"
67+
fi
68+
elif [[ -n "$(command -v podman)" ]]; then
69+
# Standalone podman (no docker alias) - requires sudo for privileged operations
70+
DOCKER_COMMAND="sudo podman"
71+
DOCKER_NETWORK="--network host"
72+
DOCKER_IS_PODMAN="yes"
73+
display_alert "Podman detected" "Using standalone Podman with sudo" "info"
5174
fi
5275

5376
# Shenanigans to go around error control & capture output in the same effort.
54-
DOCKER_INFO="$({ docker info 2> /dev/null && echo "DOCKER_INFO_OK"; } || true)"
77+
DOCKER_INFO="$({ $DOCKER_COMMAND info 2> /dev/null && echo "DOCKER_INFO_OK"; } || true)"
5578
declare -g -r DOCKER_INFO="${DOCKER_INFO}" # readonly
5679

57-
if docker --version | grep -q podman; then
58-
DOCKER_IS_PODMAN="yes"
59-
# when `docker` is a shim to `podman`, it will report its version as "podman version #.#.#"
60-
else
61-
DOCKER_IS_PODMAN=""
62-
fi
63-
declare -g -r DOCKER_IS_PODMAN="${DOCKER_IS_PODMAN}" # readonly
64-
65-
6680
declare -g DOCKER_INFO_OK="no"
6781
if [[ "${DOCKER_INFO}" =~ "DOCKER_INFO_OK" ]]; then
6882
DOCKER_INFO_OK="yes"
6983
fi
7084
declare -g -r DOCKER_INFO_OK="${DOCKER_INFO_OK}" # readonly
85+
declare -g -r DOCKER_IS_PODMAN="${DOCKER_IS_PODMAN}"
86+
declare -g -r DOCKER_COMMAND="${DOCKER_COMMAND}"
87+
declare -g -r DOCKER_NETWORK="${DOCKER_NETWORK}"
7188
fi
7289
return 0
7390
}
7491

7592
# Usage: if is_docker_ready_to_go; then ...; fi
93+
# This function checks if a container engine (Docker or Podman) is available and functional.
94+
# It prefers Docker for compatibility but will automatically fall back to Podman if Docker is not found.
7695
function is_docker_ready_to_go() {
7796
# For either Linux or Darwin.
7897
# Gotta tick all these boxes:
79-
# 0) NOT ALREADY UNDER DOCKER.
80-
# 1) can find the `docker` command in the path, via command -v
81-
# 2) can run `docker info` without errors
98+
# 0) NOT ALREADY UNDER DOCKER/PODMAN.
99+
# 1) can find the `docker` or `podman` command in the path, via command -v
100+
# 2) can run `docker info` or `podman info` without errors
82101
if [[ "${ARMBIAN_RUNNING_IN_CONTAINER}" == "yes" ]]; then
83102
display_alert "Can't use Docker" "Actually ALREADY UNDER DOCKER!" "debug"
84103
return 1
85104
fi
86-
if [[ -z "$(command -v docker)" ]]; then
87-
display_alert "Can't use Docker" "docker command not found" "debug"
105+
if [[ -z "$(command -v docker)" ]] && [[ -z "$(command -v podman)" ]]; then
106+
display_alert "Can't use Docker/Podman" "neither docker nor podman command found" "debug"
88107
return 1
89108
fi
90109

@@ -107,19 +126,19 @@ function cli_handle_docker() {
107126
# Purge Armbian Docker images
108127
if [[ "${1}" == dockerpurge && -f /etc/debian_version ]]; then
109128
display_alert "Purging Armbian Docker containers" "" "wrn"
110-
docker container ls -a | grep armbian | awk '{print $1}' | xargs docker container rm &> /dev/null
111-
docker image ls | grep armbian | awk '{print $3}' | xargs docker image rm &> /dev/null
129+
$DOCKER_COMMAND container ls -a | grep armbian | awk '{print $1}' | xargs $DOCKER_COMMAND container rm &> /dev/null
130+
$DOCKER_COMMAND image ls | grep armbian | awk '{print $3}' | xargs $DOCKER_COMMAND image rm &> /dev/null
112131
# removes "dockerpurge" from $1, thus $2 becomes $1
113132
shift
114-
set -- "docker" "$@"
133+
set -- $DOCKER_COMMAND "$@"
115134
fi
116135

117136
# Docker shell
118137
if [[ "${1}" == docker-shell ]]; then
119138
# this swaps the value of $1 with 'docker', and life continues
120139
shift
121140
SHELL_ONLY=yes
122-
set -- "docker" "$@"
141+
set -- $DOCKER_COMMAND "$@"
123142
fi
124143

125144
}
@@ -336,7 +355,7 @@ function docker_cli_build_dockerfile() {
336355

337356
if [[ "${do_force_pull}" == "no" ]]; then
338357
# Check if the base image is up to date.
339-
local_image_sha="$(docker images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
358+
local_image_sha="$($DOCKER_COMMAND images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
340359
display_alert "Checking if base image exists at all" "local_image_sha: '${local_image_sha}'" "debug"
341360
if [[ -n "${local_image_sha}" ]]; then
342361
display_alert "Armbian docker image" "already exists: ${DOCKER_ARMBIAN_BASE_IMAGE}" "info"
@@ -349,10 +368,10 @@ function docker_cli_build_dockerfile() {
349368
if [[ "${do_force_pull:-yes}" == "yes" ]]; then
350369
display_alert "Pulling" "${DOCKER_ARMBIAN_BASE_IMAGE}" "info"
351370
local pull_failed="yes"
352-
run_host_command_logged docker pull "${DOCKER_ARMBIAN_BASE_IMAGE}" && pull_failed="no"
371+
run_host_command_logged $DOCKER_COMMAND pull "${DOCKER_ARMBIAN_BASE_IMAGE}" && pull_failed="no"
353372

354373
if [[ "${pull_failed}" == "no" ]]; then
355-
local_image_sha="$(docker images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
374+
local_image_sha="$($DOCKER_COMMAND images --no-trunc --quiet "${DOCKER_ARMBIAN_BASE_IMAGE}")"
356375
display_alert "New local image sha after pull" "local_image_sha: ${local_image_sha}" "debug"
357376
# print current date and time in epoch format; touches mtime of file
358377
echo "${DOCKER_ARMBIAN_BASE_IMAGE}|${local_image_sha}|$(date +%s)" >> "${docker_marker_dir}"/last-pull
@@ -373,7 +392,7 @@ function docker_cli_build_dockerfile() {
373392
display_alert "Building" "Dockerfile via '${DOCKER_BUILDX_OR_BUILD[*]}'" "info"
374393

375394
BUILDKIT_COLORS="run=123,20,245:error=yellow:cancel=blue:warning=white" \
376-
run_host_command_logged docker "${DOCKER_BUILDX_OR_BUILD[@]}" -t "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" -f "${SRC}"/Dockerfile "${SRC}"
395+
run_host_command_logged $DOCKER_COMMAND "${DOCKER_BUILDX_OR_BUILD[@]}" -t "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" -f "${SRC}"/Dockerfile "${SRC}"
377396
}
378397

379398
function docker_cli_prepare_launch() {
@@ -521,7 +540,7 @@ function docker_cli_prepare_launch() {
521540
bind)
522541
display_alert "Mounting" "bind mount for '${MOUNT_DIR}'" "debug"
523542
mkdir -p "${SRC}/${MOUNT_DIR}"
524-
DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}")
543+
DOCKER_ARGS+=("--mount" "type=bind,source=${SRC}/${MOUNT_DIR},target=${DOCKER_ARMBIAN_TARGET_PATH}/${MOUNT_DIR}${DOCKER_IS_PODMAN:+,exec,dev}")
525544
;;
526545
namedvolume)
527546
display_alert "Mounting" "named volume id '${volume_id}' for '${MOUNT_DIR}'" "debug"
@@ -598,13 +617,13 @@ function docker_cli_launch() {
598617
# The amount of privileges and capabilities given is a bare minimum needed for losetup to work
599618
if [[ ! -e /dev/loop0 ]]; then
600619
display_alert "Running losetup in a temporary container" "because no loop devices exist" "info"
601-
run_host_command_logged docker run --rm --privileged --cap-add=MKNOD "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /usr/sbin/losetup -f
620+
run_host_command_logged $DOCKER_COMMAND run $DOCKER_NETWORK --rm --privileged --cap-add=MKNOD "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /usr/sbin/losetup -f
602621
fi
603622

604623
display_alert "-----------------Relaunching in Docker after ${SECONDS}s------------------" "here comes the 🐳" "info"
605624

606625
local -i docker_build_result
607-
if docker run "${DOCKER_ARGS[@]}" "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /bin/bash "${DOCKER_ARMBIAN_TARGET_PATH}/compile.sh" "${ARMBIAN_CLI_FINAL_RELAUNCH_ARGS[@]}"; then
626+
if $DOCKER_COMMAND run $DOCKER_NETWORK "${DOCKER_ARGS[@]}" "${DOCKER_ARMBIAN_INITIAL_IMAGE_TAG}" /bin/bash "${DOCKER_ARMBIAN_TARGET_PATH}/compile.sh" "${ARMBIAN_CLI_FINAL_RELAUNCH_ARGS[@]}"; then
608627
docker_build_result=$? # capture exit code of test done in the line above.
609628
display_alert "-------------Docker run finished after ${SECONDS}s------------------------" "🐳 successful" "info"
610629
else
@@ -636,8 +655,8 @@ function docker_purge_deprecated_volumes() {
636655
for mountpoint in "${ARMBIAN_MOUNTPOINTS_DEPRECATED[@]}"; do
637656
local volume_id="armbian-${mountpoint//\//-}"
638657
display_alert "Purging deprecated Docker volume" "${volume_id}" "info"
639-
if docker volume inspect "${volume_id}" &> /dev/null; then
640-
run_host_command_logged docker volume rm "${volume_id}"
658+
if $DOCKER_COMMAND volume inspect "${volume_id}" &> /dev/null; then
659+
run_host_command_logged $DOCKER_COMMAND volume rm "${volume_id}"
641660
display_alert "Purged deprecated Docker volume" "${volume_id} OK" "info"
642661
else
643662
display_alert "Deprecated Docker volume not found" "${volume_id} OK" "info"

0 commit comments

Comments
 (0)