From [1]:
Podman is a daemonless, open source, Linux native tool designed to make it easy to find, run, build, share and deploy applications using Open Containers Initiative (OCI) Containers and Container Images.
I use it to set up simple LAMP (linux/apache/mysql/php) servers for web development. I want to be able to run the containers without needing root. I want the containers to run on 64-bit x86 systems and on raspberry pi 5 systems (arm64 v8 architecture). All the examples below work on both architectures unless noted.
sudo pacman -S podman buildah
(during installation, it may state that there are 3 providers available for oci-runtime - crun, krun or runc - I chose crun based on https://wiki.archlinux.org/title/Podman#Runtimes).
During installation, optional dependencies for podman were listed:
apparmor: for AppArmor support
btrfs-progs: support btrfs backend devices
fuse-overlayfs: for deprecated storage driver in rootless environment
slirp4netns: for alternative rootless network support
podman-compose: for docker-compose compatibility
podman-docker: for Docker-compatible CLI
I have none installed (at the moment).
Find an apache server image:
podman search docker.io/httpd --filter=is-official
Expect output like:
NAME DESCRIPTION
docker.io/library/httpd The Apache HTTP Server Project
(will choose docker.io/library/httpd).
Create a container with this image:
podman run -d -p 127.0.0.1:8000:80 docker.io/library/httpd
The container will be stored under
~/.local/share/containers/ (but see https://github.com/containers/podman/blob/main/docs/tutorials/rootless_tutorial.md#user-configuration-files).
See https://hub.docker.com/_/httpd for more details on
httpd images.
The -d flag means run the container in the background
and print the container ID. The -p 127.0.0.1:8000:80 maps
tcp port 80 on the container (which is what the apache server listens
on) to 8000 on the host (the localhost interface only). To map to all
interfaces on the host (so the page can be accessed from another host),
leave off the 127.0.0.1:.
Check it is running:
podman ps
with expected output like:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a6aba01359ea docker.io/library/httpd:latest httpd-foreground 14 seconds ago Up 15 seconds 0.0.0.0:8000->80/tcp focused_dirac
(use podman ps -a to show all containers, including
stopped ones).
Test the web server:
curl http://localhost:8000
with expected output:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>It works! Apache httpd</title>
</head>
<body>
<p>It works!</p>
</body>
</html>
podman help
podman run --help
(similar for other podman commands).
podman inspect -l
gives lots of information about the latest container
(-l). Replace -l with the name of the
container, as reported by podman ps, if needed.
podman logs -l
shows the latest container’s logs.
podman top -l
shows the output of top for the latest container.
podman stop -l
stops the latest container. Run podman ps -a to confirm
it is stopped.
podman rm -l
to remove the latest container (confirm with
podman ps -a, which should no longer list that
container).
List all images on machine:
podman images
Remove an image:
podman image rm <image id>
where <image id> is as shown by
podman images.
Find image:
podman search docker.io/alpine --filter=is-official
Run an image of alpine linux:
podman run --rmi -it docker.io/library/alpine
-it means a (pseudo) terminal will be started on the
container and our stdin will be connected to that terminal’s stdin.
--rmi means the container and image will be removed when
exiting the container (--rm will just remove the
container). See https://hub.docker.com/_/alpine for more details on this
image.
Check can ping remote hosts:
ping 8.8.8.8
Check dns resolving works:
ping dns.google.
A LAMP server is a web server running linux/apache/mysql (or mariadb)/php.
Will create a “pod” (ie a group of containers). One container will have an apache server with php. The other will run mariadb. Specific (old) version of php and mariadb are chosen. Mariadb (not mysql) is chosen as official arm64 images do not exist for mysql for the versions I want.
See https://hub.docker.com/_/php and https://hub.docker.com/_/mariadb for more details on the php and mariadb images.
mkdir -p ~/podman/php7.4.25-apache-mariadb10.4
cd ~/podman/php7.4.25-apache-mariadb10.4
# Pull specific version of php/apache
id=$(buildah from docker.io/library/php:7.4.25-apache)
# Choose the php environment (development or production), eg
buildah run $id /bin/sh -c 'cp $PHP_INI_DIR/php.ini-development $PHP_INI_DIR/php.ini'
# Enable the php mysqli extension
buildah run $id docker-php-ext-install mysqli
# Commit image.
# The committed image will be called php_7.4.25-apache-dev - "buildah images" will list it with a "REPOSITORY" value of localhost/php_7.4.25-apache-dev
buildah commit $id php_7.4.25-apache-dev
mkdir html
# Start a container (named 'php7.4.25-apache-test') from this image. Make it part of a new pod with the name 'php7.4.25-apache-mariadb10.4-test'.
# Make /var/www/html on the container available as ./html on the host. Make apache available on port 8000 on the host, and mariadb available
# on port 33000 (both on localhost interface only). Remove the "127.0.0.1:" to make the ports available on all interfaces.
podman run -d --pod new:php7.4.25-apache-mariadb10.4-test --name php7.4.25-apache-test -p 127.0.0.1:8000:80 -p 127.0.0.1:33000:3306 --volume ./html:/var/www/html php_7.4.25-apache-dev
# Pull latest 10.4 version of mariadb and commit as new image.
id=$(buildah from docker.io/library/mariadb:10.4)
buildah commit $id mariadb_10.4
mkdir mysql
# Start a mariadb container (named 'mariadb10.4-test') and add it to the pod
podman run -d --pod php7.4.25-apache-mariadb10.4-test --name mariadb10.4-test --env MYSQL_ROOT_PASSWORD=mariadbpwd --env MYSQL_USER=testuser --env MYSQL_PASSWORD=testpwd --env MYSQL_DATABASE=testdb --volume ./mysql:/var/lib/mysql mariadb_10.4
The --env settings set up the db root password, a second
user testuser, and a db testdb.
testuser only has access to this db. This is only done if
the mysql/ directory is empty (ie the databases have not
yet been created).
(Useful pod-related commands: podman pod ps (list pods),
podman pod stop <podname>,
podman pod start <podname>,
podman pod rm <podname> (removes all containers in
the pod as well as the pod).)
Create html/index.php with contents:
<?php phpinfo() ?>
and point a browser (eg links if eg logged in remotely
to rpi5) at http://localhost:8000/ (should show a dump of
the php setup).
Access the db (on Arch Linux, install package
mariadb-clients on the host):
mariadb --user=testuser --password=testpwd --port=33000 --skip-ssl
Run eg
show databases;
quit
to list the dbs this user has access to (should be
information_schema and testdb in this
example), then quit.
Can log in to either container with
podman exec -it php7.4.25-apache-test /bin/bash
or
podman exec -it mariadb10.4-test /bin/bash
(the container names following -it matching the names
given with --name above).
Create html/show_dbs.php with the contents:
<?php
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); // will throw a mysqli_sql_exception on error
$mysqli = new mysqli('127.0.0.1', 'testuser', 'testpwd'); // not 'localhost' (which will try to use a socket to connect) - '127.0.0.1' forces a network connection
$result = $mysqli->query('SHOW DATABASES');
$rows = $result->fetch_all(MYSQLI_ASSOC);
print_r($rows);
$mysqli->close();
?>
Browse to http://localhost:8000/show_dbs.php. The page
should contain a list of the databases in the mysql container. In the
above setup, it would list:
Array ( [0] => Array ( [Database] => information_schema ) [1] => Array ( [Database] => testdb ) )
The mysql/ directory and its contents in the LAMP
server setup will not be owned by the user running podman once the pod
starts running (as its contents are created by the mysql
user within the container, and its userid is mapped to a userid on the
host system determined by /etc/subuid - see
man podman-create (search for --userns
option)).
Using podman unshare allows commands to be run within
the container’s namespace.
So eg to delete the contents of the mysql/ directory, run
podman unshare rm -rf mysql/*