Docker Folder Structure & Naming Conventions
What this is
A standardised way to organise all Docker services, folders, and naming across my homelab.
This ensures consistency, easier troubleshooting, and simpler backups.
Why I needed this
Without structure:
- Files end up scattered
- Hard to find configs
- Inconsistent naming between services
- Backups become messy
- Harder to migrate to another host
With structure:
- Everything is predictable
- Faster troubleshooting
- Easier to rebuild or migrate
- Cleaner integration with NAS storage
My Setup
Base directory:
/opt/docker/
Each service gets its own folder:
/opt/docker/
├── jellyfin/
├── homeassistant/
├── paperless/
├── dozzle/
├── diun/
├── jellyfin/
├── homeassistant/
├── paperless/
├── dozzle/
├── diun/
Inside each service:
docker-compose.yml
.env
data/
config/
logs/
.env
data/
config/
logs/
Key Concepts / Options
Flat vs Nested Structure
| Approach | Example | Verdict |
|---|---|---|
| Flat | /opt/docker/jellyfin |
✅ Simple |
| Nested by type | /opt/docker/media/jellyfin |
⚠️ Can get messy |
Named Volumes vs Bind Mounts
| Type | Example | Use Case |
|---|---|---|
| Bind mount | ./data |
✅ Preferred |
| Named volume | portainer_data |
Limited use |
Recommendation
👉 Use a flat structure with bind mounts
Why:
- Easier backups (
rsync, NAS sync) - Full visibility of files
- No hidden Docker volumes
- Cleaner disaster recovery
Setup
1. Create Base Directory
sudo mkdir -p /opt/docker
sudo chown -R $USER:$USER /opt/docker
sudo chown -R $USER:$USER /opt/docker
2. Create Service Folder
Example:
cd /opt/docker
mkdir jellyfin
cd jellyfin
mkdir jellyfin
cd jellyfin
3. Standard Folder Layout
mkdir -p data config logs
4. Example docker-compose.yml
version: "3.9"
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
ports:
- "8096:8096"
volumes:
- ./config:/config
- ./data:/data
- /mnt/sjb2/media:/media
environment:
- TZ=Europe/London
- PUID=1000
- PGID=1000
services:
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
ports:
- "8096:8096"
volumes:
- ./config:/config
- ./data:/data
- /mnt/sjb2/media:/media
environment:
- TZ=Europe/London
- PUID=1000
- PGID=1000
Configuration
Naming Conventions
Container Names
- Use same as folder:
jellyfin
homeassistant
paperless
homeassistant
paperless
Folder Names
- Lowercase
- No spaces
- Match service name
Compose Files
Always:
docker-compose.yml
Environment Files
Optional:
.env
Port Conventions (Recommended)
| Type | Range |
|---|---|
| Web UIs | 8000–8999 |
| Admin tools | 9000–9999 |
| Custom apps | 7000–7999 |
Permissions Notes
- Always align container permissions:
id
Typical:
PUID=1000PGID=1000
Fix issues:
sudo chown -R 1000:1000 /opt/docker/<service>
Verification
Check structure:
tree /opt/docker
Check running containers:
docker ps
Common Problems / Fixes
Config files missing
- Cause: Wrong volume mapping
- Fix:
- Verify paths in
docker-compose.yml
Data not persisting
- Cause: Using container-only storage
- Fix:
- Use bind mounts (
./config,./data)
Permissions errors
- Cause: Wrong ownership
- Fix:
sudo chown -R 1000:1000 .
Inconsistent naming
- Cause: Manual container creation
- Fix:
- Standardise folder + container names
Result
- Clean, predictable folder structure
- Easy backups to NAS
- Faster troubleshooting
- Consistent deployments across both hosts
Notes
- Keep everything under
/opt/docker - Avoid random folders elsewhere
- Back up:
/opt/docker/mnt/sjb2(NAS data)
- Stick to one pattern — consistency matters more than perfection
- This structure scales easily as services grow
No comments to display
No comments to display