Docker Integration¶
Overview¶
Two approaches for Docker storage with ZFS:
| Approach | How It Works | Best For |
|---|---|---|
| overlay2 + bind mounts | Docker uses overlay2; data on ZFS via bind mounts | Most setups (recommended) |
| ZFS storage driver | Docker stores images/containers directly on ZFS | Advanced use cases |
Recommended: overlay2 + Bind Mounts¶
This approach keeps Docker simple while still benefiting from ZFS for persistent data.
How It Works¶
Docker (overlay2) ZFS (tank)
+------------------+ +------------------+
| Images | | /mnt/tank/ |
| Containers | <--> | nextcloud-data |
| Build cache | bind | db |
+------------------+ mount | media |
/var/lib/docker +------------------+
- Docker manages images and containers on the root filesystem
- Application data lives on ZFS datasets
- Containers access ZFS via bind mounts
Configuration¶
Docker daemon config (/etc/docker/daemon.json):
See Docker Setup for full configuration.
Bind Mount Example¶
docker-compose.yml:
services:
nextcloud:
image: nextcloud:latest
volumes:
- /mnt/tank/nextcloud-app:/var/www/html
- /mnt/tank/nextcloud-data:/var/www/html/data
Benefits¶
- Simple Docker upgrades (no storage driver issues)
- ZFS features (snapshots, compression) for application data
- Separation of concerns: Docker handles containers, ZFS handles data
Alternative: ZFS Storage Driver¶
Docker can use ZFS directly for all storage. Each image layer and container becomes a ZFS dataset.
When to Consider¶
- Container-heavy workflows with many short-lived containers
- Want ZFS features for image layers (deduplication potential)
- Advanced ZFS management requirements
Setup¶
- Create a dataset for Docker:
- Configure Docker:
- Restart Docker:
Drawbacks¶
- More complex troubleshooting
- Docker version upgrades may have storage driver compatibility issues
- Many small datasets (one per layer/container)
Directory Structure¶
Organize container data on ZFS:
/mnt/tank/
├── nextcloud-data/ # Nextcloud user files
├── nextcloud-app/ # Nextcloud application
├── db/ # Database files
│ ├── postgres/
│ └── mariadb/
├── media/ # Plex/Jellyfin media
└── containers/ # Misc container state
├── homeassistant/
└── grafana/
Create datasets as needed:
sudo zfs create tank/containers
sudo zfs create tank/containers/homeassistant
sudo zfs create tank/containers/grafana
Snapshot Strategy¶
Per-Service Snapshots¶
Before updating a service:
# Snapshot the data
sudo zfs snapshot tank/nextcloud-data@pre-update
sudo zfs snapshot tank/nextcloud-app@pre-update
# Update the container
docker compose pull
docker compose up -d
Automated Snapshots¶
Create a script for pre-update snapshots:
#!/bin/bash
# /usr/local/bin/docker-snapshot.sh
SERVICE=$1
DATE=$(date +%Y%m%d-%H%M)
case $SERVICE in
nextcloud)
zfs snapshot tank/nextcloud-data@${DATE}
zfs snapshot tank/nextcloud-app@${DATE}
;;
plex)
zfs snapshot tank/media@${DATE}
;;
*)
zfs snapshot tank/containers/${SERVICE}@${DATE}
;;
esac
Usage:
Recovery¶
If an update breaks something:
# Stop containers
docker compose down
# Rollback
sudo zfs rollback tank/nextcloud-data@pre-update
sudo zfs rollback tank/nextcloud-app@pre-update
# Restore previous image
docker compose up -d
Permissions¶
Docker containers often run as non-root users. Set ownership accordingly:
# Nextcloud runs as www-data (UID 33)
sudo chown -R 33:33 /mnt/tank/nextcloud-data
sudo chown -R 33:33 /mnt/tank/nextcloud-app
# Plex runs as plex user
sudo chown -R 997:997 /mnt/tank/media
Check container documentation for the expected UID/GID.
Comparison Summary¶
| Feature | overlay2 + bind mounts | ZFS driver |
|---|---|---|
| Setup complexity | Low | Medium |
| Snapshot granularity | Per dataset | Per container |
| Docker upgrades | Simple | May need migration |
| Troubleshooting | Standard Docker | ZFS knowledge needed |
| Recommended | Yes | Special cases |
For most deployments, overlay2 with bind mounts provides the best balance of simplicity and ZFS benefits.