Be Your Own Cloud code · APPENDICES
Appendix A Every Compose File in One Place
Before Any of These: The Shared Proxy Network
# p. 126
docker network create proxy # once per server, before Chapter 8 Host Ports at a Glance
| Service | Host ports | Chapter |
|---|---|---|
| Nginx Proxy Manager | 80, 443, 81 (admin) | 8 |
| Pi-hole + Unbound | 53 tcp/udp, 8081 (admin) | 10 |
| Vaultwarden | none (proxy network) | 11 |
| Nextcloud AIO | 8080 (setup wizard), 11000 | 12 |
| Immich | 2283 | 13 |
| Jellyfin | 8096 | 14 |
| SearXNG | 8888 | 15 |
| Sanitiser + Presidio | none (proxy network) | 16 |
| Ollama + Open WebUI | 127.0.0.1:11434, 8090 | 16 |
| Uptime Kuma | 3001 | 17 |
| PrivateBin | 8280 | 17 |
| wg-easy | 51820/udp, 51821 | 20 |
| ntfy | 8380 | 21 |
| Authelia | none (proxy network) | 25 |
Nginx Proxy Manager — /opt/stacks/npm/ (Chapter 8)
# p. 127
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
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
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
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
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
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
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
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
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
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
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
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
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
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.