I got into Docker through a web-based setup tool, and now more than half of my system runs in containers. My services currently live under paths such as /srv/docker/jellyfin/, but I suspect I'm missing some best practices. How do you usually organize Compose YAML files, environment variables, persistent configuration, bind mounts, backups, and secrets? I'd especially appreciate examples of a clean layout that remains easy to back up, version-control, and move to another machine.
4 Answers
There isn’t one mandatory directory layout. Your existing /srv/docker/ arrangement is perfectly fine if it is organized consistently. A practical approach is to keep the Compose file at the application root, place persistent configuration in clearly named subdirectories, keep secrets outside Git, and use explicit absolute paths in the volume mappings. Document required permissions, ports, networks, and restore steps in a README so the setup can be rebuilt without relying on memory.
Another reasonable pattern is /opt/compose//. Each service directory contains its Compose file and its bind-mounted configuration folders. You can then manage each project independently, either with Docker Compose commands or a systemd template that points to the matching service directory. The important part is consistency: keep deployment definitions, persistent data, environment files, and backups clearly separated, and make sure your backup plan includes the application data as well as the Compose files.
I use a top-level /containers directory with one folder per application, such as /containers/immich, /containers/nextcloud, and /containers/jellyfin. Each folder contains its own compose.yaml and the subdirectories needed for bind mounts. I prefer bind mounts over anonymous Docker volumes because the data is visible, easy to back up, and simpler to move to another host.
For example, the Compose file might live at /containers/jellyfin/compose.yaml, with its configuration and media paths explicitly mapped to subdirectories below that project. Keeping each app isolated also prevents unrelated files from cluttering a shared directory. On some systems I put /containers on a separate NVMe drive, which makes migrations or operating-system reinstalls easier: mount the drive on the new host, install Docker, and start the projects again.
I usually give each service its own project directory. The Compose file and a non-secret .env file stay together, while persistent data is kept in a separate bind-mounted directory. Secrets and container-specific environment files go in an env/ folder that is excluded from version control. A layout might look like this:
/opt/apps/media-stack/
├── README.md
├── compose/
│ ├── compose.yml
│ └── .env
├── data/
│ └── jellyfin/
├── env/
│ ├── jellyfin.env
│ └── jellyfin-secrets.env.example
└── backups/
This keeps the deployment files separate from application data and makes it clear what should or should not be backed up. I’d also avoid :latest for important services; pinning images to a specific version or digest makes deployments reproducible.

That separation has worked well for me too. Just remember that some images have specific permission or filesystem expectations, so a bind mount may occasionally need ownership, labeling, or other adjustments depending on how the container was built.