fedora-csb-system-manager

Run Workflow

Manage container lifecycle, debug running containers, and orchestrate multi-container applications with Docker Compose.

Quick Start

Common run patterns:

# Start a container
docker run -d --name myapp -p 8080:80 myapp:latest

# Start with logs following
docker run -d --name myapp myapp:latest && docker logs -f myapp

# Start and enter shell
docker run -it --name myapp myapp:latest /bin/bash

# Start all services from compose file
docker-compose up -d

# Check running containers
docker ps

# View container logs
docker logs myapp

# Open shell in running container
docker exec -it myapp /bin/bash

Runtime detection: All commands show both Docker and Podman variants. Use DetectRuntime.sh to determine which is available.

Container Lifecycle

Starting Containers

Basic run:

docker run -d --name myapp -p 8080:80 myapp:latest
podman run -d --name myapp -p 8080:80 myapp:latest

With environment variables:

docker run -d --name myapp -e DATABASE_URL=postgres://db/myapp -e DEBUG=true myapp:latest
podman run -d --name myapp -e DATABASE_URL=postgres://db/myapp myapp:latest

With volume mount:

docker run -d --name myapp -v /host/path:/container/path myapp:latest
podman run -d --name myapp -v /host/path:/container/path myapp:latest

Interactive mode:

docker run -it --name myapp myapp:latest /bin/bash
podman run -it --name myapp myapp:latest /bin/bash

With resource limits:

# Memory limit
docker run -d --name myapp --memory 512m myapp:latest

# CPU limit
docker run -d --name myapp --cpus 1.5 myapp:latest

# Combined limits
docker run -d --name myapp --memory 512m --cpus 1.0 myapp:latest

Restart Policies

Always restart:

docker run -d --restart always --name myapp myapp:latest

Restart on failure:

docker run -d --restart on-failure:5 --name myapp myapp:latest

No restart:

docker run -d --restart no --name myapp myapp:latest

Unless stopped:

docker run -d --restart unless-stopped --name myapp myapp:latest

Listing Containers

Show running containers:

docker ps
podman ps

Show all containers (including stopped):

docker ps -a
podman ps -a

Format output:

docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
podman ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"

Stopping and Restarting

Stop container gracefully:

docker stop myapp
podman stop myapp

Stop with custom timeout:

docker stop -t 30 myapp
podman stop -t 30 myapp

Restart container:

docker restart myapp
podman restart myapp

Kill container forcefully:

docker kill myapp
podman kill myapp

Removing Containers

Remove stopped container:

docker rm myapp
podman rm myapp

Force remove running container:

docker rm -f myapp
podman rm -f myapp

Remove all stopped containers:

docker container prune
podman container prune

Remove with confirmation:

docker container prune -f

Inspecting Containers

Show detailed information:

docker inspect myapp
podman inspect myapp

Get specific field:

# Container status
docker inspect -f '{{.State.Status}}' myapp

# IP address
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# Exit code
docker inspect -f '{{.State.ExitCode}}' myapp

# Environment variables
docker inspect -f '{{.Config.Env}}' myapp

# Mounts
docker inspect -f '{{.Mounts}}' myapp

# Ports
docker inspect -f '{{.NetworkSettings.Ports}}' myapp

Show resource usage:

docker stats myapp
podman stats myapp

Debugging Containers

Viewing Logs

Show container logs:

docker logs myapp
podman logs myapp

Follow logs (real-time):

docker logs -f myapp
podman logs -f myapp

Show last N lines:

docker logs --tail 100 myapp
podman logs --tail 100 myapp

Show logs with timestamps:

docker logs -t myapp
podman logs -t myapp

Show logs since specific time:

docker logs --since 2024-01-01T10:00:00 myapp
docker logs --since 1h myapp
podman logs --since 1h myapp

Search logs:

docker logs myapp 2>&1 | grep ERROR
docker logs myapp 2>&1 | grep -i "connection failed"
podman logs myapp 2>&1 | grep ERROR

Executing Commands

Open shell in running container:

docker exec -it myapp /bin/bash
docker exec -it myapp /bin/sh  # if bash not available
podman exec -it myapp /bin/bash

Run specific command:

docker exec myapp ls -la /app
docker exec myapp ps aux
docker exec myapp cat /etc/os-release
podman exec myapp env

Run as specific user:

docker exec -u www-data -it myapp bash
docker exec -u 0 -it myapp bash  # run as root
podman exec -u 1000 -it myapp bash

Set environment variables:

