Initial commit: homelab configuration and documentation

This commit is contained in:
2025-11-29 19:03:14 +00:00
commit 0769ca6888
72 changed files with 7806 additions and 0 deletions

View File

@@ -0,0 +1,198 @@
# Full corrected Immich/Media stack (Traefik-ready)
# Requires pre-existing external overlay: traefik-public
version: '3.9'
networks:
traefik-public:
external: true
media-backend:
driver: overlay
volumes:
plex_config:
jellyfin_config:
immich_upload:
immich_model_cache:
immich_db:
immich_redis:
homarr_config:
services:
homarr:
image: ghcr.io/ajnart/homarr:latest
networks:
- traefik-public
- media-backend
volumes:
- homarr_config:/app/data
- /var/run/docker.sock:/var/run/docker.sock:ro
environment:
- TZ=America/Chicago
deploy:
placement:
constraints:
- node.labels.leader == true
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.homarr-router.rule=Host(`homarr.sj98.duckdns.org`)"
- "traefik.http.routers.homarr-router.entrypoints=websecure"
- "traefik.http.routers.homarr-router.tls.certresolver=leresolver"
- "traefik.http.services.homarr.loadbalancer.server.port=7575"
- "traefik.docker.network=traefik-public"
resources:
limits:
memory: 512M
reservations:
memory: 128M
restart_policy:
condition: on-failure
max_attempts: 3
plex:
image: plexinc/pms-docker:latest
hostname: plex
networks:
- traefik-public
- media-backend
volumes:
- plex_config:/config
- /mnt/media:/media:ro
environment:
- TZ=America/Chicago
- PLEX_CLAIM=claim-xxxxxxxxxxxx
- ADVERTISE_IP=http://192.168.1.196:32400/
deploy:
placement:
constraints:
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.plex-router.rule=Host(`plex.sj98.duckdns.org`)"
- "traefik.http.routers.plex-router.entrypoints=websecure"
- "traefik.http.routers.plex-router.tls.certresolver=leresolver"
- "traefik.http.services.plex.loadbalancer.server.port=32400"
- "traefik.docker.network=traefik-public"
restart_policy:
condition: on-failure
max_attempts: 3
jellyfin:
image: jellyfin/jellyfin:latest
networks:
- traefik-public
- media-backend
volumes:
- jellyfin_config:/config
- /mnt/media:/media:ro
environment:
- TZ=America/Chicago
deploy:
placement:
constraints:
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.jellyfin-router.rule=Host(`jellyfin.sj98.duckdns.org`)"
- "traefik.http.routers.jellyfin-router.entrypoints=websecure"
- "traefik.http.routers.jellyfin-router.tls.certresolver=leresolver"
- "traefik.http.services.jellyfin.loadbalancer.server.port=8096"
- "traefik.docker.network=traefik-public"
restart_policy:
condition: on-failure
max_attempts: 3
immich-server:
image: ghcr.io/immich-app/immich-server:release
networks:
- traefik-public
- media-backend
volumes:
- /mnt/media/immich:/usr/src/app/upload
- /etc/localtime:/etc/localtime:ro
environment:
- DB_HOSTNAME=immich-db
- DB_USERNAME=immich
- DB_PASSWORD=immich
- DB_DATABASE_NAME=immich
- REDIS_HOSTNAME=immich-redis
- TZ=America/Chicago
depends_on:
- immich-redis
- immich-db
deploy:
placement:
constraints:
- node.labels.leader == true
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.immich-server-router.rule=Host(`immich.sj98.duckdns.org`)"
- "traefik.http.routers.immich-server-router.entrypoints=websecure"
- "traefik.http.routers.immich-server-router.tls.certresolver=leresolver"
- "traefik.http.services.immich-server.loadbalancer.server.port=2283"
- "traefik.docker.network=traefik-public"
# Immich-specific headers and settings
- "traefik.http.routers.immich-server-router.middlewares=immich-headers"
- "traefik.http.middlewares.immich-headers.headers.customrequestheaders.X-Forwarded-Proto=https"
- "traefik.http.services.immich-server.loadbalancer.passhostheader=true"
resources:
limits:
memory: 2G
restart_policy:
condition: on-failure
max_attempts: 3
immich-machine-learning:
image: ghcr.io/immich-app/immich-machine-learning:release
networks:
- media-backend
volumes:
- immich_model_cache:/cache
environment:
- TZ=America/Chicago
depends_on:
- immich-server
deploy:
placement:
constraints:
- node.labels.heavy == true
- node.labels.ai == true
restart_policy:
condition: on-failure
max_attempts: 3
immich-redis:
image: redis:7-alpine
networks:
- media-backend
volumes:
- immich_redis:/data
deploy:
placement:
constraints:
- node.labels.leader == true
- node.role == manager
restart_policy:
condition: on-failure
max_attempts: 3
immich-db:
image: tensorchord/pgvecto-rs:pg14-v0.2.0
networks:
- media-backend
volumes:
- /mnt/database/immich:/var/lib/postgresql/data
environment:
- POSTGRES_PASSWORD=immich
- POSTGRES_USER=immich
- POSTGRES_DB=immich
deploy:
placement:
constraints:
- node.labels.leader == true
- node.role == manager
restart_policy:
condition: on-failure
max_attempts: 3

