๐ณ Container Management with Podman in AlmaLinux: Docker Alternative
Docker, Docker everywhereโฆ but wait, thereโs Podman! ๐ When I first heard about Podman, I thought โOh great, another container tool to learn.โ But then I discovered it runs containers WITHOUT a daemon, WITHOUT root, and is 100% Docker-compatible! Mind = blown! ๐คฏ Today Iโm showing you how to use Podman on AlmaLinux - itโs like Docker but more secure and already installed! No more Docker daemon eating your RAM. Letโs dive into the wonderful world of daemonless containers! ๐
๐ค Why Podman Over Docker?
Hereโs why Podman is becoming the go-to choice:
- ๐ Rootless Containers - Run without sudo/root!
- ๐ซ No Daemon - No background service hogging resources
- ๐ Docker Compatible - Your Docker commands just work
- ๐ฆ Pod Support - Like Kubernetes pods on your laptop
- ๐ก๏ธ SELinux Integration - Better security out of the box
- ๐ฏ Systemd Integration - Containers as system services
True story: Our Docker daemon crashed and took down 50 containers. With Podman? Each container is independent. One crashes, others keep running! ๐ช
๐ฏ What You Need
Before we start containerizing everything, ensure you have:
- โ AlmaLinux 8/9 system
- โ Regular user account (no root needed!)
- โ 2GB RAM minimum
- โ 10GB free disk space
- โ 30 minutes to master containers
- โ Excitement for rootless containers! ๐
๐ Step 1: Installing and Setting Up Podman
Letโs get Podman running!
Install Podman
# Podman is in AppStream repo - easy!
sudo dnf install -y podman
# Install additional tools
sudo dnf install -y podman-compose podman-docker buildah skopeo
# Verify installation
podman --version
# Check info
podman info
# Enable user namespaces for rootless
sudo sysctl user.max_user_namespaces=15000
echo "user.max_user_namespaces=15000" | sudo tee /etc/sysctl.d/99-rootless.conf
Configure Rootless Podman
# Setup rootless (as regular user, NOT root!)
podman system migrate
# Check subuid/subgid mappings
cat /etc/subuid
cat /etc/subgid
# Should see something like:
# yourusername:100000:65536
# Configure storage for user
mkdir -p ~/.config/containers
cat > ~/.config/containers/storage.conf << EOF
[storage]
driver = "overlay"
runroot = "/run/user/$(id -u)"
graphroot = "$HOME/.local/share/containers/storage"
EOF
# Set registries
sudo nano /etc/containers/registries.conf
# Add/verify:
unqualified-search-registries = ["docker.io", "quay.io", "registry.fedoraproject.org"]
# Test rootless
podman run hello-world
Docker Compatibility Setup
# Install podman-docker (provides docker command)
sudo dnf install -y podman-docker
# Now docker commands work!
docker ps # Actually runs podman ps
# Create docker-compose alias
echo "alias docker-compose='podman-compose'" >> ~/.bashrc
source ~/.bashrc
# Test compatibility
docker run -it alpine echo "Hello from Podman!"
๐ง Step 2: Basic Container Operations
Time to run some containers!
Running Containers
# Run a simple container
podman run -it alpine sh
# Run in background
podman run -d --name webserver nginx
# Run with port mapping
podman run -d -p 8080:80 --name myweb nginx
# Run with volume mount
podman run -v /host/path:/container/path:Z alpine ls /container/path
# Note: :Z for SELinux context
# Run with environment variables
podman run -e MY_VAR=value -e DB_HOST=localhost alpine env
# Run with resource limits
podman run --memory 512m --cpus 1 nginx
# Auto-remove after exit
podman run --rm alpine echo "I'll self-destruct!"
Managing Containers
# List running containers
podman ps
# List all containers
podman ps -a
# Stop container
podman stop container_name
# Start container
podman start container_name
# Restart container
podman restart container_name
# Remove container
podman rm container_name
# Remove all stopped containers
podman container prune
# View logs
podman logs container_name
podman logs -f container_name # Follow logs
# Execute command in running container
podman exec -it container_name bash
# Inspect container
podman inspect container_name
# View stats
podman stats
Working with Images
# Pull image
podman pull ubuntu:latest
# List images
podman images
# Search images
podman search nginx
# Remove image
podman rmi image_name
# Clean unused images
podman image prune
# Tag image
podman tag image_id myrepo/myimage:tag
# Save image to file
podman save -o image.tar nginx
# Load image from file
podman load -i image.tar
๐ Step 3: Building and Managing Images
Letโs create custom containers!
Building with Dockerfile
# Create Dockerfile
cat > Dockerfile << 'EOF'
FROM almalinux:9
RUN dnf install -y httpd
RUN echo "Hello from Podman!" > /var/www/html/index.html
EXPOSE 80
CMD ["/usr/sbin/httpd", "-DFOREGROUND"]
EOF
# Build image
podman build -t myapp:latest .
# Build with different file
podman build -f Containerfile -t myapp:v2 .
# Build with build args
podman build --build-arg VERSION=1.0 -t myapp:1.0 .
# Multi-stage build
cat > Dockerfile.multi << 'EOF'
# Build stage
FROM golang:alpine AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp
# Runtime stage
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=builder /app/myapp .
CMD ["./myapp"]
EOF
podman build -f Dockerfile.multi -t myapp:slim .
Using Buildah (Advanced)
# Create container from scratch
container=$(buildah from scratch)
# Mount container filesystem
mountpoint=$(buildah mount $container)
# Install packages
dnf install --installroot $mountpoint --releasever 9 \
--setopt=tsflags=nodocs -y coreutils bash
# Copy files
cp myapp $mountpoint/usr/local/bin/
# Configure container
buildah config --cmd "/usr/local/bin/myapp" $container
buildah config --port 8080 $container
buildah config --env APP_ENV=production $container
# Commit to image
buildah commit $container myapp:custom
# Unmount and remove working container
buildah umount $container
buildah rm $container
โ Step 4: Advanced Podman Features
Level up your container game!
Working with Pods
# Create a pod (like Kubernetes!)
podman pod create --name mypod -p 8080:80
# Add containers to pod
podman run -d --pod mypod --name web nginx
podman run -d --pod mypod --name app myapp:latest
# List pods
podman pod list
# View pod details
podman pod inspect mypod
# Stop/start entire pod
podman pod stop mypod
podman pod start mypod
# Remove pod and all containers
podman pod rm -f mypod
# Generate Kubernetes YAML from pod
podman generate kube mypod > mypod.yaml
# Play Kubernetes YAML
podman play kube mypod.yaml
Systemd Integration
# Generate systemd service for container
podman generate systemd --name mycontainer > container.service
# Install as user service
mkdir -p ~/.config/systemd/user/
podman generate systemd --name mycontainer --new \
> ~/.config/systemd/user/container.service
# Enable and start
systemctl --user daemon-reload
systemctl --user enable container.service
systemctl --user start container.service
# Auto-start on boot (lingering)
loginctl enable-linger $USER
# Generate for root service
sudo podman generate systemd --name mycontainer --new \
> /etc/systemd/system/container.service
sudo systemctl daemon-reload
sudo systemctl enable --now container.service
Container Networking
# Create custom network
podman network create mynet
# List networks
podman network ls
# Run container on custom network
podman run -d --network mynet --name db mariadb
podman run -d --network mynet --name web nginx
# Containers can reach each other by name!
podman exec web ping db
# Inspect network
podman network inspect mynet
# Connect running container to network
podman network connect mynet existing_container
# Disconnect from network
podman network disconnect mynet container_name
# Remove network
podman network rm mynet
๐ฎ Quick Examples
Example 1: Complete Web Stack ๐
#!/bin/bash
# Deploy complete LAMP stack with Podman
deploy_lamp_stack() {
echo "๐ Deploying LAMP Stack with Podman"
# Create pod
podman pod create --name lamp-pod \
-p 8080:80 \
-p 3306:3306
# Run MariaDB
podman run -d --pod lamp-pod \
--name lamp-db \
-e MYSQL_ROOT_PASSWORD=secretpass \
-e MYSQL_DATABASE=myapp \
-e MYSQL_USER=appuser \
-e MYSQL_PASSWORD=apppass \
-v lamp-db-data:/var/lib/mysql:Z \
mariadb:latest
# Wait for DB to be ready
echo "โณ Waiting for database..."
sleep 10
# Run PHP Apache
cat > index.php << 'EOF'
<?php
$conn = new mysqli("localhost", "appuser", "apppass", "myapp");
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
echo "<h1>๐ Podman LAMP Stack Working!</h1>";
echo "<p>Connected to MariaDB successfully!</p>";
phpinfo();
?>
EOF
# Create custom PHP image
cat > Dockerfile.php << 'EOF'
FROM php:apache
RUN docker-php-ext-install mysqli pdo pdo_mysql
COPY index.php /var/www/html/
EOF
podman build -f Dockerfile.php -t lamp-php .
# Run PHP container
podman run -d --pod lamp-pod \
--name lamp-web \
lamp-php
echo "โ
LAMP Stack deployed!"
echo "๐ Access at: http://localhost:8080"
echo ""
echo "๐ง Management commands:"
echo " View logs: podman pod logs lamp-pod"
echo " Stop: podman pod stop lamp-pod"
echo " Start: podman pod start lamp-pod"
echo " Remove: podman pod rm -f lamp-pod"
}
deploy_lamp_stack
Example 2: CI/CD Pipeline Runner ๐ง
#!/bin/bash
# Run CI/CD jobs in containers
run_ci_pipeline() {
PROJECT_NAME=$1
REPO_URL=$2
echo "๐ง Running CI Pipeline for $PROJECT_NAME"
# Create workspace
WORKSPACE="/tmp/ci-$PROJECT_NAME-$(date +%s)"
mkdir -p "$WORKSPACE"
# Clone repository
git clone "$REPO_URL" "$WORKSPACE/source"
# Build stage
echo "๐ฆ Build Stage"
podman run --rm \
-v "$WORKSPACE/source:/workspace:Z" \
-w /workspace \
node:alpine \
sh -c "npm install && npm run build"
# Test stage
echo "๐งช Test Stage"
podman run --rm \
-v "$WORKSPACE/source:/workspace:Z" \
-w /workspace \
node:alpine \
sh -c "npm test"
# Security scan
echo "๐ Security Scan"
podman run --rm \
-v "$WORKSPACE/source:/src:Z" \
aquasec/trivy fs /src
# Build Docker image
echo "๐ณ Building Container Image"
cd "$WORKSPACE/source"
podman build -t "$PROJECT_NAME:latest" .
# Push to registry (if configured)
if [ ! -z "$REGISTRY_URL" ]; then
podman tag "$PROJECT_NAME:latest" "$REGISTRY_URL/$PROJECT_NAME:latest"
podman push "$REGISTRY_URL/$PROJECT_NAME:latest"
fi
# Cleanup
rm -rf "$WORKSPACE"
echo "โ
CI Pipeline Complete!"
}
# Usage
run_ci_pipeline "myapp" "https://github.com/user/repo.git"
Example 3: Development Environment Manager ๐ป
#!/bin/bash
# Manage development environments with Podman
create_dev_env() {
ENV_NAME=$1
LANGUAGE=$2
echo "๐ป Creating Dev Environment: $ENV_NAME ($LANGUAGE)"
case $LANGUAGE in
python)
IMAGE="python:3.11"
PACKAGES="pip install flask django pytest"
PORT="5000"
;;
node)
IMAGE="node:18"
PACKAGES="npm install -g express nodemon jest"
PORT="3000"
;;
go)
IMAGE="golang:1.20"
PACKAGES="go install github.com/gin-gonic/gin@latest"
PORT="8080"
;;
*)
echo "โ Unsupported language: $LANGUAGE"
return 1
;;
esac
# Create persistent volume for code
podman volume create "${ENV_NAME}-code"
# Create development container
podman run -d \
--name "dev-$ENV_NAME" \
-v "${ENV_NAME}-code:/workspace:Z" \
-v "$HOME/.ssh:/root/.ssh:ro,Z" \
-p "$PORT:$PORT" \
-w /workspace \
--hostname "$ENV_NAME" \
"$IMAGE" \
tail -f /dev/null
# Install packages
echo "๐ฆ Installing packages..."
podman exec "dev-$ENV_NAME" sh -c "$PACKAGES"
# Create helper script
cat > "dev-$ENV_NAME.sh" << EOF
#!/bin/bash
# Connect to $ENV_NAME development environment
case \$1 in
shell)
podman exec -it "dev-$ENV_NAME" bash
;;
stop)
podman stop "dev-$ENV_NAME"
;;
start)
podman start "dev-$ENV_NAME"
;;
logs)
podman logs -f "dev-$ENV_NAME"
;;
destroy)
podman rm -f "dev-$ENV_NAME"
podman volume rm "${ENV_NAME}-code"
rm "dev-$ENV_NAME.sh"
;;
*)
echo "Usage: ./dev-$ENV_NAME.sh {shell|stop|start|logs|destroy}"
;;
esac
EOF
chmod +x "dev-$ENV_NAME.sh"
echo "โ
Development environment ready!"
echo "๐ Use ./dev-$ENV_NAME.sh to manage"
echo "๐ Access shell: ./dev-$ENV_NAME.sh shell"
}
# Create multiple environments
create_dev_env "webapp" "python"
create_dev_env "api" "node"
create_dev_env "microservice" "go"
Example 4: Container Health Monitor ๐ฅ
#!/bin/bash
# Monitor and auto-heal containers
monitor_containers() {
LOG_FILE="/var/log/podman-monitor.log"
echo "๐ฅ Container Health Monitor Started - $(date)" | tee -a "$LOG_FILE"
while true; do
# Check all containers
for container in $(podman ps -q); do
name=$(podman inspect $container --format '{{.Name}}')
state=$(podman inspect $container --format '{{.State.Status}}')
health=$(podman inspect $container --format '{{.State.Health.Status}}' 2>/dev/null || echo "none")
# Check if container is healthy
if [ "$health" = "unhealthy" ]; then
echo "โ ๏ธ Container $name is unhealthy!" | tee -a "$LOG_FILE"
# Try to heal
echo "๐ง Attempting to heal $name..." | tee -a "$LOG_FILE"
podman restart $container
sleep 30
# Check again
new_health=$(podman inspect $container --format '{{.State.Health.Status}}' 2>/dev/null)
if [ "$new_health" = "healthy" ]; then
echo "โ
Container $name healed!" | tee -a "$LOG_FILE"
else
echo "โ Container $name still unhealthy, may need manual intervention" | tee -a "$LOG_FILE"
# Send alert
echo "Container $name is unhealthy on $(hostname)" | \
mail -s "Container Health Alert" [email protected]
fi
fi
# Check resource usage
stats=$(podman stats --no-stream --format json $container)
cpu=$(echo $stats | jq -r '.[0].cpu_percent' | sed 's/%//')
mem=$(echo $stats | jq -r '.[0].mem_percent' | sed 's/%//')
if (( $(echo "$cpu > 90" | bc -l) )); then
echo "โ ๏ธ High CPU usage for $name: ${cpu}%" | tee -a "$LOG_FILE"
fi
if (( $(echo "$mem > 90" | bc -l) )); then
echo "โ ๏ธ High memory usage for $name: ${mem}%" | tee -a "$LOG_FILE"
fi
done
# Check for stopped containers that should be running
for container in $(podman ps -a --filter "status=exited" -q); do
name=$(podman inspect $container --format '{{.Name}}')
restart_policy=$(podman inspect $container --format '{{.HostConfig.RestartPolicy.Name}}')
if [ "$restart_policy" = "always" ] || [ "$restart_policy" = "unless-stopped" ]; then
echo "๐ Restarting stopped container $name" | tee -a "$LOG_FILE"
podman start $container
fi
done
sleep 60
done
}
# Run monitor
monitor_containers
๐จ Fix Common Problems
Problem 1: Rootless Containers Not Working โ
Permission denied errors?
# Check subuid/subgid
grep $USER /etc/subuid /etc/subgid
# If missing, add them
sudo usermod --add-subuids 100000-165535 $USER
sudo usermod --add-subgids 100000-165535 $USER
# Enable user namespaces
sudo sysctl user.max_user_namespaces=15000
# Logout and login again
exit
# Log back in
# Reset storage
podman system reset
Problem 2: Cannot Pull Images โ
Registry access issues?
# Check registries
podman info | grep -A5 registries
# Login to registry
podman login docker.io
# Use full image path
podman pull docker.io/library/nginx:latest
# Behind proxy?
export HTTP_PROXY=http://proxy:8080
export HTTPS_PROXY=http://proxy:8080
Problem 3: SELinux Blocking Volumes โ
Permission denied on mounted volumes?
# Use :Z flag for private label
podman run -v /host/path:/container/path:Z image
# Or :z for shared label
podman run -v /host/path:/container/path:z image
# Check SELinux context
ls -laZ /host/path
# Temporarily disable SELinux (not recommended)
sudo setenforce 0
Problem 4: Containers Canโt Communicate โ
Network isolation issues?
# Create shared network
podman network create shared
# Run containers on same network
podman run -d --network shared --name app1 nginx
podman run -d --network shared --name app2 alpine
# Test connectivity
podman exec app2 ping app1
# Check firewall
sudo firewall-cmd --list-all
๐ Simple Commands Summary
Task | Command |
---|---|
๐ Run container | podman run image |
๐ List containers | podman ps |
๐ Stop container | podman stop name |
๐๏ธ Remove container | podman rm name |
๐ผ๏ธ List images | podman images |
๐จ Build image | podman build -t tag . |
๐ Create network | podman network create name |
๐ฆ Create pod | podman pod create name |
๐ View stats | podman stats |
๐ก Tips for Success
- Go Rootless ๐ - Safer than Dockerโs root requirement
- Use Pods ๐ฏ - Group related containers
- Systemd Integration ๐ค - Auto-start containers at boot
- Alias Docker ๐ - Smooth transition from Docker
- Volume Labels ๐ท๏ธ - Always use :Z for SELinux
- Regular Cleanup ๐งน - Prune unused images/containers
Pro tip: I replaced our entire Docker setup with Podman in production. Zero downtime, better security, and our devs didnโt even notice - their docker commands just worked! ๐
๐ What You Learned
Youโre now a container ninja! You can:
- โ Install and configure Podman
- โ Run rootless containers safely
- โ Build custom container images
- โ Manage pods like Kubernetes
- โ Integrate with systemd
- โ Create container networks
- โ Migrate from Docker seamlessly
๐ฏ Why This Matters
Podman gives you:
- ๐ Better security (rootless!)
- ๐พ No daemon overhead
- ๐ฏ Kubernetes compatibility
- ๐ Easy Docker migration
- ๐ก๏ธ SELinux integration
- ๐ช Production-ready containers
Last week, a clientโs Docker daemon crashed and took down their entire container infrastructure. We migrated them to Podman - now each container runs independently. One crashes? Others keep running. They havenโt had downtime since! ๐
Remember: Containers are the future, and Podman is leading the charge. Rootless, daemonless, and secure by default. The way containers should be! ๐ณ
Happy containerizing! May your pods be healthy and your images be slim! ๐โจ