docker exec -e DEBUG=true myapp python script.py
podman exec -e DEBUG=true myapp python script.py

Monitoring Resources

Real-time stats:

docker stats myapp
podman stats myapp

All containers stats:

docker stats
podman stats

Stats without streaming:

docker stats --no-stream
podman stats --no-stream

Format output:

docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}"

List processes in container:

docker top myapp
docker top myapp aux
podman top myapp

Checking Container Health

If healthcheck configured:

docker inspect -f '{{.State.Health.Status}}' myapp
docker inspect -f '{{json .State.Health}}' myapp | jq

Network Debugging

Check container IP:

docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

List networks:

docker network ls
podman network ls

Inspect network:

docker network inspect bridge
podman network inspect podman

Test connectivity from container:

docker exec myapp ping google.com
docker exec myapp curl http://api.example.com
docker exec myapp nc -zv database 5432

Check DNS resolution:

docker exec myapp nslookup database
docker exec myapp cat /etc/resolv.conf

Port mapping:

docker port myapp
podman port myapp

Test from host to container:

# Get container IP
IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp)
ping $IP
curl http://$IP:8080

Filesystem Debugging

Copy files from container:

docker cp myapp:/path/to/file ./local/path
podman cp myapp:/path/to/file ./local/path

Copy files to container:

docker cp ./local/file myapp:/path/to/destination
podman cp ./local/file myapp:/path/to/destination

Check disk usage:

docker exec myapp df -h
docker exec myapp du -sh /var/log

View file contents:

docker exec myapp cat /etc/config.yml
docker exec myapp tail -f /var/log/app.log

Container Events

Watch events:

docker events
podman events

Filter events for specific container:

docker events --filter container=myapp
podman events --filter container=myapp

Filter by event type:

docker events --filter event=start
docker events --filter event=die

Troubleshooting Crashes

Check exit code:

docker inspect -f '{{.State.ExitCode}}' myapp

Common exit codes:

  • 0: Success
  • 1: Application error
  • 126: Command cannot be invoked
  • 127: Command not found
  • 137: SIGKILL (killed by system, often OOM)
  • 139: SIGSEGV (segmentation fault)
  • 143: SIGTERM (graceful termination)

Check if OOM killed:

docker inspect -f '{{.State.OOMKilled}}' myapp
dmesg | grep -i oom

Run interactively to debug:

docker run -it --entrypoint /bin/bash myapp:latest

Advanced Debugging

Attach to container (see STDOUT/STDERR):

docker attach myapp
# Detach: Ctrl+P, Ctrl+Q

Export container filesystem:

docker export myapp > container.tar
tar -xf container.tar

Commit container state:

docker commit myapp debug_snapshot:latest

Trace system calls:

docker exec myapp strace -p 1

Docker Compose

Compose File Structure

Basic compose.yml:

version: '3.8'

services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    ports:
      - "8080:80"
    environment:
      - DATABASE_URL=postgres://db/myapp
    depends_on:
      - db
    networks:
      - app-network
    volumes:
      - ./app:/app
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3

  db:
    image: postgres:16-alpine
    environment:
      - POSTGRES_DB=myapp
      - POSTGRES_PASSWORD_FILE=/run/secrets/db_password
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - app-network
    secrets:
      - db_password

networks:
  app-network:
    driver: bridge

volumes:
  db-data:

secrets:
  db_password:
    file: ./secrets/db_password.txt

Starting Services

Start all services:

docker-compose up -d
docker compose up -d
podman-compose up -d

Start specific service:

docker-compose up -d web
docker compose up -d web
podman-compose up -d web

Start with build:

docker-compose up -d --build
docker compose up -d --build
podman-compose up -d --build

Start with specific file:

docker-compose -f docker-compose.prod.yml up -d
docker compose -f docker-compose.prod.yml up -d
podman-compose -f docker-compose.prod.yml up -d

Use multiple compose files:

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

Stopping Services

Stop all services:

docker-compose down
docker compose down
podman-compose down

Stop and remove volumes:

docker-compose down -v
docker compose down -v
podman-compose down -v

Stop specific service:

docker-compose stop web
docker compose stop web
podman-compose stop web

Viewing Status and Logs

List running services:

docker-compose ps
docker compose ps
podman-compose ps

View logs:

docker-compose logs
docker compose logs
podman-compose logs

Follow logs:

docker-compose logs -f
docker compose logs -f
podman-compose logs -f

Logs for specific service:

docker-compose logs -f web
docker compose logs web
podman-compose logs web

Last N lines:

docker-compose logs --tail 50 web

Building Services

Build all services:

docker-compose build
docker compose build
podman-compose build

Build specific service:

docker-compose build web
docker compose build web
podman-compose build web

Build with no cache:

docker-compose build --no-cache
docker compose build --no-cache
podman-compose build --no-cache

Scaling Services

Scale service to multiple instances:

docker-compose up -d --scale web=3
docker compose up -d --scale web=3
podman-compose up -d --scale web=3

Executing Commands

Run command in service:

docker-compose exec web bash
docker compose exec web bash
podman-compose exec web bash

Run one-off command:

docker-compose run web python manage.py migrate
docker compose run web python manage.py migrate
podman-compose run web python manage.py migrate

Run without starting dependencies:

docker-compose run --no-deps web python script.py

Environment Variables

Using .env file:

# .env
DATABASE_URL=postgres://localhost/myapp
SECRET_KEY=mysecret
DEBUG=false

In compose file:

services:
  web:
    env_file:
      - .env
    # Or specific variables
    environment:
      - DATABASE_URL=${DATABASE_URL}
      - DEBUG=${DEBUG:-false}

Important notes:

  • .env file must be in same directory as compose file
  • Use KEY=value format (no quotes needed)
  • Variables can have defaults: ${VAR:-default}

Networking in Compose

Services communicate by service name:

services:
  web:
    environment:
      - DATABASE_URL=postgres://db:5432/myapp  # 'db' is service name
  db:
    image: postgres:16-alpine

Custom networks:

services:
  web:
    networks:
      - frontend
      - backend

  db:
    networks:
      - backend

networks:
  frontend:
  backend:
    driver: bridge

Volumes in Compose

Named volumes:

services:
  db:
    volumes:
      - db-data:/var/lib/postgresql/data

volumes:
  db-data:

Bind mounts:

services:
  web:
    volumes:
      - ./app:/app
      - ./config:/etc/app:ro  # read-only

Volume management:

# List volumes
docker volume ls

# Inspect volume
docker volume inspect projectname_db-data

# Remove unused volumes
docker volume prune

Profiles

Define profiles:

services:
  web:
    # Always runs

  debug:
    profiles: ["debug"]
    # Only runs with --profile debug

  test:
    profiles: ["testing"]

Use profiles:

docker-compose --profile debug up -d
docker compose --profile debug up -d
podman-compose --profile debug up -d

Override Files

docker-compose.override.yml automatically loaded:

# Automatically uses docker-compose.yml + docker-compose.override.yml
docker-compose up -d

Explicit override:

docker-compose -f docker-compose.yml -f docker-compose.dev.yml up -d

Resource Limits

services:
  web:
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

Podman-Specific Features

Rootless Containers

Run rootless (default in Podman):

podman run -d --name myapp myapp:latest

Check if rootless:

podman info --format '{{.Host.Security.Rootless}}'

Troubleshoot rootless:

# Check subuid/subgid
cat /etc/subuid
cat /etc/subgid

# Migrate storage if needed
podman system migrate

Systemd Integration

Generate systemd unit file:

podman generate systemd --name myapp > ~/.config/systemd/user/myapp.service

# Enable and start service
systemctl --user enable --now myapp.service

Generate from compose:

# Start services with podman-compose
podman-compose up -d

# Get pod name
podman pod ps

# Generate systemd unit
podman generate systemd --name mypod_pod --files

# Move to systemd directory
mv *.service ~/.config/systemd/user/

# Enable
systemctl --user enable --now mypod_pod.service

Pods (Kubernetes-like)

Create pod:

podman pod create --name mypod -p 8080:80

Add container to pod:

podman run -d --pod mypod --name myapp myapp:latest

List pods:

podman pod ps

Inspect pod:

podman pod inspect mypod

Stop entire pod:

podman pod stop mypod

Pod logs:

podman pod logs mypod

Generate Kubernetes YAML:

podman generate kube mypod > pod.yaml

Best Practices

Container Naming

  • Use descriptive names that indicate purpose
  • Follow consistent naming convention (e.g., project-service-env)
  • Avoid generic names like “app” or “container”

Resource Management

  • Always set memory limits for production: --memory 512m
  • Set CPU limits to prevent resource hogging: --cpus 1.5
  • Use health checks: --health-cmd "curl -f http://localhost/health"
  • Monitor resource usage with docker stats

Security

  • Run as non-root when possible (Podman rootless by default)
  • Use read-only filesystem when applicable: --read-only
  • Drop unnecessary capabilities: --cap-drop ALL
  • Use security profiles: --security-opt
  • Don’t expose unnecessary ports
  • Use secrets for sensitive data (compose)

