Be Your Own Cloud code · APPENDICES

Appendix A Every Compose File in One Place

15 blocks · pp. 126–133 in the paperback

Before Any of These: The Shared Proxy Network

# p. 126

Run Terminal, on the server — once
docker network create proxy   # once per server, before Chapter 8

Host Ports at a Glance

ServiceHost portsChapter
Nginx Proxy Manager80, 443, 81 (admin)8
Pi-hole + Unbound53 tcp/udp, 8081 (admin)10
Vaultwardennone (proxy network)11
Nextcloud AIO8080 (setup wizard), 1100012
Immich228313
Jellyfin809614
SearXNG888815
Sanitiser + Presidionone (proxy network)16
Ollama + Open WebUI127.0.0.1:11434, 809016
Uptime Kuma300117
PrivateBin828017
wg-easy51820/udp, 5182120
ntfy838021
Authelianone (proxy network)25

Nginx Proxy Manager — /opt/stacks/npm/ (Chapter 8)

# p. 127

File /opt/stacks/npm/docker-compose.yml
services:
  npm:
    image: 'jc21/nginx-proxy-manager:2.16.0'
    container_name: npm
    restart: unless-stopped
    ports:
      - '80:80'
      - '443:443'
      - '81:81'   # admin UI
    volumes:
      - ./data:/data
      - ./letsencrypt:/etc/letsencrypt
    networks:
      - proxy

networks:
  proxy:
    external: true

Pi-hole + Unbound — /opt/stacks/pihole/ (Chapter 10)

# p. 127

File /opt/stacks/pihole/docker-compose.yml
services:
  unbound:
    image: klutchell/unbound:1.26.1
    container_name: unbound
    restart: unless-stopped
    networks:
      dns:
        ipv4_address: 172.30.0.2
  pihole:
    image: pihole/pihole:2026.09.0
    container_name: pihole
    restart: unless-stopped
    depends_on:
      - unbound
    environment:
      - TZ=Your/Timezone
      # web admin login, from .env
      - FTLCONF_webserver_api_password=${PIHOLE_PASSWORD}
      # Unbound's fixed address, below
      - FTLCONF_dns_upstreams=172.30.0.2#53
      # answer the LAN and tailnet, not just Docker's own network
      - FTLCONF_dns_listeningMode=all
    volumes:
      - ./etc-pihole:/etc/pihole
    ports:
      - '53:53/tcp'
      - '53:53/udp'
      - '8081:80'   # web admin
    networks:
      dns:
        ipv4_address: 172.30.0.3

networks:
  dns:
    ipam:
      config:
        - subnet: 172.30.0.0/24

Vaultwarden — /opt/stacks/vaultwarden/ (Chapter 11)

# p. 128

File /opt/stacks/vaultwarden/docker-compose.yml
services:
  vaultwarden:
    image: vaultwarden/server:1.37.3
    container_name: vaultwarden
    restart: unless-stopped
    volumes:
      - ./data:/data
    environment:
      # set in .env: true now, false once your account exists
      - SIGNUPS_ALLOWED=${SIGNUPS_ALLOWED}
      - ADMIN_TOKEN=${ADMIN_TOKEN}
    networks:
      # Chapter 8's shared network -- no host port published at all
      - proxy

networks:
  proxy:
    external: true

Nextcloud AIO — /opt/stacks/nextcloud/ (Chapter 12)

# p. 129

File /opt/stacks/nextcloud/docker-compose.yml
services:
  nextcloud-aio-mastercontainer:
    # the one deliberate :latest in this book -- see below
    image: ghcr.io/nextcloud-releases/all-in-one:latest
    container_name: nextcloud-aio-mastercontainer
    restart: unless-stopped
    ports:
      - '8080:8080'   # AIO's own setup wizard -- one-time use
    environment:
      # reverse-proxy mode: NPM forwards here
      - APACHE_PORT=11000
      - APACHE_IP_BINDING=0.0.0.0
      # files on bulk storage (Chapter 7)
      - NEXTCLOUD_DATADIR=/mnt/media/nextcloud-data
      # your subdomain is LAN-only -- see below
      - SKIP_DOMAIN_VALIDATION=true
    volumes:
      - nextcloud_aio_mastercontainer:/mnt/docker-aio-config
      - /var/run/docker.sock:/var/run/docker.sock:ro

volumes:
  nextcloud_aio_mastercontainer:
    name: nextcloud_aio_mastercontainer

Immich — /opt/stacks/immich/ (Chapter 13)

# p. 129

File /opt/stacks/immich/docker-compose.yml
services:
  immich-server:
    image: ghcr.io/immich-app/immich-server:${IMMICH_VERSION}
    container_name: immich_server
    restart: unless-stopped
    volumes:
      - ${UPLOAD_LOCATION}:/data   # your photo library -- set in .env
      - /etc/localtime:/etc/localtime:ro
    env_file:
      - .env
    depends_on:
      - redis
      - database
    ports:
      - '2283:2283'
  immich-machine-learning:
    image: ghcr.io/immich-app/immich-machine-learning:${IMMICH_VERSION}
    container_name: immich_machine_learning
    restart: unless-stopped
    volumes:
      - model-cache:/cache
    env_file:
      - .env
  redis:
    image: docker.io/valkey/valkey:9
    container_name: immich_redis
    restart: unless-stopped
  database:
    image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
    container_name: immich_postgres
    restart: unless-stopped
    environment:
      - POSTGRES_PASSWORD=${DB_PASSWORD}
      - POSTGRES_USER=${DB_USERNAME}
      - POSTGRES_DB=${DB_DATABASE_NAME}
      - POSTGRES_INITDB_ARGS=--data-checksums
    volumes:
      - ./pgdata:/var/lib/postgresql/data
    shm_size: 128mb
volumes:
  model-cache:

Jellyfin — /opt/stacks/jellyfin/ (Chapter 14)

# p. 130

File /opt/stacks/jellyfin/docker-compose.yml
services:
  jellyfin:
    image: jellyfin/jellyfin:12.1
    container_name: jellyfin
    restart: unless-stopped
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /mnt/media/movies:/media
    ports:
      - '8096:8096'
    # devices:                  # hardware transcoding, Chapter 14
    #   - /dev/dri:/dev/dri       # Intel Quicksync

SearXNG — /opt/stacks/searxng/ (Chapter 15)

# p. 130

File /opt/stacks/searxng/docker-compose.yml
services:
  searxng:
    image: searxng/searxng:2026.9.29-4e2c1ea7f
    container_name: searxng
    restart: unless-stopped
    volumes:
      - ./searxng:/etc/searxng
    ports:
      - '8888:8080'

Sanitiser (add Presidio to this file if you want it) — /opt/stacks/sanitizer/ (Chapter 16)

# p. 131

File /opt/stacks/sanitizer/docker-compose.yml
services:
  sanitizer:
    build: .
    container_name: sanitizer
    restart: unless-stopped
    networks:
      - proxy   # Chapter 8's shared network -- no host port published

networks:
  proxy:
    external: true

Ollama + Open WebUI — /opt/stacks/ollama/ (Chapter 16)

# p. 131

File /opt/stacks/ollama/docker-compose.yml
services:
  ollama:
    image: ollama/ollama:0.35.0
    container_name: ollama
    restart: unless-stopped
    volumes:
      - ./ollama-data:/root/.ollama
    ports:
      - '127.0.0.1:11434:11434'
    # Uncomment for NVIDIA GPU passthrough:
    # deploy:
    #   resources:
    #     reservations:
    #       devices:
    #         - driver: nvidia
    #           count: 1
    #           capabilities: [gpu]

  open-webui:
    image: ghcr.io/open-webui/open-webui:v0.11.4
    container_name: open-webui
    restart: unless-stopped
    depends_on:
      - ollama
    environment:
      - OLLAMA_BASE_URL=http://ollama:11434
    volumes:
      - ./webui-data:/app/backend/data
    ports:
      - '8090:8080'

Uptime Kuma — /opt/stacks/uptime-kuma/ (Chapter 17)

# p. 132

File /opt/stacks/uptime-kuma/docker-compose.yml
services:
  uptime-kuma:
    image: louislam/uptime-kuma:2.5.5
    container_name: uptime-kuma
    restart: unless-stopped
    volumes:
      - ./data:/app/data
    ports:
      - '3001:3001'

PrivateBin — /opt/stacks/privatebin/ (Chapter 17)

# p. 132

File /opt/stacks/privatebin/docker-compose.yml
services:
  privatebin:
    image: privatebin/nginx-fpm-alpine:2.0.6
    container_name: privatebin
    restart: unless-stopped
    volumes:
      - ./data:/srv/data
    ports:
      - '8280:8080'

wg-easy (self-hosted WireGuard) — /opt/stacks/wg-easy/ (Chapter 20)

# p. 132

File /opt/stacks/wg-easy/docker-compose.yml
services:
  wg-easy:
    image: ghcr.io/wg-easy/wg-easy:15.4.0
    container_name: wg-easy
    restart: unless-stopped
    environment:
      # web UI over plain http -- LAN only, never forward 51821
      - INSECURE=true
      - DISABLE_IPV6=true
    volumes:
      - ./etc-wireguard:/etc/wireguard
      - /lib/modules:/lib/modules:ro
    ports:
      - '51820:51820/udp'
      - '51821:51821/tcp'   # web UI, and its first-run setup wizard
    cap_add:
      - NET_ADMIN
      - SYS_MODULE
    sysctls:
      - net.ipv4.ip_forward=1
      - net.ipv4.conf.all.src_valid_mark=1

ntfy — /opt/stacks/ntfy/ (Chapter 21)

# p. 133

File /opt/stacks/ntfy/docker-compose.yml
services:
  ntfy:
    image: binwiederhier/ntfy:v2.28.0
    container_name: ntfy
    command: serve
    restart: unless-stopped
    environment:
      # your server's address
      - NTFY_BASE_URL=http://192.168.1.50:8380
      # iPhone only -- see below
      - NTFY_UPSTREAM_BASE_URL=https://ntfy.sh
    volumes:
      - ./data:/var/cache/ntfy
    ports:
      - '8380:80'

Authelia — /opt/stacks/authelia/ (Chapter 25)

# p. 133

File /opt/stacks/authelia/docker-compose.yml
services:
  authelia:
    image: authelia/authelia:4.39.28
    container_name: authelia
    restart: unless-stopped
    volumes:
      - ./config:/config
    environment:
      - TZ=Your/Timezone
      - AUTHELIA_IDENTITY_VALIDATION_RESET_PASSWORD_JWT_SECRET=${JWT_SECRET}
      - AUTHELIA_SESSION_SECRET=${SESSION_SECRET}
      - AUTHELIA_STORAGE_ENCRYPTION_KEY=${STORAGE_ENCRYPTION_KEY}
    networks:
      # Chapter 8's shared network -- the proxy reaches it as
      # authelia:9091
      - proxy
networks:
  proxy:
    external: true

Reproduced exactly from Be Your Own Cloud, Appendix A. The book explains what each line does and what to do when it doesn't work. About the book

Something here not working as the book describes? Tell the author.