Files
portainer-core/CONTAINERS.md
T
jpmschweitzerandClaude Opus 4.5 f3d29089d6 config(npm): reorganize tatlock and webui domain mappings
Fix naming conflict where tatlock.schweitz.net incorrectly pointed to
Open WebUI. Now correctly maps:
- tatlock.schweitz.net → Tatlock API (port 8000) with Authentik SSO
- webui.schweitz.net → Open WebUI (port 82) with Authentik SSO
- Corresponding .schweitz.internal domains for programmatic access

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 00:03:55 +01:00

45 KiB

Container Reference - tower-of-joy Infrastructure

Last Updated: 2025-12-30 Total Services: 27 containers across 21 stacks
System: Intel i7-6700, RTX 2080 Ti (11GB VRAM), 16GB RAM, Zorin OS 16.3


🚀 Quick Reference - All Services

Service Port(s) URL External Access GPU Redis DB Status
Portainer 8080, 8443 http://192.168.86.149:8080 LAN No - Running
Nginx Proxy Manager 81 (admin), 80, 443 http://192.168.86.149:81 Internet No - Running
Authentik 9000, 9445 (outpost) https://auth.schweitz.net Internet (SSO) No 0 ⚠️ Partial
PostgreSQL Shared 5432 N/A (internal) No No - Running
Redis Shared 6379 N/A (internal) No No - Running
Ollama 11434 http://192.168.86.149:11434 LAN Yes (RTX 2080 Ti) - Running
Headscale 8085, 9090 http://192.168.86.149:8085 LAN No - Running
Netdata 19999 http://192.168.86.149:19999 LAN No - Running
Organizr 9999 https://home.schweitz.net Internet (SSO) No - Running
Watchtower None N/A (background) No No - Running
Open WebUI 82 https://webui.schweitz.net Internet (SSO) No - Running
Core API 8083 http://192.168.86.149:8083 LAN No - Running (external)
Scheduler 8090 http://192.168.86.149:8090 LAN No 3 Running (external)
SearXNG 8087 http://192.168.86.149:8087 LAN No 5 Running
Jellyfin 8096, 8920, 7359, 1900 https://media.schweitz.net Internet Yes (RTX 2080 Ti) - Running
Nextcloud 8082 https://cloud.schweitz.net Internet No 7 ⚠️ Stopped
Samba 139, 445 \\192.168.86.149 LAN No - Running
Gitea 3002, 2222 (SSH) https://git.schweitz.net Internet No - Running
Qdrant 6333, 6334 http://192.168.86.149:6333 LAN No - Running
Wiki.js 3000 http://192.168.86.149:3000 LAN No 2 Running
Tatlock 8000 https://tatlock.schweitz.net Internet (SSO) No 1, 6 Running (external)
Library Desk 8089 http://192.168.86.149:8089 LAN No 4 Running (external)
AMP (Game Server) Varies http://192.168.86.149:8081 LAN No - Running
Home Assistant 8123 https://housekeeping.schweitz.net Internet No - Running
Paperless-ngx 8091 https://documents.schweitz.net Internet No 8 Running
ClamAV 3310 N/A (host service) No No - Running
AdGuard Home 53, 3053 http://dns.schweitz.internal LAN (DNS) No - Running

External Domains (SSL via Let's Encrypt)

  • home.schweitz.net → Organizr (Protected by Authentik SSO)
  • media.schweitz.net → Jellyfin
  • cloud.schweitz.net → Nextcloud
  • git.schweitz.net → Gitea
  • auth.schweitz.net → Authentik SSO
  • api.schweitz.net → Core API
  • code.schweitz.net → Code-Server (host service)
  • amp.schweitz.net → AMP Game Server
  • housekeeping.schweitz.net → Home Assistant
  • documents.schweitz.net → Paperless-ngx
  • library.schweitz.net → Wiki.js
  • tatlock.schweitz.net → Tatlock API (Protected by Authentik SSO)
  • webui.schweitz.net → Open WebUI (Protected by Authentik SSO)

Internal Domains (HTTP, No Auth)

Internal domains provide LAN-accessible URLs without SSL or Authentik, ideal for programmatic access, scripts, and healthchecks. Each mirrors its external counterpart.

Internal Domain Backend Port
home.schweitz.internal localhost 9999
media.schweitz.internal localhost 8096
cloud.schweitz.internal localhost 8082
api.schweitz.internal localhost 8083
code.schweitz.internal localhost 8084
amp.schweitz.internal localhost 8080
housekeeping.schweitz.internal localhost 8123
documents.schweitz.internal 192.168.86.149 8091
git.schweitz.internal localhost 3002
library.schweitz.internal localhost 8088
tatlock.schweitz.internal localhost 8000
webui.schweitz.internal localhost 82
dns.schweitz.internal localhost 3053

DNS Resolution: Via /etc/hosts on tower-of-joy (192.168.86.149)

Usage: curl http://api.schweitz.internal/health or docker pull git.schweitz.internal/jpmschweitzer/core-api:latest

External Repositories

Services marked "(external)" have source code in separate Gitea repositories. Container images are built via Gitea Actions on release and auto-updated by Watchtower.

Service Repository Image
Scheduler scheduler git.schweitz.net/jpmschweitzer/scheduler:latest
Core API core-api git.schweitz.net/jpmschweitzer/core-api:latest
Library Desk library-desk git.schweitz.net/jpmschweitzer/library-desk:latest

Development workflow: Clone repo → make changes → create Gitea release → Watchtower auto-updates container.


📊 Infrastructure Management via Core API

The Core API (port 8083) provides REST endpoints for managing the entire infrastructure:

Available Endpoints

  • GET /infrastructure/health - Check Portainer + NPM connectivity
  • GET /infrastructure/services - List all deployed stacks (17 stacks)
  • GET /infrastructure/services/{name} - Get service details
  • POST /infrastructure/services - Deploy new stack from Docker Compose
  • PUT /infrastructure/services/{name} - Update existing stack
  • DELETE /infrastructure/services/{name} - Remove service and stack
  • GET /infrastructure/ports - List all allocated ports
  • GET /infrastructure/domains - List configured domains (11 domains)
  • GET /infrastructure/proxy/{id} - Get proxy host details
  • POST /infrastructure/proxy - Create new proxy host with SSL
  • PUT /infrastructure/proxy/{id} - Update proxy configuration API Documentation: http://192.168.86.149:8083/docs

Infrastructure Layer

Portainer

Portainer provides the web-based container management interface for the entire stack, offering visual control over Docker containers, stacks, images, volumes, and networks. It serves as the primary management tool for deploying and monitoring all other services, with GPU device management enabled for allocation to ML and transcoding workloads. The interface replaces the need for manual Docker CLI operations and provides real-time container logs, stats, and control.

Property Value
Image portainer/portainer-ce:latest
Container Name portainer
Access URL http://192.168.86.149:8001
External Access LAN only (behind firewall)
Port Mapping 8001:9000 (HTTP), 8443:9443 (HTTPS)
Network Mode Host
Restart Policy always
Volume Mounts portainer_data:/data, /var/run/docker.sock
Resource Limits None
GPU Required No
Dependencies None (base service)

PostgreSQL Shared

PostgreSQL Shared is a centralized PostgreSQL 17 database server providing isolated database instances for multiple applications across the infrastructure, including Authentik (SSO), Gitea (Git hosting), and future services requiring relational database storage. It implements a shared infrastructure pattern where each application gets its own database and user credentials while sharing the same PostgreSQL instance for resource efficiency. The service stores all database data on the HDD with automated backups scheduled to the backups directory, providing persistent storage with volume-based data retention across container updates.

Property Value
Image postgres:17
Container Name postgres-shared
Access URL N/A (internal database server)
External Access No (docker-dataplane network only)
Port Mapping 5432:5432 (PostgreSQL)
Network Mode Bridge (docker-dataplane)
Restart Policy unless-stopped
Volume Mounts /home/jpmschweitzer/docker-data/postgres-shared/data:/var/lib/postgresql/data, /home/jpmschweitzer/docker-data/postgres-shared/backups:/backups
Environment POSTGRES_PASSWORD=<secure-password>, POSTGRES_DB=postgres, TZ=Europe/Amsterdam, PGDATA=/var/lib/postgresql/data/pgdata
Resource Limits None
GPU Required No
Dependencies docker-dataplane network
Databases authentik (Authentik SSO), gitea (Git hosting), organizr (Organizr dashboard), paperless (Document management), system_settings (Central Tatlock settings), postgres (default/admin)
Database Users authentik_user, gitea_user, organizr_user, paperless_user, settings (system_settings RW), postgres (superuser)
Health Check pg_isready -U postgres (30s interval)
Backup Strategy /backups volume for pg_dump exports

Initialization: Databases and users for authentik and gitea are created by the postgres-init.sh script.

Adding Organizr Database:

  1. Generate a secure password for the organizr_user.
  2. In Portainer, navigate to the postgres-shared service.
  3. Go to the "Env" tab and add a new environment variable:
    • Name: ORGANIZR_DB_PASSWORD
    • Value: Your generated password
  4. Redeploy the postgres-shared service. This will trigger the postgres-init.sh script to create the organizr database and user.

Redis Shared

Redis Shared is a centralized Redis 7 key-value store providing cache, session storage, and message queue capabilities for multiple applications, with logical database isolation (DB 0-15) allowing each service to maintain separate keyspaces within the same Redis instance. It implements a shared infrastructure pattern where applications like Authentik use DB 0 for sessions/cache while future services can use DB 1-15, eliminating the need for separate Redis containers per application. The service stores data on the HDD for persistence across restarts, with AOF (Append-Only File) enabled for durability and optional RDB snapshots for backup points.

Property Value
Image redis:7-alpine
Container Name redis-shared
Access URL N/A (internal cache server)
External Access No (docker-dataplane network only)
Port Mapping 6379:6379 (Redis)
Network Mode Bridge (docker-dataplane)
Restart Policy unless-stopped
Volume Mounts /home/jpmschweitzer/docker-data/redis-shared/data:/data
Command redis-server --appendonly yes --dir /data
Resource Limits None
GPU Required No
Dependencies docker-dataplane network
Database Allocation DB 0: Authentik, DB 1: Tatlock (memory), DB 2: Wiki.js, DB 3: Scheduler, DB 4: Library Desk, DB 5: SearXNG, DB 6: Tatlock (benchmarks), DB 7: Nextcloud, DB 8: Paperless, DB 9-15: Available
Persistence AOF (Append-Only File) enabled for durability
Health Check redis-cli ping returns PONG (30s interval)
Connection String redis://redis-shared:6379/0 (DB 0), redis://redis-shared:6379/1 (DB 1), etc.

Nginx Proxy Manager

Nginx Proxy Manager serves as the unified reverse proxy and SSL certificate manager, providing a web-based interface for routing HTTP/HTTPS traffic to backend services with automatic Let's Encrypt certificate provisioning. It consolidates access to all web services through a single entry point with path-based or subdomain routing, eliminating the need to remember individual service ports. The service handles SSL termination, proxy host configuration, and access list management through an intuitive dashboard.

Property Value
Image jc21/nginx-proxy-manager:latest
Container Name nginx-proxy-manager
Access URL http://192.168.86.149:81
External Access LAN + Internet (ports 80/443 forwarded)
Port Mapping 81:81 (Admin), 80:80 (HTTP), 443:443 (HTTPS)
Network Mode Host
Restart Policy unless-stopped
Volume Mounts ~/docker-data/nginx-proxy-manager/data:/data, ~/docker-data/nginx-proxy-manager/letsencrypt:/etc/letsencrypt
Resource Limits None
GPU Required No
Dependencies None (reverse proxy for other services)

AdGuard Home

AdGuard Home is a network-wide DNS-based ad and tracker blocker that functions as a local DNS server, filtering requests at the network level before they reach any device. It provides encrypted DNS (DoH/DoT) to upstream resolvers, a web-based dashboard for query statistics and configuration, and customizable blocklists for ads, trackers, and malware domains. All devices on the network pointing to this DNS server receive ad blocking without requiring per-device software installation.

Property Value
Image adguard/adguardhome:latest
Container Name adguard
Access URL (LAN) http://192.168.86.149:3053
Internal Domain http://dns.schweitz.internal
External Access LAN only (DNS server for local network)
Port Mapping 192.168.86.149:53:53 (DNS), 3053:3000 (Web UI)
Network Mode Bridge (docker-dataplane)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/adguard/work:/opt/adguardhome/work, ~/docker-data/adguard/conf:/opt/adguardhome/conf
Resource Limits None
GPU Required No
Dependencies None
Upstream DNS Quad9 DoH (https://dns.quad9.net/dns-query)
Health Check nslookup localhost 127.0.0.1 (30s interval)

Ollama

Ollama is a GPU-accelerated large language model server that provides a REST API for running local LLM inference with models up to 13B parameters, leveraging the RTX 2080 Ti's 11GB VRAM for fast on-device AI capabilities. It manages model downloads, quantization, and serving through a simple API compatible with OpenAI's format, supporting use cases like code generation, chat applications, and text processing without cloud dependencies. The service stores models on the SSD for quick loading times and maintains persistent model storage across container restarts.

Property Value
Image ollama/ollama:latest
Container Name ollama
Access URL http://192.168.86.149:11434
External Access LAN only (API endpoint)
Port Mapping 11434:11434 (API)
Network Mode Bridge (custom network)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/ollama/models:/root/.ollama
Resource Limits Memory: 8GB, GPU: 11GB VRAM
GPU Required Yes (NVIDIA RTX 2080 Ti)
GPU Configuration NVIDIA_VISIBLE_DEVICES=all, NVIDIA_DRIVER_CAPABILITIES=compute,utility
Dependencies NVIDIA Container Toolkit
Typical Models llama3.2:3b (~2GB), mistral:7b (~4GB), codellama:7b (~4GB)

Code-Server

Code-Server provides a browser-based Visual Studio Code IDE running directly on the host system, offering a persistent development environment with full access to host-level configurations, filesystems, and systemd services without the limitations of containerization. It replaces traditional SSH access by providing a rich IDE experience that survives network disconnections through session persistence, with integrated terminal access, file explorer, git integration, and extension support. The service runs as a systemd service on the host, listening on localhost and exposed externally through Nginx Proxy Manager with SSL encryption and multi-layer authentication for secure remote development access.

Property Value
Deployment Type Host-based systemd service (NOT containerized)
Binary Location /usr/bin/code-server
Service Name code-server.service
Access URL (LAN) http://127.0.0.1:8084 (localhost only)
Access URL (Public) https://code.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Binding 127.0.0.1:8084 (not exposed to network)
User/Group jpmschweitzer:jpmschweitzer
Restart Policy always (systemd)
Config Location ~/.config/code-server/config.yaml
Data Storage (SSD) ~/docker-data/code-server/user-data/ (settings, workspace), ~/docker-data/code-server/extensions/ (extensions)
Resource Limits None (native host process)
GPU Required No
Dependencies NPM (reverse proxy), systemd
Authentication Triple-layer: NPM access list, code-server password, SSL certificate
WebSocket Support Required (enabled via NPM proxy)
Typical Memory ~200-500MB (depends on workspace size)
Setup Guide docs/code-server-setup.md

Networking Layer

Headscale

Headscale is a self-hosted control server for Tailscale's mesh VPN protocol, creating a private software-defined network across all connected devices with end-to-end encryption and zero-configuration NAT traversal. It enables secure remote access to all homelab services from anywhere without exposing ports to the internet, using a custom 10.99.0.0/16 IP range for the mesh network. The service manages device registration, authentication, and mesh routing while maintaining full data sovereignty compared to the hosted Tailscale control plane.

Property Value
Image headscale/headscale:latest
Container Name headscale
Access URL http://192.168.86.149:8085
External Access LAN only (control server)
Port Mapping 8085:8080 (Web/API), 9090:9090 (Metrics)
Network Mode Bridge (custom network)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/headscale/config:/etc/headscale, ~/docker-data/headscale/data:/var/lib/headscale
Resource Limits None
GPU Required No
Dependencies None
Network Range 10.99.0.0/16 (mesh IPs)
Pre-Auth Keys 24-hour expiration

Monitoring Layer

Netdata

Netdata provides comprehensive real-time system performance monitoring with per-second metric collection for CPU, RAM, disk I/O, network traffic, and Docker container resource usage, displaying everything through interactive web dashboards with zero configuration required. It collects thousands of metrics automatically with minimal overhead, offering drill-down capabilities from system-wide views to per-container and per-process analysis. The service maintains short-term metric history in RAM and can stream data to long-term storage backends for historical analysis.

Property Value
Image netdata/netdata:latest
Container Name netdata
Access URL http://192.168.86.149:19999
External Access LAN only (metrics dashboard)
Port Mapping 19999:19999 (Web UI)
Network Mode Host (for full system visibility)
Restart Policy unless-stopped
Volume Mounts /proc:/host/proc:ro, /sys:/host/sys:ro, /var/run/docker.sock:/var/run/docker.sock:ro
Capabilities SYS_PTRACE, apparmor:unconfined
Resource Limits None (monitoring overhead ~1-3% CPU)
GPU Required No
Dependencies Docker socket (read-only)
Metric Retention ~1 hour (RAM-based)

Organizr

Organizr serves as a comprehensive unified dashboard that consolidates all homelab services into a single tabbed interface with integrated homepage widgets showing real-time statistics from Jellyfin streams, Netdata metrics, and download client activity. It provides customizable authentication per-tab with support for SSO integration, user management with group-based access control, and a mobile-responsive interface for managing the entire infrastructure from anywhere. The service acts as a central hub replacing the need for multiple bookmarks or remembering service ports, offering both quick-access tabs and homepage cards with live data feeds from connected services.

Property Value
Image organizr/organizr:latest
Container Name organizr
Access URL (LAN) http://192.168.86.149:9999
Access URL (Public) https://home.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Mapping 9999:80 (HTTP), 443:443 (HTTPS)
Network Mode Bridge
Restart Policy unless-stopped
Volume Mounts ~/docker-data/organizr:/config
Environment DB_TYPE=pgsql, DB_HOST=postgres-shared, DB_PORT=5432, DB_NAME=organizr, DB_USER=organizr_user, DB_PASS=${ORGANIZR_DB_PASSWORD}
Resource Limits None
GPU Required No
Dependencies PostgreSQL Shared
Database PostgreSQL on postgres-shared (database organizr)
Database Size ~5-10MB (typical)
Integrated Services Jellyfin, Netdata
Authentication Internal (supports SSO, Plex OAuth, LDAP)

Configuration:

  1. In Portainer, navigate to the organizr stack.
  2. Go to the "Env" tab and ensure the following environment variables are set:
    • DB_TYPE=pgsql
    • DB_HOST=postgres-shared
    • DB_PORT=5432
    • DB_NAME=organizr
    • DB_USER=organizr_user
    • DB_PASS: This should be a secret. Create a secret in Portainer named ORGANIZR_DB_PASSWORD and set its value to the password you generated for the organizr_user.
  3. Redeploy the organizr stack.

Optimization Layer

Watchtower

Watchtower automatically monitors all running containers for updated images and performs rolling updates on a configurable schedule, ensuring the infrastructure stays current with security patches and feature releases without manual intervention. It checks Docker Hub and configured registries daily at 4 AM, pulls new images when available, gracefully stops containers, deploys updated versions, and cleans up old images to prevent disk bloat. The service logs all update activities and can send notifications through various channels when updates occur.

Property Value
Image containrrr/watchtower:latest
Container Name watchtower
Access URL N/A (background service)
External Access N/A
Port Mapping None (no exposed ports)
Network Mode Bridge
Restart Policy unless-stopped
Volume Mounts /var/run/docker.sock:/var/run/docker.sock
Environment WATCHTOWER_CLEANUP=true, WATCHTOWER_SCHEDULE=0 0 4 * * *
Resource Limits None
GPU Required No
Dependencies Docker socket (read-write for updates)
Schedule Daily at 4:00 AM
Cleanup Automatic (removes old images)

Application Layer

Open WebUI

Open WebUI is a feature-rich, self-hosted web interface for interacting with large language models via Ollama, providing a ChatGPT-like experience with support for multiple models, conversation history, RAG (Retrieval-Augmented Generation), web search integration, and user authentication. It offers a modern chat interface with streaming responses, markdown rendering, code syntax highlighting, and conversation management, enabling seamless switching between different LLM models and maintaining persistent chat histories in a local database. The service integrates directly with the local Ollama instance for GPU-accelerated inference without cloud dependencies, supporting features like web search via DuckDuckGo, document upload for context, and multi-user access with authentication. It is documented at: https://docs.openwebui.com/

Property Value
Image ghcr.io/open-webui/open-webui:main
Container Name open-webui
Access URL (LAN) http://192.168.86.149:82
Access URL (Public) https://webui.schweitz.net
Internal Domain http://webui.schweitz.internal
External Access Yes (via NPM reverse proxy with SSL + Authentik SSO)
Port Mapping 82:8080 (HTTP)
Network Mode Bridge (custom network: ai-network)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/open-webui:/app/backend/data
Environment OLLAMA_BASE_URL=http://192.168.86.149:11434, DEFAULT_MODELS=llama3.2:3b, ENABLE_RAG_WEB_SEARCH=true, ENABLE_OLLAMA_API=true, WEBUI_AUTH=true, RAG_WEB_SEARCH_ENGINE=duckduckgo, AUDIO_STT_ENGINE=openai, AUDIO_TTS_ENGINE=openai
Resource Limits None
GPU Required No (uses Ollama for GPU inference)
Dependencies Ollama (ML inference backend)
Database SQLite (persistent in volume)
Authentication Built-in user management
Integrated Services Ollama, DuckDuckGo (web search)

Core API

Core API provides OpenAI-compatible HTTP functions for Open WebUI, extending LLM capabilities with AI orchestration, web scraping, and content processing services in a hot-reload development environment. The service implements Phase 1 of the AI Orchestrator plan, providing /v1/chat/completions and /v1/models endpoints with full OpenAI API compatibility, model aliasing (gpt-3.5-turbo → gemma:7b), and streaming support via Server-Sent Events. It uses Trafilatura for intelligent content extraction with BeautifulSoup fallback, offering configurable content length limits and optional link extraction optimized for feeding webpage content to language models. The service runs on Python 3.12 with mounted source code for instant updates, maintaining a persistent venv in docker-data for fast container restarts and development agility.

Property Value
Image git.schweitz.net/jpmschweitzer/core-api:latest
Container Name core-api
Access URL http://192.168.86.149:8083
API Documentation http://192.168.86.149:8083/docs (Swagger UI)
External Access LAN only (internal API)
Port Mapping 8083:8083 (HTTP)
Network Mode Bridge (custom network: docker-dataplane)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/core-api/logs:/app/logs (logs), /var/run/docker.sock:/var/run/docker.sock:ro (Docker access)
Environment APP_NAME=Core API, APP_VERSION=1.0.0-phase1, DEBUG=true, PORT=8083, LOG_LEVEL=INFO, PYTHONPATH=/app, Model aliases: ALIAS_GPT35=gemma:7b, ALIAS_GPT4=mistral:7b
Source Repository https://git.schweitz.net/jpmschweitzer/core-api
Resource Limits None
GPU Required No (proxies requests to Ollama which uses GPU)
Dependencies Ollama (model inference), Open WebUI (consumes this API), ai-dataplane network
Framework FastAPI 0.115.0, Uvicorn 0.32.0, Pydantic 2.10.4, httpx 0.28.1
Key Features OpenAI-compatible API (/v1/chat/completions, /v1/models), Model aliasing (OpenAI → local models), Streaming & non-streaming responses, Web scraping (Trafilatura, BeautifulSoup), Hot-reload development, OpenAPI spec, Infrastructure management (Portainer)
AI Orchestrator Phase 1 Complete - OpenAI API wrapper with model routing. Phase 2+ will add memory systems, multi-agent workflows, and tool integration.
Health Check GET /health (30s interval) - checks API status and Ollama connectivity

Jellyfin

Jellyfin is a GPU-accelerated media server that organizes, streams, and transcodes video, music, and photo libraries with hardware encoding via NVIDIA NVENC, enabling smooth 4K playback across multiple simultaneous clients without taxing the CPU. It provides a Netflix-like interface accessible through web browsers, mobile apps, and smart TV clients, with automatic metadata fetching, subtitle support, and user management for family sharing. The service stores configuration and cache on the SSD for responsive browsing while accessing massive media libraries on the 3.7TB HDD, supporting direct play when possible and GPU-accelerated transcoding when format conversion is needed.

Property Value
Image jellyfin/jellyfin:latest
Container Name jellyfin
Access URL http://192.168.86.149:8096, https://media.schweitz.net
External Access LAN + Internet (via NPM reverse proxy)
Port Mapping 8096:8096 (HTTP), 8920:8920 (HTTPS), 7359:7359/udp (Discovery), 1900:1900/udp (DLNA)
Network Mode Bridge
Restart Policy unless-stopped
Volume Mounts ~/docker-data/jellyfin/config:/config, ~/docker-data/jellyfin/cache:/cache, /mnt/media/jellyfin:/media:ro
Environment NVIDIA_VISIBLE_DEVICES=all, NVIDIA_DRIVER_CAPABILITIES=all
User 1000:1000 (UID:GID)
Resource Limits Memory: 4GB, GPU: 11GB VRAM (shared)
GPU Required Yes (NVIDIA RTX 2080 Ti)
GPU Configuration NVENC hardware encoding, NVDEC hardware decoding
Dependencies NVIDIA Container Toolkit, NPM (for external access)
Media Storage /mnt/media/jellyfin (HDD)
Transcoding Hardware-accelerated (H.264/H.265)

Nextcloud

Nextcloud is a self-hosted cloud storage and collaboration platform providing file sync, sharing, calendar, contacts, and collaborative document editing with a web interface and mobile apps. Uses shared PostgreSQL for metadata and shared Redis for caching. Application config on SSD, user data on HDD. External access via NPM at https://cloud.schweitz.net.

Property Value
Image nextcloud:stable
Container Name nextcloud
Access URL (LAN) http://192.168.86.149:8082
Access URL (Public) https://cloud.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Mapping 8082:80 (HTTP)
Network Mode docker-dataplane
Restart Policy unless-stopped
Volume Mounts ~/docker-data/nextcloud/config:/var/www/html (SSD), /mnt/media/nextcloud/data:/var/www/html/data (HDD)
Resource Limits None
GPU Required No
Dependencies PostgreSQL Shared, Redis Shared, NPM (reverse proxy)
Database PostgreSQL (shared)
Storage Split Config/apps on SSD, user data on HDD
Features File sync, calendar, contacts, document editing, photo gallery, mobile apps

Home Assistant

Home Assistant is an open-source smart home automation platform that integrates with thousands of IoT devices, sensors, and services to provide centralized control, automation rules, and monitoring through a web dashboard and mobile apps. It supports local processing without cloud dependencies, enabling privacy-focused home automation with features like device tracking, energy monitoring, voice assistants, and custom automations triggered by time, location, or device states. The service stores all configuration, automations, and historical data on the SSD for responsive dashboard loading, with USB device support for Z-Wave and Zigbee coordinators.

Property Value
Image ghcr.io/home-assistant/home-assistant:stable
Container Name home-assistant
Access URL (LAN) http://192.168.86.149:8123
Access URL (Public) https://housekeeping.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Mapping 8123:8123 (HTTP)
Network Mode Bridge (docker-dataplane)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/home-assistant/config:/config (SSD - configs, database, automations)
Environment TZ=Europe/Amsterdam
Privileged Mode Yes (for USB device access: Z-Wave, Zigbee dongles)
Resource Limits Memory: 1GB
GPU Required No
Dependencies NPM (reverse proxy), USB devices (optional)
Health Check curl -fSs http://localhost:8123/api/ (30s interval)
Features Device integrations, automations, dashboards, energy monitoring, voice assistants, mobile apps

Paperless-ngx

Paperless-ngx is a document management system that transforms physical documents into a searchable online archive with automatic OCR text recognition, tagging, and full-text search. It provides a web interface for uploading, organizing, and retrieving documents with support for correspondents, document types, and custom fields. The service integrates with Library Desk for document indexing and uses ClamAV for virus scanning of uploaded files. Configs and database are stored on SSD (backed up), while documents are stored on HDD for capacity. Uses shared PostgreSQL for metadata and shared Redis (DB 8) for task queuing.

Property Value
Image ghcr.io/paperless-ngx/paperless-ngx:latest
Container Name paperless
Access URL (LAN) http://192.168.86.149:8091
Access URL (Public) https://documents.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Mapping 8091:8000 (HTTP)
Network Mode Bridge (docker-dataplane)
Restart Policy unless-stopped
Volume Mounts SSD (backed up): ~/docker-data/paperless/data:/usr/src/paperless/data; HDD: /mnt/media/paperless/media:/usr/src/paperless/media, /mnt/media/paperless/consume:/usr/src/paperless/consume, /mnt/media/paperless/export:/usr/src/paperless/export
Environment PAPERLESS_DBHOST=postgres-shared, PAPERLESS_REDIS=redis://redis-shared:6379/8, PAPERLESS_URL=https://documents.schweitz.net, TZ=Europe/Amsterdam
Resource Limits Memory: 4GB limit, 512MB reservation
GPU Required No
Dependencies PostgreSQL Shared, Redis Shared (DB 8), NPM (reverse proxy), ClamAV (host)
Database PostgreSQL paperless on postgres-shared
Health Check curl -f http://localhost:8000 (30s interval)
Features OCR (eng+nld), document tagging, full-text search, correspondents, custom fields, webhooks
Integration Library Desk (webhook on document added), ClamAV (virus scanning)

ClamAV

ClamAV is an open-source antivirus engine running on the host OS, providing virus scanning capabilities for the entire server and accessible via TCP socket for container-based services like Paperless-ngx. It includes automatic virus definition updates via freshclam and can perform both on-demand and real-time scanning. Running on the host provides better security isolation than containerized scanning and allows scanning of the host filesystem directly.

Property Value
Deployment Type Host-based systemd service (NOT containerized)
Binary Location /usr/bin/clamd, /usr/bin/clamdscan
Service Names clamav-daemon.service, clamav-freshclam.service
Access URL N/A (TCP socket only)
External Access No (internal service)
Port Binding 0.0.0.0:3310 (TCP socket for remote scanning)
Restart Policy always (systemd)
Config Location /etc/clamav/clamd.conf, /etc/clamav/freshclam.conf
Database Location /var/lib/clamav/ (virus definitions)
Resource Usage ~2-4GB RAM (virus definitions loaded in memory)
GPU Required No
Dependencies None (host service)
Update Schedule Automatic via freshclam (checks multiple times daily)
Connection String clamav://192.168.86.149:3310
Features On-demand scanning, TCP socket API, automatic updates, host filesystem access

Usage from containers:

# Test connectivity
nc -zv 192.168.86.149 3310

# Scan a file (from host)
clamdscan /path/to/file

Samba

Samba provides SMB/CIFS network file sharing for seamless access to homelab storage from Windows, macOS, Linux, and mobile devices, exposing curated shares for media libraries, downloads, and backups with configurable read-only and read-write permissions. It runs as a single container on the samba_default network, serving three shares: Media (read-write access to Jellyfin content), Downloads (read-write for torrent clients), and Backups (read-only for safe data recovery). The service uses password authentication for the user jpmschweitzer and stores its minimal configuration on the SSD while directly mounting HDD paths for zero-copy file access with native performance.

Property Value
Image dperson/samba
Container Name samba
Access URL \\192.168.86.149 or \\tower-of-joy
External Access LAN only (ports firewalled)
Port Mapping 139:139 (NetBIOS), 445:445 (SMB)
Network Mode Bridge (custom network: samba_default)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/samba:/share/config (SSD config), /mnt/media/jellyfin:/share/media (Media share), /mnt/media/downloads:/share/downloads (Downloads share), /mnt/media/backups:/share/backups:ro (Backups read-only)
Environment TZ=Europe/Amsterdam, USERID=1000, GROUPID=1000
Command Share configs: Media (browseable, guest access), Downloads (no guest), Backups (read-only, browseable)
Resource Limits None
GPU Required No
Dependencies UFW firewall rules (ports 139, 445), Host Samba service disabled
Shares 3 total: Media (R/W), Downloads (R/W), Backups (R/O)
Authentication Username/password (jpmschweitzer)
Client Compatibility Windows, macOS, Linux, iOS, Android

Gitea

Gitea is a lightweight, self-hosted Git service providing repository hosting, issue tracking, pull requests, code review, and CI/CD integration through a clean web interface accessible via both HTTPS and SSH. It runs as a multi-container stack with a PostgreSQL database for metadata storage, offering GitHub-like functionality including organizations, teams, wikis, webhooks, and automated Actions workflows while maintaining complete data sovereignty and minimal resource overhead. The service stores all Git repositories and configuration on the SSD for fast access, integrates behind Nginx Proxy Manager with SSL at https://git.schweitz.net for web access, and exposes SSH on port 2222 for standard Git operations without conflicting with the host's SSH service.

Property Value
Image gitea/gitea:latest
Container Name gitea
Access URL (LAN) http://192.168.86.149:3002
Access URL (Public) https://git.schweitz.net
External Access Yes (via NPM reverse proxy with SSL)
Port Mapping 3002:3000 (HTTP), 2222:22 (SSH)
Network Mode Bridge (custom network: gitea_gitea-network)
Restart Policy unless-stopped
Volume Mounts ~/docker-data/gitea/data:/data (SSD - repos, config)
Environment USER_UID=1000, USER_GID=1000, GITEA__database__* (PostgreSQL connection), TZ=Europe/Amsterdam
Resource Limits None
GPU Required No
Dependencies PostgreSQL 14 (gitea-db), NPM (reverse proxy)
Database PostgreSQL on SSD (~50MB)
SSH Access Port 2222 - git clone ssh://git@git.schweitz.net:2222/user/repo.git
Features Git hosting, Organizations/teams, Issues/PRs, Code review, Wikis, Webhooks, Gitea Actions (CI/CD), GitHub/GitLab migration
SSH Config Tip Add to ~/.ssh/config: Host git.schweitz.net / Port 2222 / User git for seamless cloning

Quick Reference Tables

Service Access Matrix

Service LAN URL Internet Access Primary Function
Portainer http://192.168.86.149:8001 No Container management
PostgreSQL Shared postgres-shared:5432 No (internal) Shared database server
Redis Shared redis-shared:6379 No (internal) Shared cache/session store
NPM http://192.168.86.149:81 Yes (admin) Reverse proxy admin
Code-Server https://code.schweitz.net Yes Browser-based IDE
Ollama http://192.168.86.149:11434 No ML model API
Headscale http://192.168.86.149:8085 No VPN control server
Netdata http://192.168.86.149:19999 No System metrics
Organizr https://home.schweitz.net Yes Unified dashboard
Open WebUI https://webui.schweitz.net Yes (SSO) LLM chat interface
Tatlock https://tatlock.schweitz.net Yes (SSO) AI orchestration API
Core API http://192.168.86.149:8083 No API functions & infrastructure mgmt
Jellyfin https://media.schweitz.net Yes Media streaming
Nextcloud https://cloud.schweitz.net Yes Cloud storage & sync
Gitea https://git.schweitz.net Yes Git repository hosting
Samba \\192.168.86.149 No Network file shares
Home Assistant https://housekeeping.schweitz.net Yes Smart home automation
Watchtower N/A (background) N/A Auto-updates

GPU-Enabled Services

Service GPU Usage VRAM Requirements Purpose
Ollama Compute, Utility 2-10GB (model dependent) LLM inference
Jellyfin Video Encode/Decode ~1-2GB (during transcode) Media transcoding

Total VRAM Available: 11GB (RTX 2080 Ti)


Storage Distribution

Service Config Location (SSD) Data Location (HDD) Typical Size
Portainer Docker volume: portainer_data N/A ~100MB
PostgreSQL Shared N/A ~/docker-data/postgres-shared/data/ Data: 100MB-5GB (depends on databases), Backups: variable
Redis Shared N/A ~/docker-data/redis-shared/data/ ~10-100MB (AOF + RDB snapshots)
NPM ~/docker-data/nginx-proxy-manager/ N/A ~50MB
Code-Server ~/.config/code-server/, ~/docker-data/code-server/ N/A Config: ~5MB, Extensions: ~50-200MB, User data: ~50MB
Ollama ~/docker-data/ollama/models/ Alt: /mnt/media/ollama/ 2-15GB per model
Headscale ~/docker-data/headscale/ N/A ~10MB
Netdata RAM-based (ephemeral) N/A ~200MB RAM
Organizr ~/docker-data/organizr/ N/A ~50MB
Open WebUI ~/docker-data/open-webui/ N/A ~100MB
Core API ~/docker-data/core-api/ N/A Logs: ~10MB
Jellyfin ~/docker-data/jellyfin/ /mnt/media/jellyfin/ Config: ~500MB, Media: ~2TB
Nextcloud ~/docker-data/nextcloud/ /mnt/media/nextcloud/data/ Config: ~200MB, DB: ~100MB, User data: variable
Gitea ~/docker-data/gitea/ N/A Data: ~100MB, DB: ~50MB, Repos: variable
Samba ~/docker-data/samba/ Mounts: /mnt/media/ (shares) Config: ~5MB
Home Assistant ~/docker-data/home-assistant/config/ N/A Config: ~100MB, DB: ~50MB

SSD Usage (docker-data): ~6-11GB (configs, caches, databases) HDD Usage (/mnt/media): ~2.1TB / 3.6TB (58% used)


Network Architecture

As of 2025-11-15, all services have been consolidated to the docker-dataplane bridge network for simplified service discovery and inter-container communication. This consolidation replaced 12+ legacy networks with a single unified network, enabling all services to communicate using container names as DNS hostnames.

Network Name Containers Purpose
docker-dataplane Ollama, Open WebUI, Core API, Qdrant, PostgreSQL Shared, Redis Shared, Headscale, Nextcloud, Gitea, Samba, Watchtower, Organizr, Netdata, Home Assistant Unified service mesh for all containerized applications
host Portainer, NPM Direct host port access for infrastructure management

Benefits of Consolidation:

  • Service Discovery: All services reachable via http://container-name:port (e.g., http://postgres-shared:5432)
  • Shared Infrastructure: postgres-shared and redis-shared accessible to all applications
  • Network Cleanup: Removed 7 obsolete networks (stacks_default, ai-dataplane, various stack-specific networks)

Container Name Resolution Examples:

# From any container on docker-dataplane
curl http://ollama:11434           # Ollama LLM API
psql -h postgres-shared -U postgres  # PostgreSQL connection
redis-cli -h redis-shared          # Redis connection

Restart Policies

Policy Containers Behavior
always Portainer Restart on failure, on Docker daemon restart
unless-stopped All others Restart on failure, but not after manual stop

Automated Tasks

Service Task Frequency Time
Watchtower Container updates Daily 4:00 AM
Scheduler Config backups Daily 3:05 AM
Scheduler Doc sync Monthly 11th/12th

Last Updated: 2025-12-30