Die Oberfläche ist lokalisiert; Skill-Beschreibungen und -Dokumente werden per KI automatisch in Ihre Sprache übersetzt. Code, Befehle, Pfade und Fachbegriffe bleiben im Original, maßgeblich ist das Dokument des Autors.
Skills entdecken
Bereitstellung und Betrieb · SKILL.md

rhel-docker-host

rhel-docker-host

Use when setting up and managing containers on RHEL 9 (and AlmaLinux/Rocky 9) — Podman (rootless and rootful), Docker CE installation, Buildah, Skopeo, container networking (podman network, macvlan), Quadlet systemd integration, pod management, Compose with podman-compose, private registry, SELinux container contexts, and security best practices. Part of th…

Independent GitHub snapshot · Automated classification · Original documentation governs

Skill-Dokumentation

GitHub

name: rhel-docker-host description: Use when setting up and managing containers on RHEL 9 (and AlmaLinux/Rocky 9) — Podman (rootless and rootful), Docker CE installation, Buildah, Skopeo, container networking (podman network, macvlan), Quadlet systemd integration, pod management, Compose with podman-compose, private registry, SELinux container contexts, and security best practices. Part of the rhel-* skill family. family: rhel applies_when: os_family == rhel disambiguation: Standing up containers on RHEL — Podman rootless and rootful, Docker CE, Buildah, Skopeo, SELinux. Network topology itself is docker-networking.

Container Host on RHEL 9 — Podman, Buildah, Skopeo, Docker CE

Companion skill to rhel-server-admin (parent). Podman is the default container runtime on RHEL 9. Docker CE is optional and requires the upstream docker-ce repo. For Dockerfile patterns and cross-platform design, see docker-admin.


1. Podman — Installation and Rootless Setup

Podman is pre-installed on RHEL 9 minimal. Verify or install:

podman --version && podman info
sudo dnf install -y podman buildah skopeo podman-compose containernetworking-plugins
# Rootless — verify subuid/subgid ranges; if missing, add them
grep $USER /etc/subuid /etc/subgid
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 $USER
podman system migrate
sudo loginctl enable-linger $USER   # containers survive logout
# Core operations
podman run -d --name web -p 8080:80 docker.io/library/nginx:alpine
podman ps -a && podman logs -f --tail 200 web && podman exec -it web /bin/sh
podman stop web && podman rm web
podman run -d --name app --memory 512m --cpus 1.5 --pids-limit 100 myapp:latest
# Volume with SELinux label (:Z = private, :z = shared)
podman run -d --name db -v /srv/pgdata:/var/lib/postgresql/data:Z \
  -e POSTGRES_PASSWORD=secret docker.io/library/postgres:16-alpine
# Named volumes — rootless: ~/.local/share/containers/storage/volumes/
podman volume create mydata && podman volume ls

2. Podman Pods

Pods group containers sharing network/IPC/PID namespaces (like Kubernetes pods). Ports go on the pod, not individual containers.

podman pod create --name webapp -p 8080:80 -p 5432:5432
podman run -d --pod webapp --name web docker.io/library/nginx:alpine
podman run -d --pod webapp --name db -e POSTGRES_PASSWORD=secret \
  docker.io/library/postgres:16-alpine
# Containers share localhost — web can reach db at localhost:5432
podman pod ps && podman pod stop webapp && podman pod rm webapp -f
# Generate/deploy Kubernetes YAML
podman generate kube webapp > webapp.yaml
podman play kube webapp.yaml        # deploy
podman play kube webapp.yaml --down # tear down

3. Quadlet — Modern systemd Integration

Quadlet replaces the deprecated podman generate systemd. Place unit files in:

  • Rootful: /etc/containers/systemd/
  • Rootless: ~/.config/containers/systemd/
# /etc/containers/systemd/webapp.container
[Unit]
Description=Web Application Container
After=network-online.target
[Container]
Image=docker.io/library/nginx:alpine
ContainerName=webapp
PublishPort=8080:80
Volume=/srv/webapp/html:/usr/share/nginx/html:ro,Z
Environment=TZ=America/New_York
AutoUpdate=registry
Network=app-net.network
[Service]
Restart=always
TimeoutStartSec=120
[Install]
WantedBy=multi-user.target default.target
# /etc/containers/systemd/myapp.pod
[Pod]
PodName=myapp
PublishPort=8080:80
PublishPort=5432:5432
# /etc/containers/systemd/app-net.network
[Network]
Subnet=172.25.0.0/24
Gateway=172.25.0.1

