Docker

Docker is a containerization platform that packages applications and their dependencies into isolated, portable containers. It's the foundation of modern self-hosted infrastructure.

Why Docker for Self-Hosting?

Isolation: Each service runs in its own container with its own filesystem, preventing conflicts between applications.

Portability: Containers run identically across different systems. Your configuration works on any machine with Docker installed.

Easy Updates: Pull new image versions and restart containers. Rollback is simple if something breaks.

Resource Efficiency: Containers share the host kernel, using far less resources than virtual machines.

Reproducibility: Docker Compose files document your entire stack configuration as code.

Core Concepts

Images

Pre-built templates containing an application and its dependencies. Think of them as blueprints.

# Pull an image from Docker Hub
docker pull jellyfin/jellyfin:latest

Containers

Running instances of images. These are your actual applications.

# Run a container
docker run -d --name jellyfin jellyfin/jellyfin:latest

Volumes

Persistent storage that survives container restarts and updates.

# Create a volume
docker volume create jellyfin-config

Networks

Virtual networks that allow containers to communicate with each other.

# Create a network
docker network create media-network

Docker Compose

Docker Compose manages multi-container applications using YAML configuration files.

Basic Structure

version: '3.8'

services:
  jellyfin:
    image: jellyfin/jellyfin:latest
    container_name: jellyfin
    ports:
      - "8096:8096"
    volumes:
      - jellyfin-config:/config
      - /mnt/media:/media:ro
    restart: unless-stopped

volumes:
  jellyfin-config:

Common Patterns

Environment Variables:

environment:
  - TZ=America/Chicago
  - PUID=1000
  - PGID=1000

Port Mapping:

ports:
  - "8080:80"  # host:container

Volume Mounts:

volumes:
  - ./config:/config          # Bind mount
  - media-data:/data          # Named volume
  - /mnt/media:/media:ro      # Read-only mount

Networks:

networks:
  - frontend
  - backend

Restart Policies:

restart: unless-stopped  # Recommended for services
restart: always          # Restarts even if manually stopped
restart: on-failure      # Only restarts on error

Essential Commands

Container Management

# List running containers
docker ps

# List all containers (including stopped)
docker ps -a

# Start/stop/restart a container
docker start <container>
docker stop <container>
docker restart <container>

# Remove a container
docker rm <container>

# View container logs
docker logs <container>
docker logs -f <container>  # Follow mode

Image Management

# List images
docker images

# Pull an image
docker pull <image>:<tag>

# Remove an image
docker rmi <image>

# Remove unused images
docker image prune -a

Docker Compose Commands

# Start services
docker compose up -d

# Stop services
docker compose down

# View logs
docker compose logs -f

# Pull new images
docker compose pull

# Restart services
docker compose restart

# Update and restart
docker compose pull && docker compose up -d

System Maintenance

# View disk usage
docker system df

# Clean up everything unused
docker system prune -a

# Remove stopped containers
docker container prune

# Remove unused volumes
docker volume prune

# Remove unused networks
docker network prune

Best Practices

Organization

Directory Structure:

/home/user/docker/
├── jellyfin/
│   └── docker-compose.yml
├── homeassistant/
│   └── docker-compose.yml
└── ai-stack/
    └── docker-compose.yml

Keep each service or stack in its own directory with its compose file.

Security

Run as Non-Root:

user: "1000:1000"  # UID:GID

Read-Only Mounts:

volumes:
  - /mnt/media:/media:ro  # Read-only

Network Isolation:

networks:
  - internal  # No internet access

Secrets Management:

# Use .env files for sensitive data
env_file:
  - .env

Performance

Resource Limits:

deploy:
  resources:
    limits:
      cpus: '2.0'
      memory: 4G
    reservations:
      memory: 2G

Logging:

logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"

Maintenance

Update Strategy:

  1. Pull new images: docker compose pull
  2. Stop services: docker compose down
  3. Start with new images: docker compose up -d
  4. Check logs: docker compose logs -f

Backup Important Data:

Troubleshooting

Container Won't Start

# Check logs
docker logs <container>

# Inspect container
docker inspect <container>

# Check if port is already in use
sudo ss -tulpn | grep <port>

Permission Issues

# Check file ownership
ls -la /path/to/volume

# Fix ownership (use your UID:GID)
sudo chown -R 1000:1000 /path/to/volume

Network Issues

# List networks
docker network ls

# Inspect network
docker network inspect <network>

# Recreate network
docker network rm <network>
docker network create <network>

High Disk Usage

# Check what's using space
docker system df

# Clean up
docker system prune -a
docker volume prune

Common Pitfalls

Using latest tag: Always pin specific versions in production

# Bad
image: jellyfin/jellyfin:latest

# Good
image: jellyfin/jellyfin:10.8.13

Not using volumes: Data will be lost when container is removed

# Always use volumes for persistent data
volumes:
  - config-data:/config

Exposing all ports: Only expose what's necessary

# Bad
ports:
  - "0.0.0.0:8080:80"

# Good (only local network)
ports:
  - "127.0.0.1:8080:80"

Learning Resources


Related Topics: