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:
- Pull new images:
docker compose pull - Stop services:
docker compose down - Start with new images:
docker compose up -d - Check logs:
docker compose logs -f
Backup Important Data:
- Docker compose files
- Volume data
- Environment files
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
- Docker Official Documentation
- Docker Compose Documentation
- LinuxServer.io - Quality container images
- Awesome-Selfhosted - Service catalog
Related Topics:
- Self-Hosting a Home Server - Complete homelab guide
- NGINX - Reverse proxy for Docker services
- Linux - Host operating system