Reference pod from .container with Pod=myapp.pod, network with Network=app-net.network.

sudo systemctl daemon-reload
sudo systemctl enable --now webapp.service
journalctl -u webapp.service -f
# Rootless: systemctl --user daemon-reload && systemctl --user enable --now webapp.service
# Verify Quadlet generation
/usr/libexec/podman/quadlet --dryrun          # rootful
/usr/libexec/podman/quadlet --user --dryrun   # rootless
# Auto-update (containers with AutoUpdate=registry)
sudo podman auto-update --dry-run
sudo podman auto-update
sudo systemctl enable --now podman-auto-update.timer

4. Buildah — Building OCI Images

Buildah builds OCI-compliant images without a daemon and without requiring a Dockerfile.

# Build from Containerfile (familiar workflow)
buildah bud -t myapp:v1 -f Containerfile .
# Equivalent: podman build -t myapp:v1 -f Containerfile .

# Build interactively (no Dockerfile needed)
ctr=$(buildah from docker.io/library/alpine:3.20)
buildah run $ctr -- apk add --no-cache python3 py3-pip
buildah copy $ctr ./app /opt/app
buildah config --workingdir /opt/app --cmd '["python3", "main.py"]' --port 8080 $ctr
buildah commit $ctr myapp:v1 && buildah rm $ctr

# Build from scratch (minimal image, no base OS)
ctr=$(buildah from scratch)
buildah copy $ctr ./mystaticbinary /app
buildah config --entrypoint '["/app"]' $ctr
buildah commit $ctr myapp:minimal && buildah rm $ctr

# Push
buildah push myapp:v1 docker://registry.home.lab:5000/myapp:v1

5. Skopeo — Inspect and Copy Images

Skopeo works with remote images without pulling to local storage.

skopeo inspect docker://docker.io/library/nginx:alpine
skopeo list-tags docker://docker.io/library/nginx
# Copy between registries (no local pull)
skopeo copy docker://docker.io/library/nginx:alpine \
  docker://registry.home.lab:5000/nginx:alpine
# Mirror entire repo (all tags)
skopeo sync --src docker --dest docker \
  docker.io/library/nginx registry.home.lab:5000/mirror/nginx
# Export to archive / OCI layout
skopeo copy docker://docker.io/library/alpine:3.20 docker-archive:/tmp/alpine.tar:alpine:3.20
skopeo delete docker://registry.home.lab:5000/myapp:old
# Auth stored in ${XDG_RUNTIME_DIR}/containers/auth.json
podman login docker.io && podman login registry.home.lab:5000

6. Docker CE — Optional Installation

Docker CE is NOT default on RHEL 9. Only install when explicitly required.

sudo dnf remove -y podman-docker runc 2>/dev/null
sudo dnf install -y dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/rhel/docker-ce.repo
# AlmaLinux/Rocky: use centos repo instead
# sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf install -y docker-ce docker-ce-cli containerd.io \
  docker-buildx-plugin docker-compose-plugin
sudo systemctl enable --now docker
sudo usermod -aG docker $USER && newgrp docker

daemon.json (/etc/docker/daemon.json):

{
  "storage-driver": "overlay2",
  "log-driver": "json-file",
  "log-opts": { "max-size": "20m", "max-file": "5", "compress": "true" },
  "default-address-pools": [{ "base": "172.20.0.0/16", "size": 24 }],
  "live-restore": true,
  "userland-proxy": false
}

Coexistence notes: Podman and Docker use separate storage — images are not shared. Remove podman-docker before installing Docker CE. Podman socket (sudo systemctl enable --now podman.socket) can emulate the Docker socket for tools expecting DOCKER_HOST.


7. Networking

# Named bridge network (DNS resolution by container name)
podman network create --subnet 172.25.0.0/24 --gateway 172.25.0.1 app-net
podman run -d --name web --network app-net -p 8080:80 docker.io/library/nginx:alpine
podman run -d --name api --network app-net myapi:latest
# web can reach api by name: curl http://api:8080
podman network connect app-net existing-container
podman network disconnect app-net existing-container

# Macvlan — containers get their own LAN IP, no port mapping
podman network create -d macvlan --subnet=10.0.1.0/24 --gateway=10.0.1.1 \
  --ip-range=10.0.1.200/29 -o parent=ens18 lan-net
podman run -d --name pihole --network lan-net --ip 10.0.1.200 docker.io/pihole/pihole
# Host-to-macvlan shim (required for host to reach macvlan containers)
sudo ip link add macvlan-shim link ens18 type macvlan mode bridge
sudo ip addr add 10.0.1.199/32 dev macvlan-shim && sudo ip link set macvlan-shim up
sudo ip route add 10.0.1.200/29 dev macvlan-shim

# Host networking — shares host network stack, no isolation
podman run -d --network host --name monitoring docker.io/prom/prometheus

# Port binding: -p 8080:80 (all), -p 10.0.1.50:8080:80 (specific IP), -p 127.0.0.1:8080:80 (local), -p 53:53/udp
# Firewall — RHEL uses firewalld, NOT ufw
sudo firewall-cmd --permanent --add-port=8080/tcp
sudo firewall-cmd --reload && sudo firewall-cmd --list-all
# Rootful Podman creates nftables rules; rootless uses slirp4netns/pasta (no firewall changes)

8. Compose

# podman-compose (native)
sudo dnf install -y podman-compose
podman-compose up -d && podman-compose ps && podman-compose logs -f app
# Docker Compose via Podman socket
sudo systemctl enable --now podman.socket      # rootful
systemctl --user enable --now podman.socket     # rootless
export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/podman/podman.sock
docker compose up -d

RHEL compose keys: use fully qualified images, add :z/:Z to bind-mount volumes, use named volumes for data (no label needed).

# compose.yaml
services:
  app:
    image: docker.io/library/node:20-alpine
    ports: ["8080:3000"]
    volumes: [./src:/app/src:ro,z, app-data:/app/data]
    depends_on:
      postgres: { condition: service_healthy }
    restart: unless-stopped
  postgres:
    image: docker.io/library/postgres:16-alpine
    environment: { POSTGRES_DB: myapp, POSTGRES_USER: myapp, POSTGRES_PASSWORD: changeme }
    volumes: [pg-data:/var/lib/postgresql/data]
    healthcheck: { test: ["CMD-SHELL", "pg_isready -U myapp"], interval: 10s, retries: 5 }
    restart: unless-stopped
volumes: { app-data: {}, pg-data: {} }

9. Registry Configuration and Private Registry

registries.conf

# /etc/containers/registries.conf
unqualified-search-registries = ["docker.io", "quay.io", "registry.home.lab:5000"]

[[registry]]
location = "registry.home.lab:5000"
insecure = false
[[registry.mirror]]
location = "mirror.home.lab:5000"

Deploying a Private Registry

sudo mkdir -p /opt/registry/{data,certs,auth}
openssl req -newkey rsa:4096 -nodes \
  -keyout /opt/registry/certs/registry.key \
  -x509 -days 3650 -out /opt/registry/certs/registry.crt \
  -subj "/CN=registry.home.lab" \
  -addext "subjectAltName=DNS:registry.home.lab,IP:10.0.1.50"
sudo dnf install -y httpd-tools
htpasswd -Bbn admin 'S3cur3P@ss' > /opt/registry/auth/htpasswd
sudo podman run -d --name registry -p 5000:5000 \
  -v /opt/registry/data:/var/lib/registry:Z \
  -v /opt/registry/certs:/certs:ro,Z -v /opt/registry/auth:/auth:ro,Z \
  -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.crt \
  -e REGISTRY_HTTP_TLS_KEY=/certs/registry.key \
  -e REGISTRY_AUTH=htpasswd -e REGISTRY_AUTH_HTPASSWD_REALM="Registry Realm" \
  -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
  -e REGISTRY_STORAGE_DELETE_ENABLED=true docker.io/library/registry:2
# Trust cert on RHEL clients (NOT /usr/local/share/ca-certificates like Ubuntu)
sudo cp /opt/registry/certs/registry.crt /etc/pki/ca-trust/source/anchors/
sudo update-ca-trust
podman login registry.home.lab:5000
podman tag myapp:latest registry.home.lab:5000/myapp:v1.0 && podman push registry.home.lab:5000/myapp:v1.0

For production, deploy as a Quadlet .container unit (Section 3 pattern).


10. Security

Rootless (default): Container UID 0 maps to high host UID via user namespaces. Verify: podman unshare cat /proc/self/uid_map.

SELinux: Volume labels :Z/:z auto-relabel host paths. Manual: sudo chcon -Rt container_file_t /srv/webapp/. Audit denials: sudo ausearch -m AVC -ts recent.

# Hardened container
podman run -d --name secure-app \
  --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --cap-drop ALL --cap-add NET_BIND_SERVICE \
  --security-opt=no-new-privileges:true \
  --security-opt label=type:container_t \
  --pids-limit 100 --memory 512m --cpus 1.0 --user 1001:1001 \
  myapp:latest