Networking

  • Use service names for inter-container communication
  • Limit exposed ports to necessary ones only
  • Use custom networks to isolate services
  • Avoid network_mode: host in production

Volumes and Persistence

  • Use named volumes for data persistence
  • Back up volumes regularly
  • Use read-only mounts when data shouldn’t change
  • Clean up unused volumes: docker volume prune

Compose Files

  • Use specific image tags (not latest)
  • Define health checks for all services
  • Specify restart policies
  • Use .env files for environment-specific config
  • Define resource limits
  • Document dependencies with depends_on
  • Use secrets for sensitive data

Development vs Production

  • Use override files for environment differences
  • Keep production configs minimal and secure
  • Use profiles for optional services
  • Separate build and runtime concerns

Troubleshooting

Container Exits Immediately

Diagnosis:

# Check exit code
docker inspect -f '{{.State.ExitCode}}' myapp

# Check logs
docker logs myapp

# Run interactively
docker run -it --entrypoint /bin/bash myapp:latest

Common causes:

  • Application error (check logs)
  • Missing command in Dockerfile
  • Configuration issue
  • OOM kill (exit code 137)

Port Already in Use

Diagnosis:

# Check what's using port
lsof -i :8080
ss -tlnp | grep 8080

# Check container port mappings
docker port myapp

Solutions:

  • Use different host port: -p 8081:80
  • Stop conflicting service
  • Remove old container: docker rm -f old-container

Permission Denied

Docker:

# Add user to docker group
sudo usermod -aG docker $USER
# Then logout and login again

Podman:

# Check if rootless
podman info --format '{{.Host.Security.Rootless}}'

# Check subuid/subgid
cat /etc/subuid
cat /etc/subgid

Container filesystem:

# Check file permissions
docker exec myapp ls -la /app

# Run as root to fix
docker exec -u 0 -it myapp bash

Cannot Connect to Container

Diagnosis:

# Check if container is running
docker ps

# Check port mapping
docker port myapp

# Get container IP
docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' myapp

# Test from host
curl http://localhost:8080

Check network:

docker network inspect bridge
docker exec myapp ip addr
docker exec myapp netstat -tlnp

Memory/CPU Issues

Check resources:

docker stats myapp
docker stats --no-stream

Check if OOM killed:

docker inspect -f '{{.State.OOMKilled}}' myapp
dmesg | grep -i oom

Set appropriate limits:

docker run -d --memory="1g" --cpus="2.0" --name myapp myapp:latest

Volume/Mount Issues

Check mounts:

docker inspect -f '{{json .Mounts}}' myapp | jq

Verify volume:

docker volume ls
docker volume inspect volume_name

Check permissions:

docker exec myapp ls -la /mounted/path

Common causes:

  • Wrong path (bind mount)
  • Permission issues (SELinux, user permissions)
  • Volume not created
  • Path doesn’t exist in container

Compose Issues

Compose file not found:

# Specify file explicitly
docker-compose -f /path/to/docker-compose.yml up -d

Services can’t communicate:

  • Ensure services are on same network
  • Use service names as hostnames (not IPs)
  • Check network configuration in compose file

Environment variables not loading:

  • Verify .env file is in same directory as compose file
  • Check variable syntax: KEY=value (no quotes)
  • Don’t use .env for secrets in production

Version not supported:

  • Use version 3.8 or omit version for modern compose
  • Check compose tool version: docker-compose --version

Network Connectivity

DNS not resolving:

docker exec myapp cat /etc/resolv.conf
docker exec myapp nslookup google.com

Can’t reach other containers:

docker exec myapp ping other-container
docker network inspect bridge

Can’t reach outside:

docker exec myapp ping 8.8.8.8
docker exec myapp curl https://google.com

Quick Reference

Container lifecycle:

docker run -d --name myapp myapp:latest     # Start
docker ps                                    # List running
docker stop myapp                            # Stop
docker restart myapp                         # Restart
docker rm myapp                              # Remove

Debugging:

docker logs -f myapp                         # Follow logs
docker exec -it myapp bash                   # Get shell
docker inspect myapp                         # Full details
docker stats myapp                           # Resource usage
docker top myapp                             # Processes
docker port myapp                            # Port mappings

Compose:

docker-compose up -d                         # Start all
docker-compose ps                            # List services
docker-compose logs -f                       # Follow logs
docker-compose exec web bash                 # Exec in service
docker-compose down                          # Stop all