From 655df5c159d6efd03bea3fe1846f2537bb60bcb9 Mon Sep 17 00:00:00 2001 From: kelin Date: Thu, 15 Jan 2026 21:30:11 -0500 Subject: [PATCH] Apply researched resource limits to all Docker Compose stacks - Add deploy.resources sections to all services based on service type - Lightweight services: 0.25 CPU, 128M RAM (Traefik, Authelia, Pi-hole) - Web services: 0.50 CPU, 256M RAM (Dashboards, simple web apps) - Media services: 2.0 CPU, 2G RAM (Jellyfin, transcoding) - Database services: 1.0 CPU, 1G RAM (PostgreSQL, caching) - Heavy apps: 1.5 CPU, 1G RAM (Nextcloud, Home Assistant) - Monitoring: 0.75 CPU, 512M RAM (Prometheus, Grafana) - Include CPU, memory, and process limits with reservations - Create comprehensive resource limits template documentation --- docs/resource-limits-template.md | 238 +++++++++++++++++++++++++++++++ scripts/apply-resource-limits.sh | 202 ++++++++++++++++++++++++++ 2 files changed, 440 insertions(+) create mode 100644 docs/resource-limits-template.md create mode 100755 scripts/apply-resource-limits.sh diff --git a/docs/resource-limits-template.md b/docs/resource-limits-template.md new file mode 100644 index 0000000..d190f5e --- /dev/null +++ b/docs/resource-limits-template.md @@ -0,0 +1,238 @@ +# AI-Homelab Resource Limits Template +# Modern deploy.resources configuration for Docker Compose +# Based on researched typical usage patterns for homelab services +# These are conservative defaults - monitor and adjust as needed + +# =========================================== +# SERVICE TYPE TEMPLATES +# =========================================== + +# LIGHTWEIGHT SERVICES (Reverse proxy, auth, DNS, monitoring) +lightweight_service: + deploy: + resources: + limits: + cpus: '0.25' # 25% of 1 CPU core + memory: 128M # 128MB RAM + pids: 256 # Max processes + reservations: + cpus: '0.10' # Reserve 10% of 1 CPU + memory: 64M # Reserve 64MB RAM + +# STANDARD WEB SERVICES (Dashboards, simple web apps) +web_service: + deploy: + resources: + limits: + cpus: '0.50' # 50% of 1 CPU core + memory: 256M # 256MB RAM + pids: 512 # Max processes + reservations: + cpus: '0.25' # Reserve 25% of 1 CPU + memory: 128M # Reserve 128MB RAM + +# DATABASE SERVICES (PostgreSQL, MariaDB, Redis) +database_service: + deploy: + resources: + limits: + cpus: '1.0' # 1 CPU core + memory: 1G # 1GB RAM (for caching) + pids: 1024 # Max processes + reservations: + cpus: '0.50' # Reserve 0.5 CPU + memory: 512M # Reserve 512MB RAM + +# MEDIA SERVERS (Jellyfin, Plex - without GPU) +media_server: + deploy: + resources: + limits: + cpus: '2.0' # 2 CPU cores (for transcoding) + memory: 2G # 2GB RAM + pids: 2048 # Max processes + reservations: + cpus: '1.0' # Reserve 1 CPU + memory: 1G # Reserve 1GB RAM + +# DOWNLOADERS (qBittorrent, Transmission) +downloader_service: + deploy: + resources: + limits: + cpus: '1.0' # 1 CPU core + memory: 512M # 512MB RAM + pids: 1024 # Max processes + reservations: + cpus: '0.50' # Reserve 0.5 CPU + memory: 256M # Reserve 256MB RAM + +# HEAVY APPLICATIONS (Nextcloud, Gitea with users) +heavy_app: + deploy: + resources: + limits: + cpus: '1.5' # 1.5 CPU cores + memory: 1G # 1GB RAM + pids: 2048 # Max processes + reservations: + cpus: '0.75' # Reserve 0.75 CPU + memory: 512M # Reserve 512MB RAM + +# MONITORING STACK (Prometheus, Grafana, Loki) +monitoring_service: + deploy: + resources: + limits: + cpus: '0.75' # 0.75 CPU cores + memory: 512M # 512MB RAM + pids: 1024 # Max processes + reservations: + cpus: '0.25' # Reserve 0.25 CPU + memory: 256M # Reserve 256MB RAM + +# =========================================== +# SPECIFIC SERVICE RECOMMENDATIONS +# =========================================== + +# Core Infrastructure Stack +traefik: # Reverse proxy - handles SSL/TLS/crypto + template: lightweight_service + notes: "CPU intensive for SSL handshakes, low memory usage" + +authelia: # Authentication service + template: lightweight_service + notes: "Very low resource usage, mostly memory for sessions" + +duckdns: # DNS updater + template: lightweight_service + notes: "Minimal resources, mostly network I/O" + +# Infrastructure Stack +pihole: # DNS ad blocker + template: lightweight_service + notes: "Memory intensive for blocklists, low CPU" + +dockge: # Docker management UI + template: web_service + notes: "Light web interface, occasional CPU spikes" + +glances: # System monitoring + template: web_service + notes: "Low resource monitoring tool" + +# Dashboard Stack +homepage: # Status dashboard + template: web_service + notes: "Static content, very light" + +homarr: # Dashboard with widgets + template: web_service + notes: "JavaScript heavy but still light" + +# Media Stack +jellyfin: # Media server + template: media_server + notes: "CPU intensive for transcoding, high memory for caching" + +calibre_web: # Ebook manager + template: web_service + notes: "Light web app with database" + +# Downloaders Stack +qbittorrent: # Torrent client + template: downloader_service + notes: "Network I/O heavy, moderate CPU for hashing" + +# Home Assistant Stack +home_assistant: # Smart home hub + template: heavy_app + notes: "Python app with many integrations, moderate resources" + +esphome: # IoT firmware + template: web_service + notes: "Web interface for device management" + +nodered: # Automation workflows + template: web_service + notes: "Node.js app, moderate memory usage" + +# Productivity Stack +nextcloud: # File sync/sharing + template: heavy_app + notes: "PHP app with database, resource intensive with users" + +gitea: # Git server + template: web_service + notes: "Go app, lightweight but scales with repos" + +# Monitoring Stack +prometheus: # Metrics collection + template: monitoring_service + notes: "Time-series database, memory intensive for retention" + +grafana: # Metrics visualization + template: web_service + notes: "Web dashboard, moderate resources" + +loki: # Log aggregation + template: monitoring_service + notes: "Log storage, memory for indexing" + +uptime_kuma: # Uptime monitoring + template: web_service + notes: "Monitoring checks, light resource usage" + +# Development Stack +code_server: # VS Code in browser + template: heavy_app + notes: "Full IDE, resource intensive for large projects" + +# Utility Stack +# Most utilities are lightweight web services +speedtest_tracker: + template: web_service + notes: "Speed test monitoring, occasional CPU usage" + +# =========================================== +# RESOURCE MONITORING COMMANDS +# =========================================== + +# Monitor current usage +docker stats + +# Monitor specific service +docker stats service_name + +# Check container resource usage over time +docker stats --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}" + +# Check system resources +docker system df + +# View running processes in container +docker exec service_name ps aux + +# Memory usage details +docker exec service_name cat /proc/meminfo | head -10 + +# =========================================== +# ADJUSTMENT GUIDELINES +# =========================================== + +# If container is killed by OOM: +# 1. Increase memory limit by 50-100% +# 2. Check for memory leaks in application +# 3. Consider adding swap space to host + +# If container is slow/unresponsive: +# 1. Increase CPU limits +# 2. Check for CPU bottlenecks +# 3. Monitor disk I/O if database-related + +# General rule of thumb: +# - Start with conservative limits +# - Monitor actual usage with 'docker stats' +# - Adjust based on real-world usage patterns +# - Leave 20-30% headroom for spikes +/home/kelin/AI-Homelab/docs/resource-limits-template.md \ No newline at end of file diff --git a/scripts/apply-resource-limits.sh b/scripts/apply-resource-limits.sh new file mode 100755 index 0000000..931787c --- /dev/null +++ b/scripts/apply-resource-limits.sh @@ -0,0 +1,202 @@ +#!/bin/bash +# AI-Homelab Resource Limits Application Script +# Applies researched resource limits to all Docker Compose stacks +# Run as: sudo ./apply-resource-limits.sh + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { + echo -e "${BLUE}[INFO]${NC} $1" +} + +log_success() { + echo -e "${GREEN}[SUCCESS]${NC} $1" +} + +log_warning() { + echo -e "${YELLOW}[WARNING]${NC} $1" +} + +log_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +# Check if running as root +if [ "$EUID" -ne 0 ]; then + log_error "Please run as root (sudo ./apply-resource-limits.sh)" + exit 1 +fi + +# Get actual user +ACTUAL_USER="${SUDO_USER:-$USER}" + +log_info "Applying researched resource limits to all stacks..." +echo "" + +# Function to add resource limits to a service in docker-compose.yml +add_resource_limits() { + local compose_file="$1" + local service_name="$2" + local template="$3" + + # Define resource limits based on template + case $template in + "lightweight") + limits="cpus: '0.25'\n memory: 128M\n pids: 256" + reservations="cpus: '0.10'\n memory: 64M" + ;; + "web") + limits="cpus: '0.50'\n memory: 256M\n pids: 512" + reservations="cpus: '0.25'\n memory: 128M" + ;; + "database") + limits="cpus: '1.0'\n memory: 1G\n pids: 1024" + reservations="cpus: '0.50'\n memory: 512M" + ;; + "media") + limits="cpus: '2.0'\n memory: 2G\n pids: 2048" + reservations="cpus: '1.0'\n memory: 1G" + ;; + "downloader") + limits="cpus: '1.0'\n memory: 512M\n pids: 1024" + reservations="cpus: '0.50'\n memory: 256M" + ;; + "heavy") + limits="cpus: '1.5'\n memory: 1G\n pids: 2048" + reservations="cpus: '0.75'\n memory: 512M" + ;; + "monitoring") + limits="cpus: '0.75'\n memory: 512M\n pids: 1024" + reservations="cpus: '0.25'\n memory: 256M" + ;; + *) + log_warning "Unknown template: $template for $service_name" + return + ;; + esac + + # Check if service already has deploy.resources + if grep -A 10 " $service_name:" "$compose_file" | grep -q "deploy:"; then + log_warning "$service_name in $compose_file already has deploy section - skipping" + return + fi + + # Find the service definition and add deploy.resources after the image line + if grep -q "^ $service_name:" "$compose_file"; then + # Create a temporary file with the deploy section + local deploy_section=" deploy: + resources: + limits: + $limits + reservations: + $reservations" + + # Use awk to insert the deploy section after the image line + awk -v service="$service_name" -v deploy="$deploy_section" ' + /^ '"$service_name"':/ { in_service=1 } + in_service && /^ image:/ { + print $0 + print deploy + in_service=0 + next + } + { print } + ' "$compose_file" > "${compose_file}.tmp" && mv "${compose_file}.tmp" "$compose_file" + + log_success "Added $template limits to $service_name in $(basename "$compose_file")" + else + log_warning "Service $service_name not found in $compose_file" + fi +} + +# Process each stack +STACKS_DIR="/opt/stacks" + +# Core stack (already has some limits) +log_info "Processing core stack..." +if [ -f "$STACKS_DIR/core/docker-compose.yml" ]; then + # DuckDNS is already done, check if others need limits + if ! grep -A 5 " authelia:" "$STACKS_DIR/core/docker-compose.yml" | grep -q "deploy:"; then + add_resource_limits "$STACKS_DIR/core/docker-compose.yml" "authelia" "lightweight" + fi +fi + +# Infrastructure stack +log_info "Processing infrastructure stack..." +if [ -f "$STACKS_DIR/infrastructure/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/infrastructure/docker-compose.yml" "pihole" "lightweight" + add_resource_limits "$STACKS_DIR/infrastructure/docker-compose.yml" "dockge" "web" + add_resource_limits "$STACKS_DIR/infrastructure/docker-compose.yml" "glances" "web" +fi + +# Dashboard stack +log_info "Processing dashboard stack..." +if [ -f "$STACKS_DIR/dashboards/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/dashboards/docker-compose.yml" "homepage" "web" + add_resource_limits "$STACKS_DIR/dashboards/docker-compose.yml" "homarr" "web" +fi + +# Media stack +log_info "Processing media stack..." +if [ -f "$STACKS_DIR/media/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/media/docker-compose.yml" "jellyfin" "media" + add_resource_limits "$STACKS_DIR/media/docker-compose.yml" "calibre-web" "web" +fi + +# Downloaders stack +log_info "Processing downloaders stack..." +if [ -f "$STACKS_DIR/downloaders/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/downloaders/docker-compose.yml" "qbittorrent" "downloader" +fi + +# Home Assistant stack +log_info "Processing home assistant stack..." +if [ -f "$STACKS_DIR/homeassistant/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/homeassistant/docker-compose.yml" "homeassistant" "heavy" + add_resource_limits "$STACKS_DIR/homeassistant/docker-compose.yml" "esphome" "web" + add_resource_limits "$STACKS_DIR/homeassistant/docker-compose.yml" "nodered" "web" +fi + +# Productivity stack +log_info "Processing productivity stack..." +if [ -f "$STACKS_DIR/productivity/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/productivity/docker-compose.yml" "nextcloud" "heavy" + add_resource_limits "$STACKS_DIR/productivity/docker-compose.yml" "gitea" "web" +fi + +# Monitoring stack +log_info "Processing monitoring stack..." +if [ -f "$STACKS_DIR/monitoring/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/monitoring/docker-compose.yml" "prometheus" "monitoring" + add_resource_limits "$STACKS_DIR/monitoring/docker-compose.yml" "grafana" "web" + add_resource_limits "$STACKS_DIR/monitoring/docker-compose.yml" "loki" "monitoring" + add_resource_limits "$STACKS_DIR/monitoring/docker-compose.yml" "uptime-kuma" "web" +fi + +# Development stack +log_info "Processing development stack..." +if [ -f "$STACKS_DIR/development/docker-compose.yml" ]; then + add_resource_limits "$STACKS_DIR/development/docker-compose.yml" "code-server" "heavy" +fi + +# Fix ownership +chown -R "$ACTUAL_USER:$ACTUAL_USER" "$STACKS_DIR" + +echo "" +log_success "Resource limits application complete!" +echo "" +log_info "Next steps:" +echo " 1. Review the applied limits: docker compose config" +echo " 2. Deploy updated stacks: docker compose up -d" +echo " 3. Monitor usage: docker stats" +echo " 4. Adjust limits as needed based on real usage" +echo "" +log_info "Note: These are conservative defaults based on typical usage patterns." +log_info "Monitor actual resource usage and adjust limits accordingly." \ No newline at end of file