# Custom seccomp profile
podman run --security-opt seccomp=/path/to/custom-seccomp.json myapp
# Generate seccomp from actual syscalls
sudo dnf install -y oci-seccomp-bpf-hook
podman run --annotation io.containers.trace-syscall=of:/tmp/seccomp.json myapp
# Image signing — configure /etc/containers/policy.json
podman push --sign-by admin@example.com registry.home.lab:5000/myapp:v1
# Rootful user namespace isolation — /etc/containers/containers.conf
# [containers]
# userns = "auto"

11. Maintenance

podman system df -v                          # disk usage
podman container prune -f && podman image prune -f
podman image prune -a -f && podman volume prune -f
podman builder prune -f --keep-storage=5G

Automated Cleanup (systemd timer)

# /etc/systemd/system/podman-cleanup.service
[Unit]
Description=Podman resource cleanup
[Service]
Type=oneshot
ExecStart=/usr/bin/podman container prune -f --filter "until=72h"
ExecStart=/usr/bin/podman image prune -f --filter "until=168h"
# /etc/systemd/system/podman-cleanup.timer
[Unit]
Description=Daily Podman cleanup
[Timer]
OnCalendar=*-*-* 04:00:00
Persistent=true
[Install]
WantedBy=timers.target

sudo systemctl enable --now podman-cleanup.timer

Backup and Updates

# Volume backup via helper container
podman run --rm -v myvolume:/source:ro -v /backup:/dest:Z \
  docker.io/library/alpine tar czf /dest/myvolume-$(date +%Y%m%d).tar.gz -C /source .
# App-level dump (preferred for databases)
podman exec postgres pg_dumpall -U myapp > /backup/pg-dump-$(date +%Y%m%d).sql
# Update container tools
sudo dnf update -y podman buildah skopeo && podman system migrate

Quick Reference

TaskCommand
Disk usagepodman system df -v
Networks / volumespodman network ls / podman volume ls
Live statspodman stats
Follow logspodman logs -f --tail 200 <ctr>
Shell inpodman exec -it <ctr> bash
Build imagepodman build -t myapp:v1 .
Inspect remoteskopeo inspect docker://docker.io/library/nginx:alpine
Copy registriesskopeo copy docker://src docker://dest
K8s YAMLpodman generate kube <pod>
Quadlet dry-run/usr/libexec/podman/quadlet --dryrun
Auto-updatespodman auto-update --dry-run
SELinux denialssudo ausearch -m AVC -ts recent

Anti-Patterns

Anti-PatternWhy It FailsCorrect Approach
Installing Docker CE from docker.io repo without removing Podman firstPackage conflicts between Docker and Podman; broken container runtime; confused CLI behaviorChoose one: Podman (default RHEL) or Docker CE; remove the other cleanly; do not mix runtimes
Running Podman containers as root by defaultLoses the key security advantage of Podman's rootless mode; container escape gives root accessUse rootless Podman (podman --user); root only when host resources (ports < 1024, specific devices) require it
Not using Quadlet for systemd service managementManual podman generate systemd output drifts from container config; updates require regenerationUse Quadlet (.container, .volume, .network files in /etc/containers/systemd/); declarative and auto-updating
Ignoring SELinux labels on bind mountsSELinux denials block container access to host directories; admins disable SELinux instead of fixing labelsUse :z (shared) or :Z (private) suffix on bind mounts; or set correct SELinux context with chcon
Not configuring container log rotationContainer logs grow unbounded; /var fills up; all containers on the host stop workingSet log driver options: --log-opt max-size=10m --log-opt max-file=3 in daemon.json or per container

Related Skills

WorkloadSkill
Core RHEL adminrhel-server-admin
Dockerfile patterns, cross-platformdocker-admin
Web servers (Nginx, Apache, Caddy)rhel-web-servers
Databases (PostgreSQL, MariaDB, Redis)rhel-databases
NFS, Samba, Stratisrhel-file-storage
Prometheus, Grafana, monitoringrhel-monitoring
Networking, VPN, load balancingrhel-network-infra
GPU passthrough, Ollamarhel-ollama-nvidia
Core Docker concepts, BuildKit, Dockerfilesdocker-fundamentals
Docker networking deep divedocker-networking
Volumes, storage driversdocker-storage
Image scanning, security hardeningdocker-security
CI/CD pipelines, multi-platform buildsdocker-cicd
Advanced Compose patternsdocker-compose-patterns
Gespeicherte Skills