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
This commit is contained in:
238
docs/resource-limits-template.md
Normal file
238
docs/resource-limits-template.md
Normal file
@@ -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</content>
|
||||
<parameter name="filePath">/home/kelin/AI-Homelab/docs/resource-limits-template.md
|
||||
202
scripts/apply-resource-limits.sh
Executable file
202
scripts/apply-resource-limits.sh
Executable file
@@ -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."
|
||||
Reference in New Issue
Block a user