Skip to content

Conventions and Terminology

  • privileged containers: container running as root with U/GID 0 and maximum permissions.
  • unprivileged containers: container running with an U/GID different than root's 0.
  • ephemeral containers: when the process running inside the container exists, the container gets deleted automatically.
  • subgid and subuid: set of U/GIDs the user can use besides their own U/GID.

Paths

  • /usr/share/containers/ default
  • /etc/containers/ overrides
  • ${XDG_CONFIG_HOME}/containers for rootless containers

Commands

  • Create a container: podman create alpine:latest
  • Create a container with a custom name: podman create --name=alpine alpine:latest
  • Show running containers: podman ps
  • Show existing containers: podman ps -a
  • Start container: podman start alpine
  • Stop container: podman stop alpine
  • Attach to a running container. Ctrl+p Ctrl+q to detach: podman attach -ia alpine
  • Execute a command inside a running container: podman exec -it alpine /bin/sh
  • Create and start a container: podman run -d alpine:latest
  • Create, start and attach to a container: podman run -it alpine:latest
  • Create, start and attach to an ephemeral container: podman run --rm -it alpine:latest
  • Create, start and attach to an ephemeral container with another command: podman run --rm -it --entrypoint /bin/sh alpine:latest
  • Display the running processes of a container: podman top <container>
  • List images: podman images
  • Remove an image: podman rmi <name or ID>
  • Search for an image globally: podman search <string>
  • List mapped ports: podman port <container>
  • Display the changes in the filesystem caused by a container. C for changed and A for added: podman diff <container>
  • Mount and report the location of a container's filesystem: podman mount <container>
  • Umount a container's filesystem: podman unmount <container>
  • Build Dockerfile: podman build -t <image>:<tag> .
  • Export a container's filesystem to a tar file: podman export -o <output_filename> <conainer>
  • Import a container's filesystem from a tar file: podman export <filename> [<image>:<tag>]
  • Export image to file: podman save --output image.tar localhost/image
  • Import image from file: podman load --input image.tar
  • Create an image from a container: podman commit container image:latest
  • Show command output inside the container: podman logs container_name docs
  • Show intermediate images: podman image ls --all
  • Remove all images not in use by containers: podman image prune --all
  • Remove unused data: podman system prune. Stopped containers, networks not used by at least one container, dangling images and dangling build cache.
  • Show metadata describing a container: podman inspect <container>

Container options

  • --net=none --uidmap=0:0:65536 --gidmap=0:0:65536 --name=nethack --subgidname=yu --subuidname=yu --userns=private --rm -i -t --read-only --image-volume tmpfs --env --group-add --user=0 --cap-drop=ALL --read-only=true --read-only-tmpfs=false --systemd=false --userns=keep-id --security-opt=no-new-privileges --memory=2048mb --cap-add cap_sys_chroot --volume --root --runroot --systemd=false --tmpfs --group-add --user-add

rootless containers

By default you can only run podman as root until you configure subUIDs for your user. To do so run: usermod --add-subuids 1000000-1065536 --add-subgids 1000000-1065536 username. This will add username:1000000:65536 to /etc/subuid and /etc/subgid.

Now you can run rootless containers. Simply run podman run -it alpine:latest as user to create a rootless container. This is the same as podman run --uidmap=0:0:65536 -it alpine:latest UIDs will be mapped like:

container host
0 1000
1 1000000
1000 1000999
65534 65533

If you do not want to map user 0 inside the container to your user ID in the host you need to add --uidmap=0:1:65536 to the command: podman run --uidmap=0:1:65536 -it alpine:latest. With this configuration the UID map will be like:

container host
0 1000000
1 1000001
1000 1001000
65534 1065534

If you close the user session all rootless containers will be stopped by systemd. To avoid it you have to enable lingering with loginctl enable-linger. Source


Using podman in RAM

The first step is to create a tmpfs mount, I will create mine in /tmp/podman, and create some folders there:

  • root Storage root dir in which data, including images, is stored.
  • run Storage state directory where all state information is stored.
  • tmp Path to the tmp directory, for libpod runtime content.

You can temporally change the paths with podman --root /tmp/podman/root --runroot /tmp/podman/run --tmpdir /tmp/podman/tmp (this options have to be issued before a podman command like run) so the final command could look like:

podman --root /tmp/podman/root --runroot /tmp/podman/run --tmpdir /tmp/podman/tmp run --rm -it alpine:latest

And if you want to build and image:

podman --root /tmp/podman/root --runroot /tmp/podman/run --tmpdir /tmp/podman/tmp build -t image:latest .

Using podman with GUIs

Using podman 4.2.1, sway 1.7 and wlroots 0.15.1 in archlinux.

Wayland on host and Wayland in container

You need to allow the host UID mapped to the container UID that runs the app to write to the wayland and pulseaudio socket. In my case I use the user nobody inside the container so 65534 which outside is 1065534:

setfacl -m u:1065534:-w- "/run/user/1000/wayland-1"
setfacl -m u:1065534:-w- "/run/user/1000/pulse-socket"

And map the sockets as files to the container:

podman run --rm -d --uidmap=0:1:65536 \
-v /run/user/1000/pulse-socket:/1000/pulse-socket \
-v /run/user/1000/wayland-1:/1000/wayland-1 \
-v /dev/dri/renderD128:/dev/dri/renderD128 \
firefox:latest

/dev/dri/renderD128 is already accessible by all in my system.

You may need some variables inside the container:


Wayland on host and Xorg in container

Pretty much like wayland but with X stuff.

First you need an xauthority file which sway does not have AFAIK.

touch /run/user/1000/xauthority
xauth generate :0 . trusted
xhost +

And allow the UID in the container to access them:

setfacl -m u:1065534:r-- /run/user/1000/xauthority
sudo setfacl -m u:1065534:r-x /tmp/.X11-unix
sudo setfacl -m u:1065534:rwx /tmp/.X11-unix/X0
setfacl -m u:1065534:-w- "/run/user/1000/pulse-socket"

And launch the container:

podman run --rm -d --uidmap=0:1:65536 \
-v /run/user/1000/xauthority:/1000/xauthority \
-v /tmp/.X11-unix/X0:/tmp/.X11-unix/X0 \
-v /dev/dri/renderD128:/dev/dri/renderD128 \
-v /run/user/1000/pulse-socket:/1000/pulse-socket \
lagrange:latest

You may need some variables inside the container:

--env XDG_SEAT=$XDG_SEAT --env XDG_SESSION_ID=$XDG_SESSION_ID

Turn a container into an image file

Customize the container at will. Create a custom image from the container's data with: podman commit container image:latest

You can check the size, ID and tag with podman images.

After you have the image you can export it to a file with podman save --output image.tar localhost/image.

To import the image run podman load --input image.tar and start a new container podman run image:latest.


Map an unprivileged port

Source

We have a web server in a container running in port 80. We map the container to the host on port 8080. Now, to access this server on port 80 of the host, there is 3 ways.

  • The worst: net.ipv4.ip_unprivileged_port_start = 80 or even worst net.ipv4.ip_unprivileged_port_start = 0. This will allow every program to listen on privileged ports.
  • The bad: AmbientCapabilities=CAP_NET_BIND_SERVICE in systemd grants that container the possibility to listen on privileged ports.
  • The good: firewall NAT. Haven't tried the rules, just copy pasted them.
firewall-cmd --direct --add-rule ipv4 nat OUTPUT 0 -p tcp --dport=80 -o lo -j REDIRECT --to-port=8080
iptables -t nat -A OUTPUT -o lo -p tcp --dport=80 -j REDIRECT --to-port=8080
nft add rule ip nat OUTPUT oifname "lo" tcp dport 80 counter redirect to :8080

Also, there are tools like redir to redirect ports.


Errors

Container stuck in removing

podman system renumber

Docs