View File

@@ -0,0 +1,54 @@
version: '3.9'
networks:
traefik-public:
external: true
configs:
traefik_yml:
external: true
name: traefik.yml
services:
traefik:
image: traefik:v3.6.1
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- /mnt/traefik/letsencrypt:/letsencrypt
networks:
- traefik-public
environment:
- DUCKDNS_TOKEN=14880437-fcee-4206-800a-af057cdfffe2
configs:
- source: traefik_yml
target: /etc/traefik/traefik.yml
deploy:
placement:
constraints:
- node.role == manager
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`traefik.sj98.duckdns.org`)"
- "traefik.http.routers.traefik.entrypoints=websecure"
- "traefik.http.routers.traefik.tls.certresolver=leresolver"
- "traefik.http.routers.traefik.service=api@internal"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
whoami:
image: traefik/whoami
networks:
- traefik-public
deploy:
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.sj98.duckdns.org`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
- "traefik.http.routers.whoami.tls.certresolver=leresolver"
- "traefik.http.services.whoami.loadbalancer.server.port=80"

View File

@@ -0,0 +1,100 @@
version: '3.9'
networks:
traefik-public:
external: true
productivity-backend:
driver: overlay
volumes:
nextcloud_data:
nextcloud_db:
nextcloud_redis:
services:
nextcloud-db:
image: postgres:15-alpine
volumes:
- /mnt/database/nextcloud:/var/lib/postgresql/data
environment:
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=nextcloud # Replace with a secure password in production
networks:
- productivity-backend
deploy:
placement:
constraints:
- node.labels.leader == true
restart_policy:
condition: on-failure
nextcloud-redis:
image: redis:7-alpine
volumes:
- nextcloud_redis:/data
networks:
- productivity-backend
deploy:
placement:
constraints:
- node.labels.leader == true
restart_policy:
condition: on-failure
nextcloud:
image: nextcloud:latest
volumes:
- /mnt/nextcloud_apps:/var/www/html/custom_apps
- /mnt/nextcloud_config:/var/www/html/config
- /mnt/nextcloud_data:/var/www/html/data
environment:
- POSTGRES_HOST=nextcloud-db
- POSTGRES_DB=nextcloud
- POSTGRES_USER=nextcloud
- POSTGRES_PASSWORD=nextcloud # Replace with a secure password in production
- REDIS_HOST=nextcloud-redis
- NEXTCLOUD_ADMIN_USER=admin # Replace with your desired admin username
- NEXTCLOUD_ADMIN_PASSWORD=password # Replace with a secure password
- NEXTCLOUD_TRUSTED_DOMAINS=nextcloud.sj98.duckdns.org
- OVERWRITEPROTOCOL=https
- OVERWRITEHOST=nextcloud.sj98.duckdns.org
- TRUSTED_PROXIES=172.16.0.0/12
depends_on:
- nextcloud-db
- nextcloud-redis
networks:
- traefik-public
- productivity-backend
deploy:
placement:
constraints:
- node.labels.leader == true
resources:
limits:
memory: 2G
reservations:
memory: 512M
restart_policy:
condition: on-failure
labels:
- "traefik.enable=true"
- "traefik.http.routers.nextcloud.rule=Host(`nextcloud.sj98.duckdns.org`)"
- "traefik.http.routers.nextcloud.entrypoints=websecure"
- "traefik.http.routers.nextcloud.tls.certresolver=leresolver"
- "traefik.http.services.nextcloud.loadbalancer.server.port=80"
- "traefik.docker.network=traefik-public"
# Nextcloud-specific middlewares
- "traefik.http.routers.nextcloud.middlewares=nextcloud-chain"
- "traefik.http.middlewares.nextcloud-chain.chain.middlewares=nextcloud-caldav,nextcloud-headers"
# CalDAV/CardDAV redirect
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.regex=^https://(.*)/.well-known/(card|cal)dav"
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.replacement=https://$$1/remote.php/dav/"
- "traefik.http.middlewares.nextcloud-caldav.redirectregex.permanent=true"
# Security headers
- "traefik.http.middlewares.nextcloud-headers.headers.stsSeconds=31536000"
- "traefik.http.middlewares.nextcloud-headers.headers.stsIncludeSubdomains=true"
- "traefik.http.middlewares.nextcloud-headers.headers.stsPreload=true"
- "traefik.http.middlewares.nextcloud-headers.headers.forceSTSHeader=true"
- "traefik.http.middlewares.nextcloud-headers.headers.customFrameOptionsValue=SAMEORIGIN"
- "traefik.http.middlewares.nextcloud-headers.headers.customResponseHeaders.X-Robots-Tag=noindex,nofollow"