Unraid’s default Docker interface is fine for getting started. You click through a template, fill in a few fields, and you’ve got a running container. But once you have 20 containers, that approach starts to break down. Things are inconsistently configured, you can’t easily reproduce the setup on a new machine, and there’s no version history for your container configs.
I switched to Compose-based management about 18 months ago. Here’s the structure I landed on.
Why Compose on Unraid
Unraid 6.12 added native Docker Compose support through the compose manager. Before that, you had to use the community plugin. Now it’s built in – you can define stacks in YAML files and manage them from the UI or command line.
The main benefit is reproducibility. My entire container stack is defined in YAML files stored in a git repository. If the server dies and I rebuild from scratch, I clone the repo and bring everything back up with docker compose up -d. That’s not fully automated yet – I still have to restore data volumes – but the configuration layer is covered.
The other benefit is legibility. A Compose file makes the dependencies, environment variables, network configuration, and volume mappings explicit in one place. No more hunting through the Unraid UI trying to remember why a specific port was mapped a certain way.
My Directory Structure
Everything lives under /mnt/user/compose/ on the array. Each stack gets its own subdirectory:
/mnt/user/compose/
media/
docker-compose.yml # Plex, Radarr, Sonarr, Prowlarr, Jellyfin
nextcloud/
docker-compose.yml
.env # DB passwords, admin credentials
ai/
docker-compose.yml # Ollama, Open WebUI
monitoring/
docker-compose.yml # Uptime Kuma, Dozzle
network/
docker-compose.yml # AdGuard Home, Nginx Proxy Manager
security/
docker-compose.yml # Vaultwarden, Authentik (if I ever fix it)
The grouping is functional – services that talk to each other or share a network go in the same stack. Services that are independent get their own stack so I can restart them without touching unrelated containers.
Environment Variables and Secrets
Each stack directory has a .env file for environment-specific configuration. Passwords, API keys, and paths that differ between environments go there. The .env files are excluded from the git repo (obviously) and backed up separately.
The Compose file references variables from .env like this:
environment:
- POSTGRES_PASSWORD=${NEXTCLOUD_DB_PASSWORD}
- NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_ADMIN_PASSWORD}
This means the Compose files themselves can be safely stored in version control without exposing credentials. When I need to check “what database password did I use for Nextcloud,” I look in the .env file in the secure backup, not in git history.
Networking: Named Networks Over Default Bridge
The default Docker bridge network works but it’s loose. Any container on the bridge can reach any other container on the bridge by IP. That’s fine for a home server where you trust everything, but it makes it harder to reason about which services actually need to talk to each other.
I define named networks in my Compose files and assign containers only to the networks they need. The media stack has a media_net network. Radarr and Sonarr are on it. Plex is on it. The download client is on it. Nothing outside that stack is on media_net.
Services that need external access – AdGuard Home, Nginx Proxy Manager – have a separate proxy_net network. Vaultwarden is on both proxy_net (for external access) and an isolated vault_net (for the internal database connection).
Volume Management
I use bind mounts rather than named Docker volumes. This means data lives at a predictable path on the host file system rather than in Docker’s internal volume store. For a home server this is usually the right call – you can find your files with a file manager, back them up with normal tools, and restore them without Docker-specific knowledge.
The appdata path for configuration files is /mnt/user/appdata/[service-name]. Media and large data files go on the array at /mnt/user/[category]/. The distinction matters for backup strategy – appdata is small and critical (back it up frequently), while media files are large and recoverable (back up less often or not at all if you can re-download).
The Stack I’m Most Proud Of
The monitoring stack is the one I’ve refined the most. Uptime Kuma polls every service endpoint. Dozzle aggregates container logs so I can see what’s happening without SSH-ing in every time. A Telegram webhook notifies me if any service goes down and stays down for more than 2 minutes.
This setup has caught three real problems in the last six months: a Nextcloud container that silently exited after an update, a DNS resolution failure that took down local hostname routing, and a Plex database lock that was blocking transcoding. In each case I had an alert within 5 minutes of the failure.
What I’d Tell Someone Starting Fresh
Start with Compose from day one. The Unraid template UI is a fine way to understand the options for a new service, but immediately translate it to a Compose file before you forget what you configured. The time you spend doing that pays back the first time you need to rebuild something.
Store your Compose files somewhere safe. A private git repo is ideal. A local backup is the minimum. These files represent hours of configuration work – treat them like code.
The LincStation N1 is a solid appliance if you want to get started with Unraid and Compose without building a server from scratch. It ships with an Unraid license and has enough headroom for a basic Docker stack.
Next post: local LLMs versus cloud AI, from someone who runs both every day.
Products mentioned in this post:
Affiliate disclosure: Some links in this post are Amazon affiliate links. If you buy through them, I get a small commission at no cost to you. It helps keep the lights on here.
Leave A Comment