> ## Content Index
> Fetch the complete content index at: https://notes.d4rkcyber.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Docker Compose Stacks
- URL: https://notes.d4rkcyber.com/docker-compose-stacks/
- Published: 2026-06-02T00:00:00.000Z
- Updated: 2026-10-29T11:49:09.000Z
- Description: How I structure and manage Docker Compose stacks across the homelab — separate stack directories, shared networks, named volumes, and the habits that make running 20+ containers manageable.
- Author: David Dami
- Tags: Infrastructure, docker, docker-compose, homelab, containers

## How I Organise Stacks

Each service or related group of services lives in its own directory with a `compose.yml` and a `.env`. No single monolithic Compose file that controls everything — small, focused stacks that start and stop independently.

```
~/stacks/
├── traefik/
│   ├── compose.yml
│   ├── traefik.yml
│   └── .env
├── monitoring/
│   ├── compose.yml
│   └── .env
├── n8n/
│   ├── compose.yml
│   └── .env
├── ollama/
│   ├── compose.yml
│   └── .env

```

The benefit: if I need to tear down n8n to upgrade it, nothing else is affected. Each stack is isolated.

## Shared Networks

Services that need to communicate (or route through Traefik) join a shared external network:

```bash
docker network create proxy

```

In each `compose.yml`:

```yaml
networks:
  proxy:
    external: true

services:
  myapp:
    networks:
      - proxy

```

I use `proxy` for anything that goes through Traefik. Services that only need to talk to each other (like an app and its database) get their own internal network within the stack.

## Volumes

Mapped volumes for anything I care about, I'm not a fan of docker volumes - too abstract for me:

```yaml
volumes:
  app-data:

services:
  myapp:
    volumes:
      - app-data:/app/data

```

For bulk storage (media, backups) I bind-mount to specific paths on the host:

```yaml
volumes:
  - /mnt/tank/media:/media

```

## .env Pattern

Every stack has a `.env`. The Compose file references variables with `${VAR_NAME}`. This keeps credentials out of the Compose file and makes it easy to see what needs to be configured when setting up on a new machine.

```env
POSTGRES_PASSWORD=changeme
DOMAIN=yourdomain.com
TZ=Europe/Dublin

```

## Useful Commands

```bash
# Start a stack in the background
docker compose up -d

# View real-time logs for a service
docker compose logs -f servicename

# Restart a single service
docker compose restart servicename

# Pull latest images and recreate
docker compose pull && docker compose up -d

# Remove unused containers, networks, images
docker system prune -f

```

## Always-On Services

`restart: unless-stopped` on every service that should survive a server reboot. Without this, containers have to be started manually after a restart — which I always forget.

```yaml
services:
  myapp:
    restart: unless-stopped

```

## Notes

The main thing that makes this work is keeping stacks small and independent. The temptation is to put everything in one giant Compose file — resist it. Isolated stacks mean a broken upgrade on one service doesn't cascade into an afternoon of debugging unrelated things.