Docker Compose Baseline
What this is
My standard approach for deploying and managing all containers using Docker Compose instead of manual docker run commands.
This replaces ad-hoc container creation with structured, repeatable configurations.
Why I needed this
Using docker run and Portainer manually caused:
- No consistency between services
- Harder rebuilds after failure
- No version-controlled configs
- Difficult troubleshooting
With Docker Compose:
- Everything is defined in a single file
- Easy to redeploy or migrate
- Cleaner structure across services
- Works perfectly with my NAS mounts and backups
My Setup
- Host:
minipc (192.168.86.32)→ main Docker hostmicropc (192.168.86.100)→ utility host
- Base directory:
/opt/docker/
- Structure:
/opt/docker/
├── jellyfin/
├── homeassistant/
├── paperless/
├── dozzle/
├── diun/
├── jellyfin/
├── homeassistant/
├── paperless/
├── dozzle/
├── diun/
- Each service contains:
docker-compose.yml
.env (optional)
.env (optional)
- NAS mount:
/mnt/sjb2
Key Concepts / Options
| Feature | docker run | Docker Compose |
|---|---|---|
| Repeatability | ❌ Manual | ✅ Config file |
| Backup | ❌ Hard | ✅ Easy |
| Editing | ❌ Recreate container | ✅ Edit + restart |
| Scaling | ❌ Difficult | ✅ Built-in |
Recommendation
👉 Use Docker Compose for every container
Why:
- Consistent deployments
- Easy to rebuild servers
- Cleaner troubleshooting
- Matches how most modern homelabs operate
Setup
1. Install Docker Compose
On modern Docker versions:
docker compose version
If not installed:
sudo apt install docker-compose-plugin -y
2. Create Base Directory
sudo mkdir -p /opt/docker
sudo chown -R $USER:$USER /opt/docker
sudo chown -R $USER:$USER /opt/docker
3. Create a Service Folder
Example:
cd /opt/docker
mkdir example-app
cd example-app
mkdir example-app
cd example-app
4. Create docker-compose.yml
Example template:
version: "3.9"
services:
app:
image: nginx:latest
container_name: example-app
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./data:/data
environment:
- TZ=America/Santo_Domingo
services:
app:
image: nginx:latest
container_name: example-app
restart: unless-stopped
ports:
- "8080:80"
volumes:
- ./data:/data
environment:
- TZ=America/Santo_Domingo
5. Start the Container
docker compose up -d
6. Manage the Container
Stop:
docker compose down
Restart:
docker compose restart
Update:
docker compose pull
docker compose up -d
docker compose up -d
Configuration
Environment Variables (.env)
Example:
TZ=America/Santo_Domingo
PUID=1000
PGID=1000
PUID=1000
PGID=1000
Use in compose:
environment:
- TZ=${TZ}
- TZ=${TZ}
Volume Strategy
Use:
- Local:
./data
- NAS:
/mnt/sjb2/media
Permissions Notes
- Containers may fail if permissions mismatch
- Match user IDs:
id
Typical values:
PUID=1000PGID=1000
Verification
Check running containers:
docker ps
Check logs:
docker compose logs -f
Common Problems / Fixes
Container won’t start
- Cause: Bad config or port conflict
- Fix:
docker compose logs
Port already in use
- Cause: Another container using same port
- Fix:
docker ps
Change port in compose file
Permission denied (volumes)
- Cause: Wrong ownership
- Fix:
sudo chown -R 1000:1000 ./data
Changes not applied
- Cause: Container not recreated
- Fix:
docker compose down
docker compose up -d
docker compose up -d
Result
- All services defined in clean YAML files
- Easy to rebuild entire server
- Consistent deployment across hosts
- Simplified troubleshooting and maintenance
Notes
- Prefer Compose over Portainer for deployment
- Portainer still useful for:
- Viewing logs
- Quick inspection
- Keep one folder per service
- Back up
/opt/dockerregularly - Future improvement:
- Git repository for configs
- Automated updates (DIUN already in use)
- Reverse proxy integration (Caddy)
No comments to display
No comments to display