Files
Homelab/docker-compose.yaml

878 lines
22 KiB
YAML

# Common configurations for re-use
x-logging: &default-logging
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
x-security: &default-security
security_opt:
- no-new-privileges:true
services:
# --- MANAGEMENT & INFRASTRUCTURE ---
glances:
image: nicolargo/glances:ubuntu-latest-full
container_name: glances
restart: unless-stopped
network_mode: host
environment:
- GLANCES_OPT=-w
devices:
- /dev/dri:/dev/dri
volumes:
- /data:/data:ro
- /docker:/docker:ro
- /docker:/docker-local:ro
- /run/user/1000/podman/podman.sock:/run/user/1000/podman/podman.sock
- /var/run/docker.sock:/var/run/docker.sock
logging: *default-logging
portainer:
image: portainer/portainer-ce:2.21.5
container_name: portainer
restart: unless-stopped
networks:
- web_net
ports:
- "8000:8000"
- "9443:9443"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- portainer_data:/data
deploy:
resources:
limits:
memory: 512M
logging: *default-logging
watchtower:
image: containrrr/watchtower
container_name: watchtower
restart: unless-stopped
networks:
- internal_net
environment:
- WATCHTOWER_CLEANUP=true
- WATCHTOWER_POLL_INTERVAL=86400
volumes:
- /var/run/docker.sock:/var/run/docker.sock
logging: *default-logging
homepage:
image: ghcr.io/gethomepage/homepage:latest
container_name: homepage
restart: unless-stopped
networks:
- web_net
ports:
- "7575:3000"
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
- HOMEPAGE_ALLOWED_HOSTS=${HOMEPAGE_ALLOWED_HOSTS}
volumes:
- /docker/Homepage/config/icons:/app/public/icons
- /docker/Homepage/config/images:/app/public/images
- /docker/Homepage/config:/app/config
logging: *default-logging
dockerproxy:
image: ghcr.io/tecnativa/docker-socket-proxy:latest
container_name: dockerproxy
restart: unless-stopped
networks:
- internal_net
ports:
- "127.0.0.1:2375:2375"
environment:
- CONTAINERS=1
- SERVICES=1
- TASKS=1
- EVENTS=1
- PING=1
- VERSION=1
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
logging: *default-logging
newt:
image: fosrl/newt:latest
container_name: newt
restart: unless-stopped
environment:
- PANGOLIN_ENDPOINT=https://png.kansaigaijin.com
- NEWT_ID=cuvfw5hnsszh0gc
- NEWT_SECRET=iitbnuk2cevm40lt1xtrgmnehce4f2bdk4rnllj6ebeznf6h
- LOG_LEVEL=DEBUG
npm:
image: 'jc21/nginx-proxy-manager:latest'
container_name: npm
restart: unless-stopped
ports:
- '80:80' # Public HTTP Port
- '443:443' # Public HTTPS Port
- '81:81' # Admin Web Port
environment:
DB_MYSQL_HOST: "npm-db"
DB_MYSQL_PORT: 3306
DB_MYSQL_USER: "npm"
DB_MYSQL_PASSWORD: ${NPM_PASSWORD}
DB_MYSQL_NAME: "npm"
volumes:
- ./npm/data:/data
- ./npm/letsencrypt:/etc/letsencrypt
networks:
web_net:
db_net:
depends_on:
- npm-db
npm-db:
image: 'jc21/mariadb-aria:latest'
container_name: npm-db
restart: unless-stopped
environment:
MYSQL_ROOT_PASSWORD: ${NPM_PASSWORD}
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: ${NPM_PASSWORD}
networks:
db_net:
volumes:
- ./npm/mysql:/var/lib/mysql
# --- MEDIA & ARRS (Static IPs 172.20.0.x) ---
prowlarr:
image: lscr.io/linuxserver/prowlarr:latest
container_name: prowlarr
restart: unless-stopped
networks:
media_net:
ports:
- "9696:9696"
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- /docker/Arrs/Prowlarr/config:/config
logging: *default-logging
flaresolverr:
image: ghcr.io/flaresolverr/flaresolverr:latest
container_name: flaresolverr
restart: unless-stopped
networks:
media_net:
ports:
- "127.0.0.1:8191:8191"
environment:
- TZ=${TZ}
logging: *default-logging
qbittorrent:
image: lscr.io/linuxserver/qbittorrent:latest
container_name: qbittorrent
restart: unless-stopped
networks:
media_net:
ports:
- "56881:6881"
- "56881:6881/udp"
- "7070:8080"
environment:
- TZ=${TZ}
- WEBUI_PORT=8080
- PUID=${PUID}
- PGID=${PGID}
volumes:
- /mnt/Nas-Storage/data:/data
- /docker/qBittorrent/config:/config
logging: *default-logging
radarr:
image: lscr.io/linuxserver/radarr:latest
container_name: radarr
restart: unless-stopped
networks:
media_net:
ports:
- "7878:7878"
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- /mnt/Nas-Storage/data/:/data
- /docker/Arrs/Radarr/config:/config
logging: *default-logging
sonarr:
image: lscr.io/linuxserver/sonarr:latest
container_name: sonarr
restart: unless-stopped
networks:
media_net:
ports:
- "8989:8989"
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- /mnt/Nas-Storage/data:/data
- /docker/Arrs/Sonarr/config:/config
logging: *default-logging
lidarr:
image: ghcr.io/linuxserver-labs/prarr:lidarr-plugins
container_name: lidarr
restart: unless-stopped
networks:
media_net:
ports:
- "8686:8686"
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${PGID}
volumes:
- /mnt/Nas-Storage/data:/data
- /docker/Arrs/Lidarr/config:/config
logging: *default-logging
bazarr:
image: lscr.io/linuxserver/bazarr:latest
container_name: bazarr
restart: unless-stopped
networks:
media_net:
ports:
- "6767:6767"
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TZ}
volumes:
- /mnt/Nas-Storage/data:/data
- /docker/Arrs/Bazarr/config:/config
logging: *default-logging
jellyseerr:
image: fallenbagel/jellyseerr:latest
container_name: jellyseerr
restart: unless-stopped
networks:
media_net:
ports:
- "5055:5055"
environment:
- LOG_LEVEL=debug
- TZ=${TZ}
volumes:
- /docker/Arrs/Jellyseerr/config:/app/config
logging: *default-logging
jellyfin:
image: jellyfin/jellyfin:latest
container_name: jellyfin
restart: unless-stopped
networks:
- media_net
- internal_net
ports:
- "8096:8096"
environment:
- JELLYFIN_PublishedServerUrl=${JELLYFIN_URL}
- TZ=${TZ}
- PUID=${PUID}
- PGID=${PGID}
group_add:
- "104" # This matches the GID from your ls -l output
devices:
- /dev/dri/renderD128:/dev/dri/renderD128
volumes:
- /mnt/Nas-Storage/data:/data
- /docker/Arrs/Jellyfin/cache:/cache
- /docker/Arrs/Jellyfin/config:/config
logging: *default-logging
slskd:
image: slskd/slskd
container_name: slskd
restart: unless-stopped
networks:
media_net:
ports:
- "5030:5030"
- "50300:50300"
- "5031:5031"
hostname: slskd
environment:
- TZ=${TZ}
- SLSKD_REMOTE_CONFIGURATION=true
- SLSKD_USERNAME=${SLSKD_USERNAME}
- SLSKD_PASSWORD=${SLSKD_PASSWORD}
volumes:
- /mnt/Nas-Storage/data/torrents/soulsync/complete:/downloads
- /mnt/Nas-Storage/data/torrents/soulsync/incomplete:/incomplete
- /docker/slskd:/app
logging: *default-logging
soulsync-webui:
image: boulderbadgedad/soulsync:latest
container_name: soulsync-webui
restart: unless-stopped
networks:
media_net:
ports:
- "8887:8008"
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
- TZ=${TZ}
- PUID=${PUID}
- PGID=${PGID}
- FLASK_ENV=production
volumes:
- /mnt/Nas-Storage/data/media/music:/music:ro
- /mnt/Nas-Storage/data/torrents/soulsync:/app/downloads
- /docker/soulsync/logs:/app/logs
- docker_soulsync:/app/database
logging: *default-logging
# --- DOCUMENT & AI SUITE ---
onlyoffice:
image: onlyoffice/documentserver:latest
container_name: onlyoffice-docs
restart: always
ports:
- "8091:80"
environment:
- JWT_ENABLED=true
- JWT_SECRET=${OO_JWT_SECRET}
- JWT_HEADER=Authorization
- ALLOW_PRIVATE_IP_ADDRESS=true
- USE_UNAUTHORIZED_STORAGE=true
volumes:
- docker_onlyoffice:/var/lib/onlyoffice
- docker_onlyoffice:/var/www/onlyoffice/Data
- docker_onlyoffice:/var/log/onlyoffice
paperless-db:
image: mariadb:11
container_name: paperless-db
restart: unless-stopped
networks:
- db_net
environment:
- MARIADB_ROOT_PASSWORD=${PAPERLESS_DB_ROOT_PASSWORD}
- MARIADB_DATABASE=${PAPERLESS_DB_NAME}
- MARIADB_USER=${PAPERLESS_DB_USER}
- MARIADB_PASSWORD=${PAPERLESS_DB_PASSWORD}
volumes:
- docker_dbdata:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 3
logging: *default-logging
paperless-broker:
image: redis:8
container_name: paperless-broker
restart: unless-stopped
networks:
- internal_net
volumes:
- docker_redisdata:/data
logging: *default-logging
paperless-tika:
image: apache/tika:latest
container_name: paperless-tika
restart: unless-stopped
networks:
- internal_net
logging: *default-logging
paperless-gotenberg:
image: gotenberg/gotenberg:8.20
container_name: paperless-gotenberg
restart: unless-stopped
networks:
- internal_net
command: ["gotenberg", "--chromium-disable-javascript=true", "--chromium-allow-list=file:///tmp/.*"]
logging: *default-logging
paperless-webserver:
image: ghcr.io/paperless-ngx/paperless-ngx:latest
container_name: paperless-webserver
restart: unless-stopped
networks:
- web_net
- db_net
- internal_net
ports:
- "8100:8000"
environment:
- PAPERLESS_DBENGINE=mariadb
- PAPERLESS_DBHOST=paperless-db
- PAPERLESS_DBPASS=${PAPERLESS_DB_PASSWORD}
- PAPERLESS_DBUSER=${PAPERLESS_DB_USER}
- PAPERLESS_DBPORT=3306
- PAPERLESS_REDIS=redis://paperless-broker:6379
- PAPERLESS_TIKA_ENDPOINT=http://paperless-tika:9998
- PAPERLESS_TIKA_GOTENBERG_ENDPOINT=http://paperless-gotenberg:3000
- PAPERLESS_SECRET_KEY=${PAPERLESS_SECRET_KEY}
- PAPERLESS_URL=${PAPERLESS_URL}
- PAPERLESS_TIME_ZONE=${TZ}
- PAPERLESS_OCR_LANGUAGE=eng
volumes:
- /docker/paperless/consume:/usr/src/paperless/consume
- /docker/paperless/data:/usr/src/paperless/data
- /docker/paperless/export:/usr/src/paperless/export
- /docker/paperless/media:/usr/src/paperless/media
depends_on:
paperless-db:
condition: service_healthy
paperless-broker:
condition: service_started
logging: *default-logging
paperless-ai:
image: clusterzx/paperless-ai
container_name: paperless-ai
restart: unless-stopped
networks:
- internal_net
ports:
- "127.0.0.1:3040:3000"
environment:
- PUID=${PUID}
- PGID=${PGID}
- RAG_SERVICE_URL=http://localhost:8000
- RAG_SERVICE_ENABLED=true
volumes:
- docker_aidata:/app/data
depends_on:
- paperless-webserver
logging: *default-logging
stirling-pdf:
image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:latest
container_name: stirling-PDF
restart: unless-stopped
networks:
- web_net
ports:
- "8090:8080"
environment:
- UI_APPNAME=Stirling-PDF
- SHOW_SURVEY=true
- SYSTEM_MAXFILESIZE=100
- PUID=${PUID}
- PGID=${PGID}
volumes:
- /docker/stirling/config:/configs
- /docker/stirling/data:/usr/share/tessdata
- /docker/stirling/logs:/logs
logging: *default-logging
open-webui:
image: ghcr.io/open-webui/open-webui:main
container_name: open-webui
restart: unless-stopped
networks:
- web_net
- internal_net
ports:
- "3000:8080"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- open-webui:/app/backend/data
logging: *default-logging
litellm:
image: docker.litellm.ai/berriai/litellm:main-latest
container_name: litellm
restart: unless-stopped
networks:
- internal_net
- db_net
ports:
- "127.0.0.1:4000:4000"
environment:
- GROQ_API_KEY=${GROQ_API_KEY}
- DATABASE_URL=postgresql://litellm:litellm_pass@litellm-postgres:5432/litellm_db
- LITELLM_MASTER_KEY=${LITELLM_MASTER_KEY}
volumes:
- /docker/litellm/config.yaml:/app/config.yaml
depends_on:
litellm-postgres:
condition: service_healthy
logging: *default-logging
litellm-postgres:
image: postgres:15
container_name: litellm-postgres
restart: unless-stopped
networks:
- db_net
environment:
- POSTGRES_USER=litellm
- POSTGRES_PASSWORD=litellm_pass
- POSTGRES_DB=litellm_db
volumes:
- /docker/litellm/postgres-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U litellm -d litellm_db"]
interval: 10s
logging: *default-logging
# --- PHOTO & DATA MGMT ---
immich-server:
image: ghcr.io/immich-app/immich-server:release
container_name: immich_server
restart: unless-stopped
networks:
- web_net
- db_net
- internal_net
ports:
- "2283:2283"
environment:
- TZ=${TZ}
- DB_USERNAME=postgres
- DB_PASSWORD=${IMMICH_POSTGRES_PASSWORD}
- DB_DATABASE_NAME=immich
- DB_HOSTNAME=immich-postgres
- REDIS_HOSTNAME=immich-redis
- UPLOAD_LOCATION=/data
volumes:
# LOCAL (SSD) - Config, Thumbs, Profile, and Backups
- /docker/immich:/usr/src/app/upload/library
- /docker/immich/thumbs:/usr/src/app/upload/thumbs
- /docker/immich/profile:/usr/src/app/upload/profile
- /docker/immich/backups:/usr/src/app/upload/backups
- /docker/immich/encoded-video:/usr/src/app/upload/encoded-video
- /docker/immich/upload:/usr/src/app/upload/upload
depends_on:
immich-postgres:
condition: service_healthy
logging: *default-logging
immich-postgres:
image: ghcr.io/immich-app/postgres:14-vectorchord0.4.3-pgvectors0.2.0
container_name: immich_postgres
restart: unless-stopped
networks:
- db_net
environment:
- POSTGRES_USER=postgres
- POSTGRES_DB=immich
- POSTGRES_PASSWORD=${IMMICH_POSTGRES_PASSWORD}
volumes:
- /docker/immich/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 10s
logging: *default-logging
immich-redis:
image: valkey/valkey:8-bookworm
container_name: immich_redis
restart: unless-stopped
networks:
- internal_net
logging: *default-logging
syncthing:
image: syncthing/syncthing
container_name: syncthing
restart: unless-stopped
networks:
- web_net
- internal_net
ports:
- "21027:21027/udp"
- "22000:22000"
- "8384:8384"
volumes:
- /docker/obsidian/vaults:/var/syncthing/obsidian
- /docker/syncthing:/var/syncthing
logging: *default-logging
# --- DEVELOPMENT & APPS ---
gitea:
image: docker.gitea.com/gitea:1.25.3
container_name: gitea
restart: unless-stopped
networks:
- web_net
- db_net
ports:
- "222:22"
- "8418:3000"
environment:
- GITEA__database__HOST=gitea-db:3306
- GITEA__database__NAME=gitea
- GITEA__database__USER=gitea
- GITEA__database__PASSWD=gitea
- GITEA__database__DB_TYPE=mysql
volumes:
- /docker/gitea/data:/data
depends_on:
gitea-db:
condition: service_healthy
logging: *default-logging
gitea-db:
image: mysql:8
container_name: gitea-db
restart: unless-stopped
networks:
- db_net
environment:
- MYSQL_ROOT_PASSWORD=gitea
- MYSQL_USER=gitea
- MYSQL_PASSWORD=gitea
- MYSQL_DATABASE=gitea
volumes:
- /docker/gitea/mysql:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"]
interval: 10s
logging: *default-logging
# --- GENEALOGY (Gramps) ---
grampsweb-redis:
image: redis:7.2.4-alpine
container_name: grampsweb_redis
restart: unless-stopped
networks:
- internal_net
logging: *default-logging
grampsweb-jamie:
image: ghcr.io/gramps-project/grampsweb:latest
container_name: grampsweb-jamie
restart: unless-stopped
networks:
- web_net
- internal_net
ports:
- "5511:5000"
environment:
- GRAMPSWEB_TREE=Miller Tree
- GRAMPSWEB_CELERY_CONFIG__broker_url=redis://grampsweb_redis:6379/0
volumes:
- /docker/gramps-jamie/cache:/app/cache
- /docker/gramps-jamie/db:/app/data/.gramps/grampsdb
- /docker/gramps-jamie/media:/app/media
depends_on:
- grampsweb-redis
logging: *default-logging
grampsweb-helen:
image: ghcr.io/gramps-project/grampsweb:latest
container_name: grampsweb-helen
restart: unless-stopped
networks:
- web_net
- internal_net
ports:
- "5512:5000"
environment:
- GRAMPSWEB_CELERY_CONFIG__broker_url=redis://grampsweb_redis:6379/0
volumes:
- /docker/gramps-helen/cache:/app/cache
- /docker/gramps-helen/db:/app/data/.gramps/grampsdb
- /docker/gramps-helen/media:/app/media
depends_on:
- grampsweb-redis
logging: *default-logging
# --- UTILITIES ---
speedtest-tracker:
image: lscr.io/linuxserver/speedtest-tracker:latest
container_name: speedtest-tracker
restart: unless-stopped
networks:
- web_net
- db_net
ports:
- "8180:80"
environment:
- SPEEDTEST_SCHEDULE="0 */6 * * *"
- SPEEDTEST_SERVERS=7317
- DB_HOST=speedtest-db
- DB_DATABASE=${SPEEDTEST_DB_NAME}
- DB_PASSWORD=${SPEEDTEST_DB_PASSWORD}
- DB_CONNECTION=mariadb
- DB_USERNAME=${SPEEDTEST_DB_USER}
- APP_KEY=${SPEEDTEST_APP_KEY}
volumes:
- /docker/speedtest-tracker/data:/config
depends_on:
speedtest-db:
condition: service_healthy
logging: *default-logging
speedtest-db:
image: mariadb:11
container_name: speedtest-db
restart: unless-stopped
networks:
- db_net
environment:
- MYSQL_USER=${SPEEDTEST_DB_USER}
- MYSQL_PASSWORD=${SPEEDTEST_DB_PASSWORD}
- MYSQL_DATABASE=${SPEEDTEST_DB_NAME}
- MYSQL_RANDOM_ROOT_PASSWORD=true
volumes:
- /docker/speedtest-tracker/db:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
logging: *default-logging
rustdesk-hbbs:
image: rustdesk/rustdesk-server:latest
container_name: hbbs
network_mode: host
restart: unless-stopped
command: hbbs
volumes:
- /docker/rustdesk/data:/root
logging: *default-logging
rustdesk-hbbr:
image: rustdesk/rustdesk-server:latest
container_name: hbbr
network_mode: host
restart: unless-stopped
command: hbbr
volumes:
- /docker/rustdesk/data:/root
logging: *default-logging
redbot:
image: phasecorex/red-discordbot
container_name: redbot
restart: unless-stopped
networks:
- internal_net
environment:
- TOKEN=${REDBOT_TOKEN}
volumes:
- /docker/redbot:/data
logging: *default-logging
iperf3-server:
image: networkstatic/iperf3
container_name: iperf3-server
restart: unless-stopped
networks:
- internal_net
ports:
- "5201:5201"
command: -s
logging: *default-logging
# --- FINANCE ---
wygiwyh-web:
image: eitchtee/wygiwyh:latest
container_name: WYGIWYH
environment:
# --- DATABASE SETTINGS ---
- SQL_ENGINE=django.db.backends.postgresql
- SQL_HOST=wygiwyh-db # Matches the service name below
- SQL_PORT=5432
- SQL_DATABASE=${WYGIWYH_DB_DATABASE}
- SQL_USER=${WYGIWYH_DB_USER}
- SQL_PASSWORD=${WYGIWYH_DB_PASSWORD}
# --- APP SETTINGS ---
- SECRET_KEY=${WYGIWYH_SECRET_KEY}
- DJANGO_ALLOWED_HOSTS=${WYGIWYH_ALLOWED_HOSTS}
- CSRF_TRUSTED_ORIGINS=${WYGIWYH_CSRF_TRUSTED_ORIGINS}
- WYGIWYH_URL=${WYGIWYH_URL}
networks:
- web_net
- db_net
ports:
- 9008:8000
depends_on:
- wygiwyh-db
restart: unless-stopped
wygiwyh-db:
image: postgres:15-bookworm
container_name: WYGIWYH-db
environment:
- POSTGRES_USER=${WYGIWYH_DB_USER}
- POSTGRES_PASSWORD=${WYGIWYH_DB_PASSWORD}
- POSTGRES_DB=${WYGIWYH_DB_DATABASE}
networks:
- db_net
volumes:
- ./wygiwyh/postgres_data:/var/lib/postgresql/data/
# --- MUSIC & SCROBBLING ---
maloja:
image: krateng/maloja:latest
container_name: maloja
restart: unless-stopped
networks:
- internal_net
- web_net
ports:
- "42010:42010"
volumes:
- /docker/maloja/config:/etc/maloja
- /docker/maloja/data:/var/lib/maloja
logging: *default-logging
multi-scrobbler:
image: foxxmd/multi-scrobbler:latest
container_name: multi-scrobbler
restart: unless-stopped
networks:
- internal_net
ports:
- "9078:9078"
environment:
- MALOJA_URL=http://maloja:42010
volumes:
- /docker/scrobble/config:/config
logging: *default-logging
networks:
media_net:
name: media_net
driver: bridge
db_net:
name: db_net
internal: true
web_net:
name: web_net
driver: bridge
internal_net:
name: internal_net
driver: bridge
volumes:
docker_aidata:
docker_dbdata:
docker_model-cache:
docker_onlyoffice:
docker_redisdata:
docker_soulsync:
open-webui:
portainer_data: