From 13c2599776c487c38189714085410acb1ea1f22b Mon Sep 17 00:00:00 2001 From: Jamie Miller Date: Sat, 27 Dec 2025 13:58:19 +0000 Subject: [PATCH] Initial Docker Compose infrastructure backup --- .env.example | 90 +++++ .gitignore | 115 ++++++ README.md | 352 ++++++++++++++++++ docker-compose.yaml | 862 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 1419 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 docker-compose.yaml diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fe13807 --- /dev/null +++ b/.env.example @@ -0,0 +1,90 @@ +# Docker Compose Environment Variables Template +# Copy this file to .env and fill in your values +# NEVER commit .env to version control! + +# ============================================ +# SYSTEM CONFIGURATION +# ============================================ +PUID=1000 +PGID=1000 +TZ=Pacific/Auckland + +# ============================================ +# SERVICE URLs (for reverse proxy/public access) +# ============================================ +JELLYFIN_URL=https://jellyfin.yourdomain.com +PAPERLESS_URL=https://paperless.yourdomain.com +WYGIWYH_URL=https://wygiwyh.yourdomain.com +HOMEPAGE_ALLOWED_HOSTS=homepage.yourdomain.com + +# ============================================ +# NGINX PROXY MANAGER +# ============================================ +NPM_PASSWORD=change_this_secure_password + +# ============================================ +# PAPERLESS-NGX +# ============================================ +PAPERLESS_DB_ROOT_PASSWORD=change_this_secure_password +PAPERLESS_DB_NAME=paperless +PAPERLESS_DB_USER=paperless +PAPERLESS_DB_PASSWORD=change_this_secure_password +# Generate with: openssl rand -base64 32 +PAPERLESS_SECRET_KEY=change_this_to_random_secret_key + +# ============================================ +# IMMICH +# ============================================ +IMMICH_POSTGRES_PASSWORD=change_this_secure_password + +# ============================================ +# LITELLM (AI Gateway) +# ============================================ +# Get from: https://console.groq.com/keys +GROQ_API_KEY=gsk_your_groq_api_key_here +# Generate with: openssl rand -base64 32 +LITELLM_MASTER_KEY=sk-your_master_key_here + +# ============================================ +# SLSKD (Soulseek) +# ============================================ +SLSKD_USERNAME=your_slskd_username +SLSKD_PASSWORD=change_this_secure_password + +# ============================================ +# SPEEDTEST TRACKER +# ============================================ +SPEEDTEST_DB_NAME=speedtest +SPEEDTEST_DB_USER=speedtest +SPEEDTEST_DB_PASSWORD=change_this_secure_password +# Generate with: php artisan key:generate --show (or use: openssl rand -base64 32) +SPEEDTEST_APP_KEY=base64:your_generated_laravel_key_here + +# ============================================ +# WYGIWYH (Finance Tracker) +# ============================================ +WYGIWYH_DB_DATABASE=wygiwyh +WYGIWYH_DB_USER=wygiwyh +WYGIWYH_DB_PASSWORD=change_this_secure_password +# Generate with: openssl rand -base64 50 +WYGIWYH_SECRET_KEY=change_this_to_random_secret_key +WYGIWYH_ALLOWED_HOSTS=localhost,127.0.0.1,wygiwyh.yourdomain.com + +# ============================================ +# REDBOT (Discord Bot) +# ============================================ +# Get from: https://discord.com/developers/applications +REDBOT_TOKEN=your_discord_bot_token_here + +# ============================================ +# NOTES ON GENERATING SECURE VALUES +# ============================================ +# Generate random passwords: +# openssl rand -base64 32 +# +# Generate Laravel app key: +# docker run --rm -v $(pwd):/app composer create-project laravel/laravel temp +# docker run --rm -v $(pwd)/temp:/app php:8.2-cli php artisan key:generate --show +# +# Generate Django secret key: +# python3 -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..99b09c5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +# Environment and Secrets +.env +.env.* +!.env.example + +# Docker Runtime Data +/npm/data/ +/npm/letsencrypt/ +/npm/mysql/ + +# Media & Arr Configs (contain API keys and tokens) +/docker/Arrs/ +/docker/qBittorrent/ +/docker/slskd/ +/docker/soulsync/ + +# Document Management Data +/docker/paperless/ +/docker/stirling/ + +# AI/LLM Data (may contain sensitive prompts/data) +/docker/litellm/ + +# Photo Management (contains personal photos) +/docker/immich/ + +# Personal Data +/docker/obsidian/ +/docker/syncthing/ + +# Development +/docker/gitea/ + +# Genealogy (personal family data) +/docker/gramps-jamie/ +/docker/gramps-helen/ + +# Utilities Data +/docker/speedtest-tracker/ +/docker/rustdesk/ +/docker/redbot/ + +# Finance (sensitive financial data) +/wygiwyh/ + +# Music Scrobbling Data +/docker/maloja/ +/docker/scrobble/ + +# Homepage (may contain custom configs with internal URLs) +/docker/Homepage/ + +# Docker Volumes (managed by Docker) +*.sock +/var/run/docker.sock + +# Logs +*.log +/docker/*/logs/ +logs/ + +# Database Files +*.db +*.sqlite +*.sqlite3 +*.db-shm +*.db-wal + +# Backup Files +*.backup +*.bak +*.old +*~ + +# OS Generated Files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +desktop.ini + +# Editor Files +.vscode/ +.idea/ +*.swp +*.swo +*~ +.project +.settings/ + +# Temporary Files +*.tmp +*.temp +tmp/ +temp/ + +# Certificates and Keys +*.pem +*.key +*.crt +*.cert +*.p12 +*.pfx + +# Docker Compose Override (may contain personal customizations) +docker-compose.override.yml + +# Keep only structure files +!README.md +!.gitignore +!.env.example +!docker-compose.yaml diff --git a/README.md b/README.md index e69de29..6b80c3b 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,352 @@ +# Docker Compose Infrastructure Documentation + +## Overview + +This infrastructure manages a comprehensive self-hosted environment including media management (Jellyfin, *arr stack), document management (Paperless-ngx), AI services (Open WebUI, LiteLLM), photo management (Immich), genealogy tools (GrampsWeb), and various utilities. + +## Architecture + +### Network Segmentation + +- **media_net**: Media services and *arr applications +- **db_net**: Database services (internal only) +- **web_net**: Web-accessible services +- **internal_net**: Internal service communication + +### Service Categories + +#### Management & Infrastructure +- **Glances**: System monitoring dashboard +- **Portainer**: Container management UI +- **Watchtower**: Automatic container updates +- **Homepage**: Service dashboard +- **Docker Proxy**: Secure Docker socket proxy +- **Nginx Proxy Manager (NPM)**: Reverse proxy with SSL + +#### Media & Arr Stack +- **Prowlarr**: Indexer manager +- **Radarr**: Movie management +- **Sonarr**: TV show management +- **Lidarr**: Music management +- **Bazarr**: Subtitle management +- **Jellyseerr**: Media request management +- **Jellyfin**: Media server with hardware transcoding +- **qBittorrent**: Download client +- **Flaresolverr**: Cloudflare bypass +- **Slskd**: Soulseek client +- **Soulsync**: Music sync automation + +#### Document & AI Suite +- **Paperless-ngx**: Document management system + - MariaDB database + - Redis broker + - Tika for text extraction + - Gotenberg for PDF rendering +- **Paperless-AI**: AI-powered document analysis +- **Stirling-PDF**: PDF manipulation tools +- **Open WebUI**: LLM interface +- **LiteLLM**: LLM proxy/gateway with PostgreSQL + +#### Photo & Data Management +- **Immich**: Photo management and backup + - PostgreSQL with vector extensions + - Redis cache + - Optimized storage: SSD for thumbnails/profiles, NAS for full resolution +- **Syncthing**: File synchronization (Obsidian vaults) + +#### Development +- **Gitea**: Self-hosted Git service with MySQL + +#### Genealogy +- **GrampsWeb**: Web interface for genealogy research + - Two instances: Jamie and Helen family trees + - Shared Redis broker + +#### Utilities +- **Speedtest Tracker**: Network performance monitoring with MariaDB +- **RustDesk**: Remote desktop (hbbs + hbbr servers) +- **RedBot**: Discord bot +- **iperf3**: Network performance testing +- **Newt**: Custom application + +#### Finance +- **WYGIWYH**: Financial tracking with PostgreSQL + +#### Music & Scrobbling +- **Maloja**: Music scrobbling server +- **Multi-Scrobbler**: Cross-platform scrobbler + +## Storage Structure + +### Local Docker Configs (`/docker/`) +``` +/docker/ +├── Homepage/ +├── Arrs/ (Prowlarr, Radarr, Sonarr, Lidarr, Bazarr, Jellyfin, Jellyseerr) +├── qBittorrent/ +├── slskd/ +├── soulsync/ +├── paperless/ +├── stirling/ +├── immich/ +├── obsidian/ +├── syncthing/ +├── gitea/ +├── gramps-jamie/ +├── gramps-helen/ +├── speedtest-tracker/ +├── rustdesk/ +├── redbot/ +├── maloja/ +├── scrobble/ +└── litellm/ +``` + +### NAS Storage (`/mnt/Nas-Storage/data/`) +``` +/mnt/Nas-Storage/data/ +├── media/ +│ ├── movies/ +│ ├── tv/ +│ ├── music/ +│ └── images/ +└── torrents/ + └── soulsync/ +``` + +## Prerequisites + +### Required Environment Variables (.env file) + +```bash +# System +PUID=1000 +PGID=1000 +TZ=Pacific/Auckland + +# URLs +JELLYFIN_URL=https://your-jellyfin-domain.com +PAPERLESS_URL=https://your-paperless-domain.com +WYGIWYH_URL=https://your-wygiwyh-domain.com +HOMEPAGE_ALLOWED_HOSTS=your-homepage-domain.com + +# Nginx Proxy Manager +NPM_PASSWORD=your_secure_password + +# Paperless +PAPERLESS_DB_ROOT_PASSWORD=your_secure_password +PAPERLESS_DB_NAME=paperless +PAPERLESS_DB_USER=paperless +PAPERLESS_DB_PASSWORD=your_secure_password +PAPERLESS_SECRET_KEY=your_secret_key + +# Immich +IMMICH_POSTGRES_PASSWORD=your_secure_password + +# LiteLLM +GROQ_API_KEY=your_groq_api_key +LITELLM_MASTER_KEY=your_master_key + +# Slskd +SLSKD_USERNAME=your_username +SLSKD_PASSWORD=your_secure_password + +# Speedtest Tracker +SPEEDTEST_DB_NAME=speedtest +SPEEDTEST_DB_USER=speedtest +SPEEDTEST_DB_PASSWORD=your_secure_password +SPEEDTEST_APP_KEY=base64:your_generated_key + +# WYGIWYH +WYGIWYH_DB_DATABASE=wygiwyh +WYGIWYH_DB_USER=wygiwyh +WYGIWYH_DB_PASSWORD=your_secure_password +WYGIWYH_SECRET_KEY=your_secret_key +WYGIWYH_ALLOWED_HOSTS=localhost,127.0.0.1,your-domain.com + +# RedBot +REDBOT_TOKEN=your_discord_bot_token +``` + +### System Requirements + +- Docker Engine 20.10+ +- Docker Compose V2 +- Sufficient storage for media and databases +- Intel GPU for Jellyfin hardware transcoding (optional) + +## Installation + +1. **Clone/Download this repository** + +2. **Create required directories** + ```bash + # See directory_structure.sh for automated setup + chmod +x directory_structure.sh + ./directory_structure.sh + ``` + +3. **Create .env file** + ```bash + cp .env.example .env + # Edit .env with your values + nano .env + ``` + +4. **Set correct permissions** + ```bash + sudo chown -R $PUID:$PGID /docker + sudo chown -R $PUID:$PGID /mnt/Nas-Storage/data + ``` + +5. **Start services** + ```bash + docker compose up -d + ``` + +## Service Access + +Default ports (configure reverse proxy for SSL/domains): + +- Homepage: 7575 +- Portainer: 9443 +- Jellyfin: 8096 +- Radarr: 7878 +- Sonarr: 8989 +- Prowlarr: 9696 +- Lidarr: 8686 +- Bazarr: 6767 +- Jellyseerr: 5055 +- qBittorrent: 7070 +- Paperless: 8100 +- Stirling-PDF: 8090 +- Open WebUI: 3000 +- Immich: 2283 +- Syncthing: 8384 +- Gitea: 8418 +- GrampsWeb Jamie: 5511 +- GrampsWeb Helen: 5512 +- Speedtest Tracker: 8180 +- NPM Admin: 81 +- Maloja: 42010 +- Multi-Scrobbler: 9078 +- WYGIWYH: 9008 + +## Maintenance + +### Backup Strategy + +**Critical Data to Backup:** +- `/docker/` - All service configurations +- Database volumes (see docker-compose.yaml) +- `.env` file (store securely, contains secrets) + +**Optional (can be regenerated):** +- Media files on NAS +- Immich thumbnails (can be regenerated) + +### Updates + +Watchtower automatically updates containers daily. To manually update: + +```bash +docker compose pull +docker compose up -d +``` + +### Logs + +View logs for any service: +```bash +docker compose logs -f [service_name] +``` + +### Resource Limits + +- Portainer: 512MB RAM limit +- Log rotation: 10MB max, 3 files + +## Hardware Acceleration + +Jellyfin is configured for Intel GPU transcoding: +- Device: `/dev/dri/renderD128` +- Group: `104` (render group) + +Verify GPU access: +```bash +ls -l /dev/dri/renderD128 +``` + +## Security Considerations + +1. **Secrets Management**: Store `.env` securely, never commit to version control +2. **Network Segmentation**: Database network is internal-only +3. **Container Security**: `no-new-privileges:true` on supported services +4. **Reverse Proxy**: Use NPM for SSL termination and authentication +5. **Updates**: Watchtower keeps containers updated + +## Troubleshooting + +### Service won't start +```bash +docker compose logs [service_name] +docker compose restart [service_name] +``` + +### Database connection issues +```bash +# Check database is healthy +docker compose ps +# Verify network connectivity +docker compose exec [service] ping [db_service] +``` + +### Permission errors +```bash +# Verify ownership +ls -la /docker/[service]/ +# Fix if needed +sudo chown -R $PUID:$PGID /docker/[service]/ +``` + +### Storage full +```bash +# Check Docker disk usage +docker system df +# Clean up unused resources +docker system prune -a +``` + +## Migration/Restoration + +1. Install Docker and Docker Compose on new system +2. Restore `/docker/` directory structure +3. Restore `.env` file +4. Restore database volumes (if applicable) +5. Mount NAS storage at `/mnt/Nas-Storage/data/` +6. Run `docker compose up -d` + +## Contributing Services + +To add new services: +1. Add service definition to appropriate section +2. Assign to correct network(s) +3. Add volume mounts under `/docker/[service]/` +4. Update this README with service description +5. Add required environment variables to `.env.example` + +## License + +This is a personal infrastructure setup. Adapt as needed for your use case. + +## Support + +For issues with specific services, consult their official documentation: +- Jellyfin: https://jellyfin.org/docs/ +- Paperless-ngx: https://docs.paperless-ngx.com/ +- Immich: https://immich.app/docs/ +- And so on... + +--- + +**Last Updated**: December 2025 diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..17ecdf0 --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,862 @@ +# 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 --- + 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/library/thumbs:/usr/src/app/upload/thumbs + - /docker/immich/library/profile:/usr/src/app/upload/profile + - /docker/immich/library/backups:/usr/src/app/upload/backups + # NAS (HDD) - High Resolution Library & Uploads + - /mnt/Nas-Storage/data/media/images/library:/usr/src/app/upload/library + - /mnt/Nas-Storage/data/media/images/upload:/usr/src/app/upload/upload + - /mnt/Nas-Storage/data/media/images/encoded-video:/usr/src/app/upload/encoded-video + 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} + - 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: