Compare commits
14 Commits
2ba24f2a94
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 3cba0998a7 | |||
| a7c38cd58d | |||
| 889a666c81 | |||
| 32f2e1b666 | |||
| 248c3c3dc7 | |||
| b106cfc177 | |||
| 1f823fa9d1 | |||
| cce25bfa4d | |||
| f618ce2c69 | |||
| a7213441da | |||
| 148a0827e3 | |||
| a99a1e65ce | |||
| 814c4e71a3 | |||
| a0e4e63d01 |
@@ -1,8 +1,5 @@
|
|||||||
# HOPS .gitignore
|
# HOPS .gitignore
|
||||||
|
|
||||||
# Claude Code development documentation
|
|
||||||
CLAUDE.md
|
|
||||||
|
|
||||||
# Log files
|
# Log files
|
||||||
*.log
|
*.log
|
||||||
|
|
||||||
|
|||||||
+23
-674
@@ -1,715 +1,64 @@
|
|||||||
# Advanced Configuration & Troubleshooting
|
# Advanced Configuration
|
||||||
|
|
||||||
This guide covers advanced HOPS configuration, troubleshooting, security features, and system administration.
|
This guide covers advanced HOPS configuration and system administration.
|
||||||
|
|
||||||
## 🔧 Advanced Configuration
|
> **Note**: This document is being rebuilt alongside the 1.0.0 codebase.
|
||||||
|
> Content will be expanded as features are stabilized.
|
||||||
|
|
||||||
### Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
HOPS uses both encrypted secret management and traditional environment files for configuration.
|
HOPS writes a `.env` file to `~/hops/` during installation. Key variables:
|
||||||
|
|
||||||
#### Encrypted Secret Management (v3.1.0+)
|
|
||||||
```bash
|
```bash
|
||||||
# Initialize secret management
|
PUID=1000 # User ID (run: id -u)
|
||||||
./lib/secrets.sh init
|
PGID=1000 # Group ID (run: id -g)
|
||||||
|
|
||||||
# Create encrypted environment
|
|
||||||
./lib/secrets.sh create
|
|
||||||
|
|
||||||
# Update values
|
|
||||||
./lib/secrets.sh update DOMAIN example.com
|
|
||||||
./lib/secrets.sh update ACME_EMAIL admin@example.com
|
|
||||||
|
|
||||||
# Retrieve values
|
|
||||||
./lib/secrets.sh get PUID
|
|
||||||
./lib/secrets.sh get DEFAULT_ADMIN_PASSWORD
|
|
||||||
|
|
||||||
# List all keys
|
|
||||||
./lib/secrets.sh list
|
|
||||||
|
|
||||||
# Backup encrypted secrets
|
|
||||||
./lib/secrets.sh backup /backup/location/
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Traditional Environment File (~/hops/.env)
|
|
||||||
```bash
|
|
||||||
# User and Group Configuration
|
|
||||||
PUID=1000 # User ID
|
|
||||||
PGID=1000 # Group ID
|
|
||||||
TZ=America/New_York # Timezone
|
TZ=America/New_York # Timezone
|
||||||
|
|
||||||
# Directory Configuration
|
DATA_ROOT=/mnt/media # Media storage
|
||||||
DATA_ROOT=/mnt/media # Media storage (Linux)
|
CONFIG_ROOT=/opt/appdata # App configurations
|
||||||
CONFIG_ROOT=/opt/appdata # App configurations (Linux)
|
|
||||||
|
|
||||||
# macOS paths (automatically set)
|
DEFAULT_ADMIN_PASSWORD=... # Auto-generated
|
||||||
DATA_ROOT=/Users/username/hops/media
|
DEFAULT_DB_PASSWORD=... # Auto-generated
|
||||||
CONFIG_ROOT=/Users/username/hops/config
|
|
||||||
|
|
||||||
# Security (auto-generated and encrypted)
|
|
||||||
DEFAULT_ADMIN_PASSWORD=... # Generated secure password
|
|
||||||
DEFAULT_DB_PASSWORD=... # Database password
|
|
||||||
JELLYFIN_PASSWORD=... # Service-specific passwords
|
|
||||||
|
|
||||||
# Optional: Custom Domain Configuration
|
|
||||||
DOMAIN=yourdomain.com
|
|
||||||
ACME_EMAIL=admin@yourdomain.com
|
|
||||||
|
|
||||||
# Optional: Service Overrides
|
|
||||||
JELLYFIN_PORT=8096
|
|
||||||
SONARR_PORT=8989
|
|
||||||
RADARR_PORT=7878
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Service-Specific Configuration
|
## Docker Compose Commands
|
||||||
|
|
||||||
#### Reverse Proxy Setup
|
|
||||||
|
|
||||||
**Traefik Configuration:**
|
|
||||||
```bash
|
|
||||||
# Traefik automatically configured with labels
|
|
||||||
# Custom configuration in ~/hops/config/traefik/
|
|
||||||
|
|
||||||
# Example dynamic configuration
|
|
||||||
mkdir -p ~/hops/config/traefik/dynamic
|
|
||||||
cat > ~/hops/config/traefik/dynamic/middleware.yml << 'EOF'
|
|
||||||
http:
|
|
||||||
middlewares:
|
|
||||||
default-headers:
|
|
||||||
headers:
|
|
||||||
frameDeny: true
|
|
||||||
sslRedirect: true
|
|
||||||
browserXssFilter: true
|
|
||||||
contentTypeNosniff: true
|
|
||||||
forceSTSHeader: true
|
|
||||||
stsIncludeSubdomains: true
|
|
||||||
stsPreload: true
|
|
||||||
stsSeconds: 31536000
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
**Nginx Proxy Manager:**
|
|
||||||
- Access admin interface on port 81
|
|
||||||
- Default credentials: `admin@example.com` / `changeme`
|
|
||||||
- Configure SSL certificates through web interface
|
|
||||||
|
|
||||||
**Caddy Configuration:**
|
|
||||||
```bash
|
|
||||||
# Create Caddy configuration directory
|
|
||||||
mkdir -p ~/hops/config/caddy
|
|
||||||
|
|
||||||
# Create custom Caddyfile
|
|
||||||
cat > ~/hops/config/caddy/Caddyfile << 'EOF'
|
|
||||||
# Global options
|
|
||||||
{
|
|
||||||
email your-email@domain.com
|
|
||||||
# Optional: Use custom CA
|
|
||||||
# acme_ca https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main domain
|
|
||||||
yourdomain.com {
|
|
||||||
reverse_proxy jellyfin:8096
|
|
||||||
|
|
||||||
# Custom headers
|
|
||||||
header {
|
|
||||||
# Security headers
|
|
||||||
Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
|
|
||||||
X-Content-Type-Options "nosniff"
|
|
||||||
X-Frame-Options "DENY"
|
|
||||||
Referrer-Policy "strict-origin-when-cross-origin"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Subdomain routing
|
|
||||||
sonarr.yourdomain.com {
|
|
||||||
reverse_proxy sonarr:8989
|
|
||||||
}
|
|
||||||
|
|
||||||
radarr.yourdomain.com {
|
|
||||||
reverse_proxy radarr:7878
|
|
||||||
}
|
|
||||||
|
|
||||||
# Internal services (authentication required)
|
|
||||||
portainer.yourdomain.com {
|
|
||||||
reverse_proxy portainer:9000
|
|
||||||
|
|
||||||
# Optional: IP allowlist
|
|
||||||
@internal {
|
|
||||||
remote_ip 192.168.1.0/24 10.0.0.0/8
|
|
||||||
}
|
|
||||||
handle @internal {
|
|
||||||
reverse_proxy portainer:9000
|
|
||||||
}
|
|
||||||
handle {
|
|
||||||
respond "Access denied" 403
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Authelia Integration
|
|
||||||
```bash
|
|
||||||
# Authelia configuration
|
|
||||||
mkdir -p ~/hops/config/authelia
|
|
||||||
|
|
||||||
# Example configuration (simplified)
|
|
||||||
cat > ~/hops/config/authelia/configuration.yml << 'EOF'
|
|
||||||
theme: dark
|
|
||||||
default_redirection_url: https://yourdomain.com
|
|
||||||
|
|
||||||
server:
|
|
||||||
host: 0.0.0.0
|
|
||||||
port: 9091
|
|
||||||
|
|
||||||
log:
|
|
||||||
level: warn
|
|
||||||
|
|
||||||
authentication_backend:
|
|
||||||
file:
|
|
||||||
path: /config/users_database.yml
|
|
||||||
password:
|
|
||||||
algorithm: argon2id
|
|
||||||
|
|
||||||
access_control:
|
|
||||||
default_policy: deny
|
|
||||||
rules:
|
|
||||||
- domain: jellyfin.yourdomain.com
|
|
||||||
policy: bypass
|
|
||||||
- domain: "*.yourdomain.com"
|
|
||||||
policy: one_factor
|
|
||||||
|
|
||||||
session:
|
|
||||||
name: authelia_session
|
|
||||||
domain: yourdomain.com
|
|
||||||
|
|
||||||
regulation:
|
|
||||||
max_retries: 3
|
|
||||||
ban_time: 10m
|
|
||||||
|
|
||||||
storage:
|
|
||||||
local:
|
|
||||||
path: /config/db.sqlite3
|
|
||||||
|
|
||||||
notifier:
|
|
||||||
filesystem:
|
|
||||||
filename: /config/notification.txt
|
|
||||||
EOF
|
|
||||||
```
|
|
||||||
|
|
||||||
### Service Management Commands
|
|
||||||
|
|
||||||
#### User Operations Script (No Sudo Required)
|
|
||||||
```bash
|
|
||||||
# Service status and control
|
|
||||||
./user-operations status # View all service status
|
|
||||||
./user-operations status jellyfin # View specific service
|
|
||||||
./user-operations logs jellyfin # View service logs
|
|
||||||
./user-operations logs jellyfin -f # Follow logs
|
|
||||||
|
|
||||||
# Deployment operations
|
|
||||||
./user-operations deploy # Deploy all services
|
|
||||||
./user-operations stop # Stop all services
|
|
||||||
./user-operations restart # Restart all services
|
|
||||||
./user-operations restart jellyfin # Restart specific service
|
|
||||||
|
|
||||||
# Update operations
|
|
||||||
./user-operations update # Update all containers
|
|
||||||
./user-operations update jellyfin # Update specific container
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Direct Docker Compose Commands
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/hops
|
cd ~/hops
|
||||||
|
|
||||||
# Service management
|
|
||||||
docker compose ps # List services
|
docker compose ps # List services
|
||||||
docker compose up -d # Start all services
|
docker compose up -d # Start all services
|
||||||
docker compose down # Stop all services
|
docker compose down # Stop all services
|
||||||
docker compose restart # Restart all services
|
docker compose restart [service] # Restart a service
|
||||||
docker compose restart jellyfin # Restart specific service
|
docker compose logs -f [service] # Follow logs
|
||||||
|
docker compose logs --tail=100 [service] # Last 100 lines
|
||||||
# Logs and monitoring
|
|
||||||
docker compose logs # View all logs
|
|
||||||
docker compose logs -f jellyfin # Follow specific service logs
|
|
||||||
docker compose logs --tail=100 sonarr # Last 100 lines
|
|
||||||
|
|
||||||
# Updates and maintenance
|
|
||||||
docker compose pull # Pull new images
|
docker compose pull # Pull new images
|
||||||
docker compose up -d --force-recreate # Recreate containers
|
docker compose up -d --force-recreate # Recreate containers
|
||||||
docker compose down && docker compose up -d # Full restart
|
|
||||||
|
|
||||||
# Resource monitoring
|
|
||||||
docker stats # Real-time resource usage
|
docker stats # Real-time resource usage
|
||||||
docker system df # Disk usage
|
docker system df # Disk usage
|
||||||
docker system prune # Clean unused data
|
docker system prune # Clean unused data
|
||||||
```
|
```
|
||||||
|
|
||||||
### New Modular Architecture
|
## Firewall
|
||||||
|
|
||||||
HOPS v3.1.0+ introduces a modular library system:
|
HOPS configures UFW automatically on Linux. Manual management:
|
||||||
|
|
||||||
```
|
|
||||||
hops/
|
|
||||||
├── lib/ # Shared libraries
|
|
||||||
│ ├── common.sh # Logging, UI, utilities
|
|
||||||
│ ├── system.sh # OS detection, requirements
|
|
||||||
│ ├── docker.sh # Docker operations
|
|
||||||
│ ├── security.sh # Security functions
|
|
||||||
│ ├── validation.sh # Input validation
|
|
||||||
│ ├── secrets.sh # Secret management
|
|
||||||
│ └── privileges.sh # Privilege separation
|
|
||||||
├── setup # Main installation wrapper
|
|
||||||
├── privileged-setup # Root-only operations
|
|
||||||
├── user-operations # User operations
|
|
||||||
├── services-improved # Enhanced service definitions
|
|
||||||
└── hops # Main script
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Using Library Functions
|
|
||||||
```bash
|
```bash
|
||||||
# Source libraries in custom scripts
|
|
||||||
source lib/common.sh
|
|
||||||
source lib/system.sh
|
|
||||||
|
|
||||||
# Use logging functions
|
|
||||||
log "INFO" "Starting custom operation"
|
|
||||||
warning "This is a warning message"
|
|
||||||
error_exit "Fatal error occurred"
|
|
||||||
|
|
||||||
# Use system functions
|
|
||||||
detect_os
|
|
||||||
check_system_requirements 2 10 # 2GB RAM, 10GB disk
|
|
||||||
|
|
||||||
# Use validation functions
|
|
||||||
validate_port "8080"
|
|
||||||
validate_domain "example.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔒 Security Features & Hardening
|
|
||||||
|
|
||||||
### Automatic Security Hardening
|
|
||||||
|
|
||||||
HOPS automatically implements several security measures:
|
|
||||||
|
|
||||||
#### Firewall Configuration (Linux)
|
|
||||||
```bash
|
|
||||||
# UFW rules automatically created
|
|
||||||
sudo ufw status
|
sudo ufw status
|
||||||
|
|
||||||
# Manual firewall management
|
|
||||||
sudo ufw allow 8096/tcp comment "Jellyfin"
|
sudo ufw allow 8096/tcp comment "Jellyfin"
|
||||||
sudo ufw allow 9000/tcp comment "Portainer"
|
sudo ufw delete allow 8096/tcp
|
||||||
sudo ufw delete allow 8096/tcp # Remove rule
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### File Permissions
|
## File Permissions
|
||||||
```bash
|
|
||||||
# Automatic permission hardening
|
|
||||||
# Secrets: 600 (owner read/write only)
|
|
||||||
# Configs: 644 (owner write, group/other read)
|
|
||||||
# Scripts: 755 (executable)
|
|
||||||
|
|
||||||
# Manual permission fixes
|
```bash
|
||||||
chmod 600 ~/hops/.env
|
chmod 600 ~/hops/.env
|
||||||
chmod 644 ~/hops/docker-compose.yml
|
chmod 644 ~/hops/docker-compose.yml
|
||||||
chmod 755 ~/hops/user-operations
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Network Isolation
|
|
||||||
```bash
|
|
||||||
# Docker network isolation
|
|
||||||
docker network ls | grep homelab
|
|
||||||
docker network inspect homelab
|
|
||||||
|
|
||||||
# View network configuration
|
|
||||||
docker compose config | grep network
|
|
||||||
```
|
|
||||||
|
|
||||||
### Security Auditing
|
|
||||||
```bash
|
|
||||||
# Run security audit
|
|
||||||
./lib/security.sh audit
|
|
||||||
|
|
||||||
# Check for security issues
|
|
||||||
./lib/security.sh check-passwords
|
|
||||||
./lib/security.sh check-permissions
|
|
||||||
./lib/security.sh check-firewall
|
|
||||||
|
|
||||||
# Security recommendations
|
|
||||||
./lib/security.sh recommendations
|
|
||||||
```
|
|
||||||
|
|
||||||
### SSL/TLS Configuration
|
|
||||||
|
|
||||||
#### Traefik SSL
|
|
||||||
Traefik automatically handles SSL certificates with Let's Encrypt:
|
|
||||||
```bash
|
|
||||||
# Check certificate status
|
|
||||||
docker compose logs traefik | grep -i certificate
|
|
||||||
|
|
||||||
# Manual certificate renewal (if needed)
|
|
||||||
docker compose restart traefik
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Custom SSL Certificates
|
|
||||||
```bash
|
|
||||||
# For custom certificates, place in:
|
|
||||||
# ~/hops/config/traefik/certs/
|
|
||||||
# - yourdomain.com.crt
|
|
||||||
# - yourdomain.com.key
|
|
||||||
|
|
||||||
# Update Traefik configuration to use custom certs
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🆘 Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues & Solutions
|
|
||||||
|
|
||||||
#### Docker Issues
|
|
||||||
|
|
||||||
**Docker Not Starting:**
|
|
||||||
```bash
|
|
||||||
# Check Docker status
|
|
||||||
systemctl status docker
|
|
||||||
|
|
||||||
# Restart Docker
|
|
||||||
sudo systemctl restart docker
|
|
||||||
|
|
||||||
# Check Docker logs
|
|
||||||
journalctl -u docker --since "1 hour ago"
|
|
||||||
|
|
||||||
# Reinstall Docker (Linux)
|
|
||||||
curl -fsSL https://get.docker.com | sh
|
|
||||||
sudo usermod -aG docker $USER
|
|
||||||
newgrp docker
|
|
||||||
```
|
|
||||||
|
|
||||||
**Docker Compose Errors:**
|
|
||||||
```bash
|
|
||||||
# Validate compose file
|
|
||||||
docker compose config
|
|
||||||
|
|
||||||
# Check for syntax errors
|
|
||||||
docker compose config --quiet
|
|
||||||
|
|
||||||
# Force recreate containers
|
|
||||||
docker compose up -d --force-recreate
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Service-Specific Issues
|
|
||||||
|
|
||||||
**Service Won't Start:**
|
|
||||||
```bash
|
|
||||||
# Check service logs
|
|
||||||
docker compose logs [service-name]
|
|
||||||
|
|
||||||
# Check container status
|
|
||||||
docker compose ps
|
|
||||||
|
|
||||||
# Restart service
|
|
||||||
docker compose restart [service-name]
|
|
||||||
|
|
||||||
# Check port conflicts
|
|
||||||
sudo lsof -i :[port-number]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Permission Issues:**
|
|
||||||
```bash
|
|
||||||
# Fix ownership (Linux)
|
|
||||||
sudo chown -R $USER:$USER /opt/appdata
|
sudo chown -R $USER:$USER /opt/appdata
|
||||||
sudo chown -R $USER:$USER /mnt/media
|
sudo chown -R $USER:$USER /mnt/media
|
||||||
sudo chown -R $USER:$USER ~/hops
|
|
||||||
|
|
||||||
# Fix ownership (macOS)
|
|
||||||
sudo chown -R $USER:$USER ~/hops/config
|
|
||||||
sudo chown -R $USER:$USER ~/hops/media
|
|
||||||
|
|
||||||
# Check PUID/PGID values
|
|
||||||
id $USER # Should match PUID/PGID in .env
|
|
||||||
```
|
|
||||||
|
|
||||||
**Database Issues:**
|
|
||||||
```bash
|
|
||||||
# Reset service database (example: Sonarr)
|
|
||||||
docker compose down sonarr
|
|
||||||
rm -rf ~/hops/config/sonarr/sonarr.db* # macOS
|
|
||||||
rm -rf /opt/appdata/sonarr/sonarr.db* # Linux
|
|
||||||
docker compose up -d sonarr
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Network Issues
|
|
||||||
|
|
||||||
**Can't Access Services:**
|
|
||||||
```bash
|
|
||||||
# Check if services are running
|
|
||||||
docker compose ps
|
|
||||||
|
|
||||||
# Check port mapping
|
|
||||||
docker compose port jellyfin 8096
|
|
||||||
|
|
||||||
# Check firewall (Linux)
|
|
||||||
sudo ufw status
|
|
||||||
|
|
||||||
# Check local firewall (macOS)
|
|
||||||
# System Preferences → Security & Privacy → Firewall
|
|
||||||
|
|
||||||
# Find container IP
|
|
||||||
docker inspect jellyfin | grep IPAddress
|
|
||||||
```
|
|
||||||
|
|
||||||
**Reverse Proxy Issues:**
|
|
||||||
```bash
|
|
||||||
# Check proxy logs
|
|
||||||
docker compose logs traefik
|
|
||||||
docker compose logs nginx-proxy-manager
|
|
||||||
|
|
||||||
# Verify DNS resolution
|
|
||||||
nslookup yourdomain.com
|
|
||||||
|
|
||||||
# Check certificate status
|
|
||||||
openssl s_client -connect yourdomain.com:443 -servername yourdomain.com
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Platform-Specific Issues
|
|
||||||
|
|
||||||
**macOS Issues:**
|
|
||||||
```bash
|
|
||||||
# Docker Desktop not starting
|
|
||||||
open /Applications/Docker.app
|
|
||||||
|
|
||||||
# Homebrew issues
|
|
||||||
brew doctor
|
|
||||||
brew update && brew upgrade
|
|
||||||
|
|
||||||
# Fix keychain authentication
|
|
||||||
security unlock-keychain ~/Library/Keychains/login.keychain
|
|
||||||
```
|
|
||||||
|
|
||||||
**Windows/WSL2 Issues:**
|
|
||||||
```bash
|
|
||||||
# WSL2 not starting
|
|
||||||
wsl --shutdown
|
|
||||||
wsl -d Ubuntu-22.04
|
|
||||||
|
|
||||||
# Docker Desktop WSL2 integration
|
|
||||||
# Check Docker Desktop → Settings → Resources → WSL Integration
|
|
||||||
|
|
||||||
# File permission issues
|
|
||||||
# Ensure files are in WSL2 filesystem, not /mnt/c/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Log Analysis
|
|
||||||
|
|
||||||
#### Log Locations
|
|
||||||
- **Installation Logs**:
|
|
||||||
- Linux: `/var/log/hops/`
|
|
||||||
- macOS: `/usr/local/var/log/hops/`
|
|
||||||
- Windows: `~/hops/logs/`
|
|
||||||
- **Service Logs**: `docker compose logs [service-name]`
|
|
||||||
- **System Logs**: `journalctl -u docker`
|
|
||||||
|
|
||||||
#### Log Analysis Commands
|
|
||||||
```bash
|
|
||||||
# HOPS installation logs
|
|
||||||
sudo tail -f /var/log/hops/hops-main-*.log
|
|
||||||
|
|
||||||
# Service logs with filtering
|
|
||||||
docker compose logs jellyfin | grep -i error
|
|
||||||
docker compose logs --since="1h" sonarr
|
|
||||||
docker compose logs --tail=100 radarr
|
|
||||||
|
|
||||||
# System resource logs
|
|
||||||
dmesg | grep -i memory
|
|
||||||
journalctl --since="1 hour ago" | grep docker
|
|
||||||
```
|
|
||||||
|
|
||||||
### Recovery Procedures
|
|
||||||
|
|
||||||
#### Service Recovery
|
|
||||||
```bash
|
|
||||||
# Stop problematic service
|
|
||||||
docker compose stop [service-name]
|
|
||||||
|
|
||||||
# Remove container (keeps data)
|
|
||||||
docker compose rm [service-name]
|
|
||||||
|
|
||||||
# Recreate service
|
|
||||||
docker compose up -d [service-name]
|
|
||||||
|
|
||||||
# Full service reset (destroys data)
|
|
||||||
docker compose down [service-name]
|
|
||||||
rm -rf /path/to/service/config
|
|
||||||
docker compose up -d [service-name]
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Complete System Recovery
|
|
||||||
```bash
|
|
||||||
# Stop all services
|
|
||||||
docker compose down
|
|
||||||
|
|
||||||
# Backup current state
|
|
||||||
sudo tar -czf hops-backup-$(date +%Y%m%d).tar.gz ~/hops /opt/appdata
|
|
||||||
|
|
||||||
# Clean Docker system
|
|
||||||
docker system prune -a
|
|
||||||
docker volume prune
|
|
||||||
|
|
||||||
# Restart from backup
|
|
||||||
cd ~/hops
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
# Or reinstall HOPS
|
|
||||||
sudo ./uninstall
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Performance Tuning
|
|
||||||
|
|
||||||
### Resource Monitoring
|
|
||||||
```bash
|
|
||||||
# Container resource usage
|
|
||||||
docker stats
|
|
||||||
|
|
||||||
# System resource usage
|
|
||||||
htop
|
|
||||||
iotop
|
|
||||||
free -h
|
|
||||||
df -h
|
|
||||||
|
|
||||||
# Service-specific monitoring
|
|
||||||
docker compose exec portainer sh
|
|
||||||
# Access Portainer for detailed monitoring
|
|
||||||
```
|
|
||||||
|
|
||||||
### Optimization Strategies
|
|
||||||
|
|
||||||
#### For Low-Resource Systems (2-4GB RAM)
|
|
||||||
```bash
|
|
||||||
# Recommended minimal stack
|
|
||||||
# - Jellyfin (media server)
|
|
||||||
# - qBittorrent (download)
|
|
||||||
# - Sonarr (TV management)
|
|
||||||
# - Portainer (monitoring)
|
|
||||||
|
|
||||||
# Resource limits in docker-compose.yml
|
|
||||||
services:
|
|
||||||
jellyfin:
|
|
||||||
mem_limit: 1g
|
|
||||||
cpus: '2'
|
|
||||||
|
|
||||||
sonarr:
|
|
||||||
mem_limit: 512m
|
|
||||||
cpus: '1'
|
|
||||||
```
|
|
||||||
|
|
||||||
#### For High-Performance Systems (8GB+ RAM)
|
|
||||||
```bash
|
|
||||||
# Full stack deployment
|
|
||||||
# Enable GPU transcoding
|
|
||||||
# Use multiple download clients
|
|
||||||
# Add monitoring stack
|
|
||||||
|
|
||||||
# GPU support (Linux only)
|
|
||||||
# Intel GPU passthrough automatically configured
|
|
||||||
devices:
|
|
||||||
- /dev/dri:/dev/dri
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Storage Optimization
|
|
||||||
```bash
|
|
||||||
# Use SSD for application data
|
|
||||||
# HDD for media storage
|
|
||||||
# Separate Docker volumes
|
|
||||||
|
|
||||||
# Example optimized storage layout
|
|
||||||
/opt/appdata -> SSD
|
|
||||||
/mnt/media -> HDD array
|
|
||||||
~/hops -> SSD
|
|
||||||
```
|
|
||||||
|
|
||||||
### Update Management
|
|
||||||
```bash
|
|
||||||
# Automated updates with Watchtower
|
|
||||||
# Configure update schedule
|
|
||||||
WATCHTOWER_SCHEDULE=0 0 2 * * * # 2 AM daily
|
|
||||||
|
|
||||||
# Manual update process
|
|
||||||
docker compose pull
|
|
||||||
docker compose up -d
|
|
||||||
|
|
||||||
# Rollback to previous version
|
|
||||||
docker compose down
|
|
||||||
docker tag service:latest service:backup
|
|
||||||
docker pull service:previous
|
|
||||||
docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Advanced Features
|
|
||||||
|
|
||||||
### Custom Service Definitions
|
|
||||||
```bash
|
|
||||||
# Add custom services to docker-compose.yml
|
|
||||||
services:
|
|
||||||
custom-service:
|
|
||||||
image: custom/image:latest
|
|
||||||
container_name: custom-service
|
|
||||||
environment:
|
|
||||||
- PUID=${PUID}
|
|
||||||
- PGID=${PGID}
|
|
||||||
- TZ=${TZ}
|
|
||||||
volumes:
|
|
||||||
- ${CONFIG_ROOT}/custom:/config
|
|
||||||
- ${DATA_ROOT}:/data
|
|
||||||
ports:
|
|
||||||
- "8999:8999"
|
|
||||||
restart: unless-stopped
|
|
||||||
networks:
|
|
||||||
- homelab
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backup Automation
|
|
||||||
```bash
|
|
||||||
# Automated backup script
|
|
||||||
#!/bin/bash
|
|
||||||
DATE=$(date +%Y%m%d_%H%M%S)
|
|
||||||
BACKUP_DIR="/backup/hops"
|
|
||||||
|
|
||||||
# Create backup directory
|
|
||||||
mkdir -p "$BACKUP_DIR"
|
|
||||||
|
|
||||||
# Backup configurations
|
|
||||||
tar -czf "$BACKUP_DIR/config-$DATE.tar.gz" /opt/appdata
|
|
||||||
|
|
||||||
# Backup compose files
|
|
||||||
cp ~/hops/.env ~/hops/docker-compose.yml "$BACKUP_DIR/"
|
|
||||||
|
|
||||||
# Backup encrypted secrets
|
|
||||||
./lib/secrets.sh backup "$BACKUP_DIR/secrets-$DATE.enc"
|
|
||||||
|
|
||||||
# Clean old backups (keep 7 days)
|
|
||||||
find "$BACKUP_DIR" -name "*.tar.gz" -mtime +7 -delete
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development & Testing
|
|
||||||
```bash
|
|
||||||
# Development setup
|
|
||||||
git clone https://github.com/skiercm/hops.git
|
|
||||||
cd hops
|
|
||||||
|
|
||||||
# Syntax validation
|
|
||||||
bash -n lib/*.sh
|
|
||||||
bash -n *.sh
|
|
||||||
|
|
||||||
# Test service definitions
|
|
||||||
./services-improved list
|
|
||||||
./services-improved generate jellyfin
|
|
||||||
|
|
||||||
# Test installation in VM/container
|
|
||||||
# Use test environment before production deployment
|
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
For installation guides, see [INSTALLATION.md](INSTALLATION.md).
|
For installation, see [INSTALLATION.md](INSTALLATION.md).
|
||||||
For service information, see [SERVICES.md](SERVICES.md).
|
For troubleshooting, see [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||||
|
|||||||
+37
-249
@@ -1,257 +1,45 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
All notable changes to HOPS will be documented in this file.
|
All notable changes to HOPS will be documented here.
|
||||||
|
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
||||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
||||||
|
|
||||||
## [3.3.0] - 2025-01-19
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **🔄 Automatic Updates**: Git-based update mechanism with backup functionality
|
|
||||||
- **📱 Command Line Interface**: New flags for update management
|
|
||||||
- `--update`: Update HOPS to latest version automatically
|
|
||||||
- `--check-updates`: Check for available updates (returns exit code 1 if updates available)
|
|
||||||
- `--version`: Display current version information
|
|
||||||
- `--help`: Show comprehensive help and usage information
|
|
||||||
- **🛡️ Safe Updates**: Automatic backup of local changes before updating
|
|
||||||
- **📋 Change Tracking**: Display recent changes and version comparison during updates
|
|
||||||
- **🎛️ Interactive Updates**: Update checking integrated into main menu (option 6)
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Version Numbering**: Updated to v3.3.0 across all components
|
|
||||||
- **Menu Structure**: Added "Check for Updates" as menu option 6, shifted other options
|
|
||||||
- **Documentation**: Updated README.md and CLAUDE.md with new update functionality
|
|
||||||
- **Color Code Handling**: Removed duplicate color definitions, now sourced from lib/common.sh
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Script Compatibility**: Resolved readonly variable conflicts between main script and libraries
|
|
||||||
- **Update Process**: Robust error handling and rollback for failed updates
|
|
||||||
- **Exit Codes**: Proper exit codes for command-line operations
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- **Backup Protection**: Local changes are automatically backed up before any updates
|
|
||||||
- **Git Validation**: Comprehensive validation that HOPS is in a git repository before updates
|
|
||||||
- **Privilege Handling**: Updates require appropriate privileges (root/sudo) for system changes
|
|
||||||
|
|
||||||
## [3.2.0] - 2024-07-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Caddy Support**: Added Caddy reverse proxy as a service option
|
|
||||||
- **Enhanced macOS Compatibility**: Comprehensive improvements for macOS installation and operation
|
|
||||||
- **Docker Desktop Integration**: Improved Docker Desktop startup and management on macOS
|
|
||||||
- **Keychain Integration**: Proper Docker authentication with macOS keychain on macOS
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **User Directory Fixes**: All directories now use actual user home instead of root on macOS
|
|
||||||
- **Password Generation**: Resolved `shuf` command and encoding issues on macOS
|
|
||||||
- **Container Creation**: Fixed Docker Compose working directory and execution context issues
|
|
||||||
- **Healthcheck Improvements**: Enhanced service health monitoring, particularly for Jellyseerr
|
|
||||||
- **File Permissions**: Proper ownership of all directories and files across platforms
|
|
||||||
- **Docker Compose Warnings**: Resolved version warnings and compatibility issues
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **macOS File Structure**: Improved directory layout using user home instead of system directories
|
|
||||||
- **Error Handling**: Enhanced error messages and troubleshooting information for macOS
|
|
||||||
- **Documentation**: Updated platform-specific installation and configuration guides
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- **Secure Authentication**: Enhanced Docker authentication methods on macOS
|
|
||||||
- **File Ownership**: Improved file permission management across all platforms
|
|
||||||
|
|
||||||
## [3.1.0-beta] - 2024-06-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Encrypted Secret Management**: All passwords and sensitive data now encrypted with AES-256
|
|
||||||
- **Input Validation**: Comprehensive validation preventing injection attacks
|
|
||||||
- **Privilege Separation**: Root operations separated from user operations for enhanced security
|
|
||||||
- **Pinned Container Versions**: All container images use specific versions, not `latest`
|
|
||||||
- **Modular Architecture**: Shared code organized in `lib/` directory for better maintainability
|
|
||||||
- **Cross-Platform Support**: Native support for Linux, macOS, and Windows (WSL2)
|
|
||||||
- **Enhanced Error Handling**: Better error messages and recovery mechanisms
|
|
||||||
- **Improved Service Definitions**: Standardized service generation with validation
|
|
||||||
- **Complete Documentation**: Added `CLAUDE.md` for development guidance
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Installation Methods**: New secure installer `setup` script as recommended method
|
|
||||||
- **Service Management**: New `user-operations` script for non-privileged service management
|
|
||||||
- **Architecture**: Modular library system replacing monolithic scripts
|
|
||||||
- **Security Model**: Clear separation between privileged and user operations
|
|
||||||
|
|
||||||
### Security
|
|
||||||
- **AES-256 Encryption**: All secrets stored encrypted with master key management
|
|
||||||
- **Input Sanitization**: Comprehensive validation preventing code injection
|
|
||||||
- **Container Security**: Pinned versions preventing supply chain attacks
|
|
||||||
- **Privilege Minimization**: Reduced root access requirements
|
|
||||||
|
|
||||||
## [3.0.0] - 2024-05-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Cross-Platform Support**: Full support for Linux, macOS, and Windows (WSL2)
|
|
||||||
- **Automatic Dependency Installation**: Docker and system requirements installed automatically
|
|
||||||
- **Platform Detection**: Intelligent OS detection and platform-specific optimizations
|
|
||||||
- **Enhanced Service Catalog**: Expanded service definitions with health checks
|
|
||||||
- **Comprehensive Logging**: Detailed logging system for troubleshooting
|
|
||||||
- **Service Health Monitoring**: Built-in health checks for all services
|
|
||||||
- **Rollback Capabilities**: Automatic rollback on deployment failure
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Installation Process**: Streamlined installation with better user experience
|
|
||||||
- **Directory Structure**: Platform-appropriate directory layouts
|
|
||||||
- **Service Definitions**: Standardized Docker Compose patterns
|
|
||||||
- **Error Handling**: Improved error messages and recovery procedures
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Port Conflict Detection**: Better handling of port conflicts
|
|
||||||
- **Permission Issues**: Improved file permission management
|
|
||||||
- **Service Dependencies**: Enhanced dependency resolution
|
|
||||||
|
|
||||||
## [2.1.0] - 2024-03-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Huntarr Support**: Missing media discovery and automation
|
|
||||||
- **Jellystat Support**: Jellyfin statistics and monitoring
|
|
||||||
- **Watchtower Integration**: Automatic container updates
|
|
||||||
- **Enhanced Monitoring**: Improved service status monitoring
|
|
||||||
- **Backup Utilities**: Built-in backup and recovery tools
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Service Management**: Improved start/stop/restart functionality
|
|
||||||
- **Log Viewing**: Enhanced centralized log viewing
|
|
||||||
- **Configuration Management**: Better environment variable handling
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Memory Usage**: Optimized resource usage for low-resource systems
|
|
||||||
- **Startup Issues**: Resolved service startup race conditions
|
|
||||||
- **Network Configuration**: Fixed Docker network isolation issues
|
|
||||||
|
|
||||||
## [2.0.0] - 2024-02-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Management Interface**: Comprehensive web-based management
|
|
||||||
- **Security Hardening**: Automatic firewall configuration and secure passwords
|
|
||||||
- **Service Templates**: Standardized service definitions
|
|
||||||
- **Real-time Monitoring**: Live service status and resource monitoring
|
|
||||||
- **User Interface**: Menu-driven installation and management
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Architecture**: Complete rewrite with modular design
|
|
||||||
- **Installation**: Simplified one-command installation
|
|
||||||
- **Configuration**: Centralized configuration management
|
|
||||||
|
|
||||||
### Breaking Changes
|
|
||||||
- **Directory Structure**: New standardized directory layout
|
|
||||||
- **Configuration Format**: Updated environment variable structure
|
|
||||||
- **Service Names**: Standardized container and service naming
|
|
||||||
|
|
||||||
## [1.2.0] - 2024-01-15
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Authelia Support**: Multi-factor authentication and SSO
|
|
||||||
- **Nginx Proxy Manager**: Alternative reverse proxy option
|
|
||||||
- **Enhanced SSL**: Automatic SSL certificate management
|
|
||||||
- **Service Discovery**: Automatic service registration
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
- **Traefik Configuration**: Improved reverse proxy setup
|
|
||||||
- **SSL Issues**: Resolved certificate generation problems
|
|
||||||
- **Network Routing**: Fixed internal service communication
|
|
||||||
|
|
||||||
## [1.1.0] - 2023-12-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Traefik Integration**: Automatic reverse proxy with SSL
|
|
||||||
- **Service Categories**: Organized services by function
|
|
||||||
- **Dependency Management**: Automatic service dependency resolution
|
|
||||||
- **Health Checks**: Service health monitoring and restart
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
- **Service Definitions**: Improved Docker Compose templates
|
|
||||||
- **Network Configuration**: Enhanced Docker networking
|
|
||||||
|
|
||||||
## [1.0.0] - 2023-11-01
|
|
||||||
|
|
||||||
### Added
|
|
||||||
- **Initial Release**: Core HOPS functionality
|
|
||||||
- **Service Support**: Basic *arr stack, download clients, and media servers
|
|
||||||
- **Docker Integration**: Docker Compose based deployment
|
|
||||||
- **Linux Support**: Ubuntu/Debian/Mint support
|
|
||||||
- **Basic Management**: Simple service management interface
|
|
||||||
|
|
||||||
### Features
|
|
||||||
- **Automated Installation**: One-command deployment
|
|
||||||
- **Service Selection**: Interactive service selection
|
|
||||||
- **Basic Security**: Firewall rules and secure passwords
|
|
||||||
- **Directory Management**: Automatic directory creation and permissions
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Version Support
|
## [Unreleased]
|
||||||
|
|
||||||
- **v3.3.x**: Current stable release with automatic update support
|
|
||||||
- **v3.2.x**: Previous stable release, upgrade recommended
|
|
||||||
- **v3.1.x**: Beta features, limited support
|
|
||||||
- **v3.0.x**: Legacy support for critical bugs only
|
|
||||||
- **v2.x and earlier**: No longer supported
|
|
||||||
|
|
||||||
## Upgrade Path
|
|
||||||
|
|
||||||
### From v3.2.x to v3.3.0 (Recommended - Automatic)
|
|
||||||
```bash
|
|
||||||
# Use built-in update system
|
|
||||||
cd /path/to/hops
|
|
||||||
sudo ./hops --update
|
|
||||||
|
|
||||||
# Or use interactive menu
|
|
||||||
sudo ./hops
|
|
||||||
# Select option 6: Check for Updates
|
|
||||||
```
|
|
||||||
|
|
||||||
### From v3.1.x to v3.3.0 (Manual)
|
|
||||||
```bash
|
|
||||||
# Backup current installation
|
|
||||||
sudo tar -czf hops-backup-$(date +%Y%m%d).tar.gz ~/hops /opt/appdata
|
|
||||||
|
|
||||||
# Pull latest version
|
|
||||||
cd ~/hops
|
|
||||||
git pull origin main
|
|
||||||
|
|
||||||
# Run upgrade
|
|
||||||
sudo ./setup --upgrade
|
|
||||||
```
|
|
||||||
|
|
||||||
### From v3.0.x to v3.2.0
|
|
||||||
```bash
|
|
||||||
# Major version upgrade requires fresh installation
|
|
||||||
# Backup data first
|
|
||||||
sudo ./uninstall --keep-data
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
### From v2.x to v3.2.0
|
|
||||||
```bash
|
|
||||||
# Migration script available
|
|
||||||
sudo ./migrate-from-v2.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
## Migration Notes
|
|
||||||
|
|
||||||
### v3.2.0 Changes
|
|
||||||
- **macOS Users**: Directory structure has changed, migration handled automatically
|
|
||||||
- **Caddy Users**: Manual Caddyfile configuration required
|
|
||||||
- **Configuration**: Encrypted secrets now default for new installations
|
|
||||||
|
|
||||||
### v3.1.0 Changes
|
|
||||||
- **Security**: All passwords moved to encrypted storage
|
|
||||||
- **Architecture**: New modular library system
|
|
||||||
- **Privileges**: Installation process now uses privilege separation
|
|
||||||
|
|
||||||
### v3.0.0 Changes
|
|
||||||
- **Cross-Platform**: New platform detection and configuration
|
|
||||||
- **Directories**: Platform-specific directory structures
|
|
||||||
- **Services**: Updated service definitions and health checks
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
For detailed upgrade instructions, see [INSTALLATION.md](INSTALLATION.md).
|
## [1.0.1] - 2026-06-10
|
||||||
For breaking changes and migration help, see [ADVANCED.md](ADVANCED.md).
|
|
||||||
|
Stabilization pass: bug fixes, security hardening, and codebase consolidation
|
||||||
|
on the Path A install pipeline.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Infinite recursion in get_timezone_mount() and get_gpu_devices() on Linux (B1)
|
||||||
|
- ((x++)) abort under set -e across hops and install (B5)
|
||||||
|
- Glob stored as string breaking multi-user directory detection (B3)
|
||||||
|
- Missing hops_service_definitions.sh reference in firewall setup (B4)
|
||||||
|
- Linux-only guard missing from hops entry point (B6)
|
||||||
|
- eval echo "~$SUDO_USER" replaced with getent passwd in uninstall (B3/S5)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Duplicate log, error_exit, warning, success, info removed from hops,
|
||||||
|
uninstall, and install; single canonical copies in lib/common.sh (A5, Q1)
|
||||||
|
- Duplicate validate_password, generate_secure_password, create_docker_networks,
|
||||||
|
validate_timezone removed from install; lib/security.sh, lib/docker.sh,
|
||||||
|
lib/validation.sh are now the sole definitions (A5)
|
||||||
|
- lib/docker.sh, lib/validation.sh, lib/security.sh use LIB_DIR instead of
|
||||||
|
SCRIPT_DIR so sourcing them inside a function does not clobber the caller
|
||||||
|
- validate_timezone updated to warn-and-default instead of error_exit
|
||||||
|
- validate_password updated to handle empty input (return 3)
|
||||||
|
- uninstall sources lib/common.sh directly, allowing standalone execution
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Path B install pipeline (setup, privileged-setup, user-operations,
|
||||||
|
services-improved, lib/privileges.sh) -- dead code, never wired in (A1, A3)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## [1.0.0] - TBD
|
||||||
|
|
||||||
|
Full rewrite establishing the Path A install pipeline.
|
||||||
|
|||||||
@@ -0,0 +1,84 @@
|
|||||||
|
# HOPS - Claude Code Project Guide
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
HOPS (Homelab Orchestration Provisioning Script) is a bash-based tool that
|
||||||
|
deploys homelab infrastructure via Docker Compose. It presents an interactive
|
||||||
|
menu for selecting services, generates a docker-compose.yml and .env, installs
|
||||||
|
Docker if needed, and manages the lifecycle of the deployment.
|
||||||
|
|
||||||
|
**Target platform**: Linux only (Ubuntu 20.04+, Debian 11+, Linux Mint 20+).
|
||||||
|
macOS and WSL2 are on the future roadmap, not in scope for 1.0.0.
|
||||||
|
|
||||||
|
## Install Pipeline (Path A -- canonical)
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo ./hops # Entry point and management menu
|
||||||
|
-> install # Full installation logic (sourced by hops)
|
||||||
|
-> services # Service definitions and compose generators (sourced by install)
|
||||||
|
-> uninstall # Removal logic (standalone)
|
||||||
|
```
|
||||||
|
|
||||||
|
All other top-level scripts and `lib/privileges.sh` were Path B artifacts and
|
||||||
|
have been deleted. Do not recreate them.
|
||||||
|
|
||||||
|
## File Map
|
||||||
|
|
||||||
|
| File | Purpose |
|
||||||
|
|------|---------|
|
||||||
|
| `hops` | Entry point, management menu, update logic |
|
||||||
|
| `install` | Full install flow: Docker setup, service selection, compose generation, firewall |
|
||||||
|
| `uninstall` | Reversal of install: stops containers, removes dirs, optionally purges Docker |
|
||||||
|
| `services` | Single canonical service catalog: compose generators, port map, image refs |
|
||||||
|
| `lib/common.sh` | Logging, UI helpers, shared utilities -- source this, don't duplicate |
|
||||||
|
| `lib/system.sh` | OS detection, Docker install, system requirement checks |
|
||||||
|
| `lib/docker.sh` | Docker service management, status checks, network helpers |
|
||||||
|
| `lib/security.sh` | Password generation, validation, firewall, security audit |
|
||||||
|
| `lib/validation.sh` | Input sanitisation and validation functions |
|
||||||
|
| `lib/secrets.sh` | .env encryption at rest (being fixed and wired in -- see TODO M4/A4/S1) |
|
||||||
|
|
||||||
|
## Key Architectural Decisions
|
||||||
|
|
||||||
|
- **One service catalog**: `services` is the single source of truth for images,
|
||||||
|
ports, and compose definitions. `lib/docker.sh` has a secondary map that must
|
||||||
|
be kept in sync until reconciled (TODO A2).
|
||||||
|
- **Latest image tags**: all service images use `latest`, not pinned versions.
|
||||||
|
- **Secrets**: `lib/secrets.sh` will encrypt the `.env` file at rest using gpg.
|
||||||
|
Currently being fixed -- the old AES-GCM implementation was broken.
|
||||||
|
- **No duplicate functions**: consolidate into `lib/common.sh`. Do not define
|
||||||
|
`log`, `error_exit`, `warning`, `success`, or `info` in any other file.
|
||||||
|
|
||||||
|
## Coding Conventions
|
||||||
|
|
||||||
|
- Bash only. No Python, no Node, no external interpreters required at runtime.
|
||||||
|
- ASCII only -- no Unicode, no emojis in scripts or output strings.
|
||||||
|
- No comments explaining what the code does. Only add a comment when the WHY
|
||||||
|
is non-obvious (hidden constraint, workaround, subtle invariant).
|
||||||
|
- Error handling: use `set -e` with care. Arithmetic increments must use
|
||||||
|
`x=$((x + 1))` not `((x++))` -- the latter aborts under `set -e` when the
|
||||||
|
pre-increment value is 0.
|
||||||
|
- Temp files: always use `mktemp`, never `/tmp/name_$$`.
|
||||||
|
- User home resolution: use `getent passwd "$SUDO_USER" | cut -d: -f6`,
|
||||||
|
never `eval echo "~$SUDO_USER"`.
|
||||||
|
- Command arrays: build commands as bash arrays, not strings, to avoid
|
||||||
|
word-splitting on unusual usernames or paths.
|
||||||
|
|
||||||
|
## Running / Testing
|
||||||
|
|
||||||
|
There is no test suite. Validate with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash -n hops install uninstall services # syntax check
|
||||||
|
bash -n lib/*.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Full integration testing requires a Linux VM. The scripts must be run as root
|
||||||
|
or via sudo on a supported distro.
|
||||||
|
|
||||||
|
## Dynamic Information
|
||||||
|
|
||||||
|
The following docs change frequently -- check them rather than this file:
|
||||||
|
|
||||||
|
- **TODO.md** -- prioritised bug list, cleanup tasks, and feature backlog
|
||||||
|
- **CHANGELOG.md** -- version history and unreleased changes
|
||||||
|
- **SERVICES.md** -- supported service list with ports and upstream links
|
||||||
+28
-331
@@ -1,78 +1,37 @@
|
|||||||
# Installation Guide
|
# Installation Guide
|
||||||
|
|
||||||
This guide provides detailed installation instructions for all supported platforms.
|
> **Note**: This document is being rebuilt alongside the 1.0.0 codebase.
|
||||||
|
> Content will be expanded as features are stabilized.
|
||||||
|
|
||||||
## 🔧 System Requirements
|
## System Requirements
|
||||||
|
|
||||||
### Minimum Requirements
|
- **OS**: Ubuntu 20.04+, Debian 11+, or Linux Mint 20+ (x86_64)
|
||||||
- **RAM**: 2GB (4GB+ recommended)
|
- **RAM**: 2GB minimum, 4GB+ recommended
|
||||||
- **Storage**: 10GB free space (more for media storage)
|
- **Storage**: 10GB free (more for media)
|
||||||
- **CPU**: 2 cores recommended
|
- **CPU**: 2 cores recommended
|
||||||
- **Network**: Internet connection required
|
- **Access**: sudo privileges
|
||||||
|
|
||||||
### Platform-Specific Requirements
|
## Installation
|
||||||
|
|
||||||
#### Linux (Ubuntu/Debian/Mint)
|
|
||||||
- **OS**: Ubuntu 20.04+, Debian 11+, or Linux Mint 20+
|
|
||||||
- **Architecture**: x86_64
|
|
||||||
- **Access**: Root/sudo privileges
|
|
||||||
- **Dependencies**: Automatically installed
|
|
||||||
|
|
||||||
#### macOS
|
|
||||||
- **OS**: macOS 11.0+ (Big Sur)
|
|
||||||
- **Architecture**: Intel (x86_64) or Apple Silicon (ARM64)
|
|
||||||
- **Access**: Admin privileges (sudo)
|
|
||||||
- **Dependencies**: Homebrew and Docker Desktop installed automatically
|
|
||||||
|
|
||||||
#### Windows (WSL2)
|
|
||||||
- **OS**: Windows 10 (build 19041+) or Windows 11
|
|
||||||
- **WSL**: WSL2 enabled with Ubuntu 20.04+ distribution
|
|
||||||
- **Docker**: Docker Desktop with WSL2 backend
|
|
||||||
- **Access**: Admin access for initial setup
|
|
||||||
- **Architecture**: x86_64 with virtualization support
|
|
||||||
- **BIOS**: Hyper-V and virtualization enabled
|
|
||||||
|
|
||||||
⚠️ **Note**: Windows support has limited testing. Proceed with caution and ensure backups.
|
|
||||||
|
|
||||||
## 🐧 Linux Installation
|
|
||||||
|
|
||||||
### Quick Install
|
|
||||||
```bash
|
```bash
|
||||||
# Download HOPS
|
git clone https://git.canadabot.net/canadabot/hops.git
|
||||||
git clone https://github.com/skiercm/hops.git
|
|
||||||
cd hops
|
cd hops
|
||||||
chmod +x hops install uninstall setup
|
chmod +x hops install uninstall
|
||||||
|
|
||||||
# Run installation
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Installation (Advanced)
|
|
||||||
```bash
|
|
||||||
# Two-phase installation for enhanced security
|
|
||||||
sudo ./privileged-setup # Root operations
|
|
||||||
./user-operations generate <services> # User operations
|
|
||||||
./user-operations deploy # Deploy services
|
|
||||||
|
|
||||||
# Legacy method (still supported)
|
|
||||||
sudo ./hops
|
sudo ./hops
|
||||||
```
|
```
|
||||||
|
|
||||||
### What Gets Installed
|
HOPS will install Docker automatically if not present, then walk you through
|
||||||
- Docker Engine (via official Docker script)
|
selecting and deploying services interactively.
|
||||||
- Docker Compose
|
|
||||||
- Required system packages
|
## Directory Structure
|
||||||
- UFW firewall rules (automatic)
|
|
||||||
- Service directories and permissions
|
|
||||||
|
|
||||||
### Directory Structure
|
|
||||||
```
|
```
|
||||||
~/hops/ # Main deployment directory
|
~/hops/ # Deployment directory
|
||||||
├── docker-compose.yml # Service definitions
|
├── docker-compose.yml # Generated service definitions
|
||||||
├── .env # Environment variables
|
├── .env # Environment variables and secrets
|
||||||
└── logs/ # Application logs
|
└── logs/ # HOPS logs
|
||||||
|
|
||||||
/opt/appdata/ # Application configurations
|
/opt/appdata/ # Application config data
|
||||||
├── jellyfin/
|
├── jellyfin/
|
||||||
├── sonarr/
|
├── sonarr/
|
||||||
└── [other services]/
|
└── [other services]/
|
||||||
@@ -84,282 +43,20 @@ sudo ./hops
|
|||||||
└── downloads/
|
└── downloads/
|
||||||
```
|
```
|
||||||
|
|
||||||
## 🍎 macOS Installation
|
## Post-Installation
|
||||||
|
|
||||||
### Prerequisites Setup
|
|
||||||
HOPS automatically handles dependency installation on macOS, but you can pre-install if desired:
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Optional: Install Homebrew (will be done automatically)
|
# Check service status
|
||||||
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
cd ~/hops && docker compose ps
|
||||||
|
|
||||||
# Optional: Install Docker Desktop (will be done automatically)
|
# View logs
|
||||||
brew install --cask docker
|
docker compose logs -f [service-name]
|
||||||
```
|
|
||||||
|
|
||||||
### HOPS Installation
|
# Access management interface
|
||||||
```bash
|
|
||||||
# Download HOPS
|
|
||||||
git clone https://github.com/skiercm/hops.git
|
|
||||||
cd hops
|
|
||||||
chmod +x hops install uninstall setup
|
|
||||||
|
|
||||||
# Run installation with admin privileges
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
### macOS-Specific Features
|
|
||||||
- **Docker Desktop Integration**: Automatic installation and startup
|
|
||||||
- **Keychain Authentication**: Secure Docker authentication
|
|
||||||
- **User Directory Structure**: All files in user home directory
|
|
||||||
- **Automatic Dependency Resolution**: Homebrew packages installed as needed
|
|
||||||
|
|
||||||
### Directory Structure (macOS)
|
|
||||||
```
|
|
||||||
~/hops/ # Main deployment directory
|
|
||||||
├── docker-compose.yml # Service definitions
|
|
||||||
├── .env # Environment variables
|
|
||||||
├── logs/ # Application logs
|
|
||||||
├── config/ # Application configurations
|
|
||||||
│ ├── jellyfin/
|
|
||||||
│ ├── sonarr/
|
|
||||||
│ └── [other services]/
|
|
||||||
└── media/ # Media storage
|
|
||||||
├── movies/
|
|
||||||
├── tv/
|
|
||||||
├── music/
|
|
||||||
└── downloads/
|
|
||||||
```
|
|
||||||
|
|
||||||
### Important Notes for macOS
|
|
||||||
- **GPU Acceleration**: Not available (Docker containers cannot access macOS GPU)
|
|
||||||
- **Firewall**: Manual configuration required (automatic UFW setup skipped)
|
|
||||||
- **File Permissions**: Uses user's home directory for better compatibility
|
|
||||||
- **Performance**: Excellent performance on both Intel and Apple Silicon
|
|
||||||
|
|
||||||
## 🪟 Windows Installation (WSL2)
|
|
||||||
|
|
||||||
Windows support uses WSL2 (Windows Subsystem for Linux) for excellent Linux compatibility.
|
|
||||||
|
|
||||||
### Step 1: Enable WSL2
|
|
||||||
```powershell
|
|
||||||
# Run in PowerShell as Administrator
|
|
||||||
wsl --install
|
|
||||||
# Restart computer when prompted
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 2: Install Ubuntu Distribution
|
|
||||||
```powershell
|
|
||||||
# Install Ubuntu 22.04 LTS (recommended)
|
|
||||||
wsl --install Ubuntu-22.04
|
|
||||||
# Follow prompts to create username and password
|
|
||||||
```
|
|
||||||
|
|
||||||
### Step 3: Install Docker Desktop
|
|
||||||
1. Download [Docker Desktop for Windows](https://docs.docker.com/desktop/install/windows-install/)
|
|
||||||
2. Enable WSL2 integration during installation
|
|
||||||
3. Ensure "Use WSL 2 based engine" is enabled in Docker settings
|
|
||||||
4. Enable integration with your Ubuntu distribution
|
|
||||||
|
|
||||||
### Step 4: Install HOPS
|
|
||||||
```bash
|
|
||||||
# Launch Ubuntu from Start Menu or run: wsl -d Ubuntu-22.04
|
|
||||||
cd ~
|
|
||||||
git clone https://github.com/skiercm/hops.git
|
|
||||||
cd hops
|
|
||||||
chmod +x hops install uninstall setup
|
|
||||||
|
|
||||||
# Run installation (same as Linux)
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
### Windows-Specific Considerations
|
|
||||||
|
|
||||||
#### File System Performance
|
|
||||||
- **Always run HOPS from WSL2 filesystem** (`~/hops/`) for optimal performance
|
|
||||||
- **Avoid Windows drives** (`/mnt/c/`) as they have significant performance penalties
|
|
||||||
- WSL2 provides 95% of native Linux performance when using WSL2 filesystem
|
|
||||||
|
|
||||||
#### Media Access
|
|
||||||
Your Windows media can be accessed from WSL2:
|
|
||||||
- `C:\Users\YourName\` → `/mnt/c/Users/YourName/`
|
|
||||||
- External drives → `/mnt/d/`, `/mnt/e/`, etc.
|
|
||||||
|
|
||||||
#### Service Access
|
|
||||||
- **Web interfaces**: Accessible from Windows browsers using WSL2 IP or localhost
|
|
||||||
- **File shares**: Available in Windows Explorer via `\\wsl.localhost\Ubuntu-22.04\home\username\hops\`
|
|
||||||
- **Network**: Services are accessible from both Windows and WSL2
|
|
||||||
|
|
||||||
#### Directory Structure (Windows/WSL2)
|
|
||||||
```
|
|
||||||
# WSL2 filesystem (recommended location)
|
|
||||||
~/hops/ # Main deployment directory
|
|
||||||
├── docker-compose.yml # Service definitions
|
|
||||||
├── .env # Environment variables
|
|
||||||
└── logs/ # Application logs
|
|
||||||
|
|
||||||
/opt/appdata/ # Application configurations
|
|
||||||
├── jellyfin/
|
|
||||||
├── sonarr/
|
|
||||||
└── [other services]/
|
|
||||||
|
|
||||||
/mnt/media/ # Media storage (can link to Windows drives)
|
|
||||||
├── movies/ -> /mnt/d/Movies/
|
|
||||||
├── tv/ -> /mnt/d/TV/
|
|
||||||
├── music/ -> /mnt/d/Music/
|
|
||||||
└── downloads/
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Installation Options
|
|
||||||
|
|
||||||
### Option 1: Secure Installation (Recommended)
|
|
||||||
```bash
|
|
||||||
sudo ./setup
|
|
||||||
```
|
|
||||||
- **Best Practice**: Separates privileged and user operations
|
|
||||||
- **Enhanced Security**: Minimizes root access time
|
|
||||||
- **User-Friendly**: Guided interactive setup
|
|
||||||
|
|
||||||
### Option 2: Manual Two-Phase Installation
|
|
||||||
```bash
|
|
||||||
sudo ./privileged-setup # Root-only operations
|
|
||||||
./user-operations generate [services] # Select and generate services
|
|
||||||
./user-operations deploy # Deploy services
|
|
||||||
```
|
|
||||||
- **Advanced Users**: Full control over each phase
|
|
||||||
- **Automation**: Can be scripted for multiple deployments
|
|
||||||
- **Security**: Clear separation of privileged operations
|
|
||||||
|
|
||||||
### Option 3: Legacy Installation
|
|
||||||
```bash
|
|
||||||
sudo ./hops
|
sudo ./hops
|
||||||
```
|
```
|
||||||
- **Compatibility**: Original installation method
|
|
||||||
- **Full-Featured**: Complete management interface
|
|
||||||
- **Reliability**: Extensively tested method
|
|
||||||
|
|
||||||
## 🔍 Post-Installation Verification
|
## Getting Help
|
||||||
|
|
||||||
### Check Service Status
|
- Issues: [git.canadabot.net/canadabot/hops/issues](https://git.canadabot.net/canadabot/hops/issues)
|
||||||
```bash
|
- Troubleshooting: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
# Via HOPS management interface
|
|
||||||
sudo ./hops
|
|
||||||
# Select option 4: Service Status
|
|
||||||
|
|
||||||
# Via user operations
|
|
||||||
./user-operations status
|
|
||||||
|
|
||||||
# Direct Docker commands
|
|
||||||
cd ~/hops
|
|
||||||
docker compose ps
|
|
||||||
```
|
|
||||||
|
|
||||||
### Access Service URLs
|
|
||||||
After installation, HOPS provides URLs for all services:
|
|
||||||
```
|
|
||||||
📱 Access your services at:
|
|
||||||
● Jellyfin http://192.168.1.100:8096
|
|
||||||
● Sonarr http://192.168.1.100:8989
|
|
||||||
● Radarr http://192.168.1.100:7878
|
|
||||||
● Portainer http://192.168.1.100:9000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Verify File Permissions
|
|
||||||
```bash
|
|
||||||
# Check directory ownership
|
|
||||||
ls -la ~/hops/
|
|
||||||
ls -la /opt/appdata/ (Linux) or ~/hops/config/ (macOS)
|
|
||||||
ls -la /mnt/media/ (Linux) or ~/hops/media/ (macOS)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🆘 Troubleshooting Installation
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
#### Docker Installation Failed
|
|
||||||
```bash
|
|
||||||
# Check Docker status
|
|
||||||
systemctl status docker
|
|
||||||
|
|
||||||
# Restart Docker
|
|
||||||
sudo systemctl restart docker
|
|
||||||
|
|
||||||
# Reinstall Docker (Linux)
|
|
||||||
curl -fsSL https://get.docker.com | sh
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Permission Denied Errors
|
|
||||||
```bash
|
|
||||||
# Fix directory ownership
|
|
||||||
sudo chown -R $USER:$USER ~/hops/
|
|
||||||
sudo chown -R $USER:$USER /opt/appdata/ (Linux)
|
|
||||||
sudo chown -R $USER:$USER ~/hops/config/ (macOS)
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Port Conflicts
|
|
||||||
```bash
|
|
||||||
# Check what's using a port
|
|
||||||
sudo lsof -i :PORT_NUMBER
|
|
||||||
|
|
||||||
# Kill conflicting process
|
|
||||||
sudo kill -9 PID
|
|
||||||
```
|
|
||||||
|
|
||||||
#### WSL2 Issues (Windows)
|
|
||||||
```bash
|
|
||||||
# Restart WSL2
|
|
||||||
wsl --shutdown
|
|
||||||
wsl -d Ubuntu-22.04
|
|
||||||
|
|
||||||
# Check Docker Desktop WSL2 integration
|
|
||||||
# Go to Docker Desktop Settings → Resources → WSL Integration
|
|
||||||
```
|
|
||||||
|
|
||||||
### Log Locations
|
|
||||||
- **Installation Logs**:
|
|
||||||
- Linux: `/var/log/hops/`
|
|
||||||
- macOS: `/usr/local/var/log/hops/`
|
|
||||||
- Windows: `~/hops/logs/`
|
|
||||||
- **Service Logs**: `docker compose logs [service-name]`
|
|
||||||
- **System Logs**: `journalctl -u docker`
|
|
||||||
|
|
||||||
### Getting Help
|
|
||||||
1. **Built-in Help**: `sudo ./hops` → Option 7
|
|
||||||
2. **Check Logs**: Review installation logs for errors
|
|
||||||
3. **Verify Prerequisites**: Ensure all system requirements are met
|
|
||||||
4. **Docker Status**: Confirm Docker is running and accessible
|
|
||||||
5. **GitHub Issues**: Report persistent issues with logs
|
|
||||||
|
|
||||||
## 🔄 Backup Strategy
|
|
||||||
|
|
||||||
### Before Installation
|
|
||||||
```bash
|
|
||||||
# Backup existing media and configs
|
|
||||||
sudo tar -czf homelab-backup-$(date +%Y%m%d).tar.gz \
|
|
||||||
/path/to/your/media \
|
|
||||||
/path/to/your/configs
|
|
||||||
```
|
|
||||||
|
|
||||||
### After Installation
|
|
||||||
```bash
|
|
||||||
# Backup HOPS configuration
|
|
||||||
sudo tar -czf hops-config-backup-$(date +%Y%m%d).tar.gz \
|
|
||||||
~/hops \
|
|
||||||
/opt/appdata (Linux) or ~/hops/config (macOS)
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📊 Performance Optimization
|
|
||||||
|
|
||||||
### During Installation
|
|
||||||
- **SSD Storage**: Install on SSD for better performance
|
|
||||||
- **Sufficient RAM**: Ensure adequate memory for selected services
|
|
||||||
- **Network Speed**: Faster internet improves Docker image downloads
|
|
||||||
|
|
||||||
### After Installation
|
|
||||||
- **Monitor Resources**: Use Portainer to monitor CPU, RAM, and disk usage
|
|
||||||
- **Optimize Services**: Start with fewer services, add more gradually
|
|
||||||
- **Storage Configuration**: Use dedicated drives for media storage
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
For additional help, see [ADVANCED.md](ADVANCED.md) for configuration and troubleshooting details.
|
|
||||||
|
|||||||
@@ -1,41 +1,27 @@
|
|||||||
# HOPS - Homelab Orchestration Provisioning Script
|
# HOPS - Homelab Orchestration Provisioning Script
|
||||||
|
|
||||||
[](https://opensource.org/licenses/MIT)
|
[](https://opensource.org/licenses/MIT)
|
||||||
[]()
|
[]()
|
||||||
[]()
|
[]()
|
||||||
|
|
||||||
**HOPS** is a comprehensive automation tool for deploying homelab infrastructure using Docker Compose. Deploy and manage popular homelab services including media servers, download clients, monitoring tools, and more through an intuitive menu-driven interface.
|
**HOPS** is a comprehensive automation tool for deploying homelab infrastructure using Docker Compose. Deploy and manage popular homelab services including media servers, download clients, monitoring tools, and more through an intuitive menu-driven interface.
|
||||||
|
|
||||||
## ⚠️ Important: Beta Software
|
## Important: Beta Software
|
||||||
**HOPS is beta software**. Always backup your data before installation and test in non-production environments first.
|
**HOPS is beta software**. Always backup your data before installation and test in non-production environments first.
|
||||||
|
|
||||||
**Platform Status:**
|
**Platform Status:**
|
||||||
- **Linux**: ✅ Stable and extensively tested
|
- **Linux**: Stable and actively developed
|
||||||
- **macOS**: ✅ Recently improved with v3.2.0 fixes
|
|
||||||
- **Windows (WSL2)**: ⚠️ Limited testing
|
|
||||||
|
|
||||||
## 🆕 What's New in v3.3.0
|
## Key Features
|
||||||
- **🔄 Automatic Updates**: Git-based update system with backup functionality
|
|
||||||
- **📱 Command Line Interface**: `--update`, `--check-updates`, `--version`, `--help` flags
|
|
||||||
- **🛡️ Safe Updates**: Automatic backup of local changes before updating
|
|
||||||
- **📋 Change Tracking**: View recent changes and version comparison
|
|
||||||
|
|
||||||
### Previous Updates (v3.2.0)
|
- **Easy Installation**: One-command setup with automatic Docker installation
|
||||||
- **Enhanced macOS Support**: Docker Desktop integration, keychain authentication, user directory fixes
|
- **Security First**: Encrypted secrets, firewall configuration, input validation
|
||||||
- **Caddy Support**: Added reverse proxy option (user provides configuration)
|
- **Management**: Real-time monitoring, centralized logs, service control
|
||||||
- **Bug Fixes**: Password generation, container creation, healthchecks, file permissions
|
- **Auto-Updates**: Built-in update system with backup protection
|
||||||
- **Security**: Encrypted secret management, input validation, privilege separation
|
- **Reliability**: Error handling, rollback capabilities, dependency management
|
||||||
|
- **Linux Native**: Ubuntu, Debian, and Linux Mint support
|
||||||
|
|
||||||
## ✨ Key Features
|
## Supported Services
|
||||||
|
|
||||||
- **🚀 Easy Installation**: One-command setup with automatic Docker installation
|
|
||||||
- **🔒 Security First**: Encrypted secrets, firewall configuration, input validation
|
|
||||||
- **📊 Management**: Real-time monitoring, centralized logs, service control
|
|
||||||
- **🔄 Auto-Updates**: Built-in update system with backup protection
|
|
||||||
- **🔧 Reliability**: Error handling, rollback capabilities, dependency management
|
|
||||||
- **🌐 Cross-Platform**: Linux, macOS, and Windows (WSL2) support
|
|
||||||
|
|
||||||
## 📱 Supported Services
|
|
||||||
|
|
||||||
**Media Management**: Sonarr, Radarr, Lidarr, Readarr, Bazarr, Prowlarr, Tdarr, Huntarr
|
**Media Management**: Sonarr, Radarr, Lidarr, Readarr, Bazarr, Prowlarr, Tdarr, Huntarr
|
||||||
**Download Clients**: qBittorrent, Transmission, NZBGet, SABnzbd
|
**Download Clients**: qBittorrent, Transmission, NZBGet, SABnzbd
|
||||||
@@ -46,27 +32,25 @@
|
|||||||
|
|
||||||
[View complete service list with support links →](SERVICES.md)
|
[View complete service list with support links →](SERVICES.md)
|
||||||
|
|
||||||
## 🔧 System Requirements
|
## System Requirements
|
||||||
|
|
||||||
**Minimum**: 2GB RAM, 10GB storage, 2 CPU cores, internet connection
|
**Minimum**: 2GB RAM, 10GB storage, 2 CPU cores, internet connection
|
||||||
|
|
||||||
**Supported Platforms:**
|
**Supported Platforms:**
|
||||||
- **Linux**: Ubuntu 20.04+, Debian 11+, Linux Mint 20+ (x86_64, sudo access)
|
- **Linux**: Ubuntu 20.04+, Debian 11+, Linux Mint 20+ (x86_64, sudo access)
|
||||||
- **macOS**: 11.0+ Big Sur, Intel/Apple Silicon (admin access, auto-installs Docker Desktop)
|
|
||||||
- **Windows**: 10/11 with WSL2 + Ubuntu 20.04+ (limited testing, requires Docker Desktop)
|
|
||||||
|
|
||||||
[View detailed installation guides →](INSTALLATION.md)
|
[View detailed installation guides →](INSTALLATION.md)
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## Quick Start
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Download HOPS
|
# 1. Download HOPS
|
||||||
git clone https://github.com/skiercm/hops.git
|
git clone https://git.canadabot.net/canadabot/hops.git
|
||||||
cd hops
|
cd hops
|
||||||
chmod +x hops install uninstall setup
|
chmod +x hops install uninstall
|
||||||
|
|
||||||
# 2. Run installation
|
# 2. Run installation
|
||||||
sudo ./setup
|
sudo ./hops
|
||||||
|
|
||||||
# 3. Follow interactive setup to select services
|
# 3. Follow interactive setup to select services
|
||||||
|
|
||||||
@@ -76,10 +60,10 @@ sudo ./setup
|
|||||||
|
|
||||||
**Directory Structure:**
|
**Directory Structure:**
|
||||||
- `~/hops/` - Main deployment (docker-compose.yml, .env, logs)
|
- `~/hops/` - Main deployment (docker-compose.yml, .env, logs)
|
||||||
- `/opt/appdata/` (Linux) or `~/hops/config/` (macOS) - App configs
|
- `/opt/appdata/` - App configs
|
||||||
- `/mnt/media/` (Linux) or `~/hops/media/` (macOS) - Media storage
|
- `/mnt/media/` - Media storage
|
||||||
|
|
||||||
## 🎛️ Management
|
## Management
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Access management interface
|
# Access management interface
|
||||||
@@ -90,36 +74,30 @@ sudo ./hops --update # Update to latest version
|
|||||||
sudo ./hops --check-updates # Check for updates
|
sudo ./hops --check-updates # Check for updates
|
||||||
./hops --version # Show version
|
./hops --version # Show version
|
||||||
./hops --help # Show help
|
./hops --help # Show help
|
||||||
|
|
||||||
# Service operations (no sudo required)
|
|
||||||
./user-operations status # View service status
|
|
||||||
./user-operations logs <service> # View logs
|
|
||||||
./user-operations deploy # Deploy services
|
|
||||||
./user-operations stop # Stop all services
|
|
||||||
```
|
```
|
||||||
|
|
||||||
[View advanced configuration and troubleshooting →](ADVANCED.md)
|
[View advanced configuration and troubleshooting →](ADVANCED.md)
|
||||||
|
|
||||||
## 📞 Support
|
## Support
|
||||||
|
|
||||||
**HOPS Issues**: [GitHub Issues](https://github.com/skiercm/hops/issues) | [Discussions](https://github.com/skiercm/hops/discussions)
|
**HOPS Issues**: [Gitea Issues](https://git.canadabot.net/canadabot/hops/issues)
|
||||||
|
|
||||||
**Service Issues**: Contact individual service developers (links in [SERVICES.md](SERVICES.md))
|
**Service Issues**: Contact individual service developers (links in [SERVICES.md](SERVICES.md))
|
||||||
|
|
||||||
**Contact HOPS for**: Installation/deployment issues, Docker Compose problems, cross-platform issues
|
**Contact HOPS for**: Installation/deployment issues, Docker Compose problems, cross-platform issues
|
||||||
**Contact Service Developers for**: Service configuration, features, service-specific bugs
|
**Contact Service Developers for**: Service configuration, features, service-specific bugs
|
||||||
|
|
||||||
## 📄 Documentation
|
## Documentation
|
||||||
|
|
||||||
- [INSTALLATION.md](INSTALLATION.md) - Detailed installation guides for all platforms
|
- [INSTALLATION.md](INSTALLATION.md) - Detailed installation guide
|
||||||
- [SERVICES.md](SERVICES.md) - Complete service list with support links
|
- [SERVICES.md](SERVICES.md) - Complete service list with support links
|
||||||
- [ADVANCED.md](ADVANCED.md) - Configuration, troubleshooting, security
|
- [ADVANCED.md](ADVANCED.md) - Configuration, troubleshooting, security
|
||||||
- [CHANGELOG.md](CHANGELOG.md) - Version history and changes
|
- [CHANGELOG.md](CHANGELOG.md) - Version history and changes
|
||||||
|
|
||||||
## 📄 License
|
## License
|
||||||
|
|
||||||
MIT License - see [LICENSE](LICENSE) file for details.
|
MIT License - see [LICENSE](LICENSE) file for details.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Made with ❤️ for the homelab community**
|
**Made for the homelab community**
|
||||||
|
|||||||
+12
-22
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
HOPS supports a comprehensive collection of homelab services across multiple categories. All services use LinuxServer.io containers for consistency and reliability.
|
HOPS supports a comprehensive collection of homelab services across multiple categories. All services use LinuxServer.io containers for consistency and reliability.
|
||||||
|
|
||||||
## 📺 Media Management (*arr Stack)
|
## Media Management (*arr Stack)
|
||||||
|
|
||||||
### Sonarr - TV Series Management
|
### Sonarr - TV Series Management
|
||||||
**Purpose**: Automatic TV show downloading and management
|
**Purpose**: Automatic TV show downloading and management
|
||||||
@@ -27,7 +27,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Default Port**: 8787
|
**Default Port**: 8787
|
||||||
**Support**: [github.com/Readarr/Readarr](https://github.com/Readarr/Readarr)
|
**Support**: [github.com/Readarr/Readarr](https://github.com/Readarr/Readarr)
|
||||||
**Documentation**: [wiki.servarr.com/readarr](https://wiki.servarr.com/readarr)
|
**Documentation**: [wiki.servarr.com/readarr](https://wiki.servarr.com/readarr)
|
||||||
**Status**: ⚠️ Project retired, limited support
|
**Status**: Project retired, limited support
|
||||||
|
|
||||||
### Bazarr - Subtitle Management
|
### Bazarr - Subtitle Management
|
||||||
**Purpose**: Automatic subtitle downloading for movies and TV
|
**Purpose**: Automatic subtitle downloading for movies and TV
|
||||||
@@ -53,7 +53,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Support**: [github.com/plexguide/Huntarr.io](https://github.com/plexguide/Huntarr.io)
|
**Support**: [github.com/plexguide/Huntarr.io](https://github.com/plexguide/Huntarr.io)
|
||||||
**Documentation**: Community-driven
|
**Documentation**: Community-driven
|
||||||
|
|
||||||
## ⬇️ Download Clients
|
## Download Clients
|
||||||
|
|
||||||
### qBittorrent - BitTorrent Client
|
### qBittorrent - BitTorrent Client
|
||||||
**Purpose**: Feature-rich BitTorrent client with web interface
|
**Purpose**: Feature-rich BitTorrent client with web interface
|
||||||
@@ -79,7 +79,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Support**: [github.com/sabnzbd/sabnzbd](https://github.com/sabnzbd/sabnzbd)
|
**Support**: [github.com/sabnzbd/sabnzbd](https://github.com/sabnzbd/sabnzbd)
|
||||||
**Documentation**: [sabnzbd.org/wiki](https://sabnzbd.org/wiki)
|
**Documentation**: [sabnzbd.org/wiki](https://sabnzbd.org/wiki)
|
||||||
|
|
||||||
## 🎞️ Media Servers
|
## Media Servers
|
||||||
|
|
||||||
### Jellyfin - Open Source Media Server
|
### Jellyfin - Open Source Media Server
|
||||||
**Purpose**: Free, open-source media server and entertainment hub
|
**Purpose**: Free, open-source media server and entertainment hub
|
||||||
@@ -109,7 +109,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Documentation**: GitHub repository
|
**Documentation**: GitHub repository
|
||||||
**Requirements**: Requires Jellyfin server
|
**Requirements**: Requires Jellyfin server
|
||||||
|
|
||||||
## 🎛️ Request Management
|
## Request Management
|
||||||
|
|
||||||
### Overseerr - Plex Request Management
|
### Overseerr - Plex Request Management
|
||||||
**Purpose**: Media discovery and request management for Plex
|
**Purpose**: Media discovery and request management for Plex
|
||||||
@@ -132,7 +132,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Documentation**: [docs.ombi.app](https://docs.ombi.app)
|
**Documentation**: [docs.ombi.app](https://docs.ombi.app)
|
||||||
**Integration**: Plex, Emby, Jellyfin
|
**Integration**: Plex, Emby, Jellyfin
|
||||||
|
|
||||||
## 🔒 Reverse Proxy & Security
|
## Reverse Proxy & Security
|
||||||
|
|
||||||
### Traefik - Modern Reverse Proxy
|
### Traefik - Modern Reverse Proxy
|
||||||
**Purpose**: Automatic reverse proxy with SSL certificate management
|
**Purpose**: Automatic reverse proxy with SSL certificate management
|
||||||
@@ -153,7 +153,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Default Ports**: 80, 443, 2019 (admin)
|
**Default Ports**: 80, 443, 2019 (admin)
|
||||||
**Support**: [github.com/caddyserver/caddy](https://github.com/caddyserver/caddy)
|
**Support**: [github.com/caddyserver/caddy](https://github.com/caddyserver/caddy)
|
||||||
**Documentation**: [caddyserver.com/docs](https://caddyserver.com/docs)
|
**Documentation**: [caddyserver.com/docs](https://caddyserver.com/docs)
|
||||||
**Note**: ⚠️ **Configuration not included** - Users must provide their own Caddyfile
|
**Note**: **Configuration not included** - Users must provide their own Caddyfile
|
||||||
|
|
||||||
### Authelia - Authentication & Authorization
|
### Authelia - Authentication & Authorization
|
||||||
**Purpose**: Multi-factor authentication and single sign-on
|
**Purpose**: Multi-factor authentication and single sign-on
|
||||||
@@ -162,7 +162,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Documentation**: [authelia.com/integration](https://www.authelia.com/integration)
|
**Documentation**: [authelia.com/integration](https://www.authelia.com/integration)
|
||||||
**Features**: 2FA, LDAP, OAuth2, OIDC
|
**Features**: 2FA, LDAP, OAuth2, OIDC
|
||||||
|
|
||||||
## 📈 Monitoring & Management
|
## Monitoring & Management
|
||||||
|
|
||||||
### Portainer - Container Management
|
### Portainer - Container Management
|
||||||
**Purpose**: Web-based Docker container management interface
|
**Purpose**: Web-based Docker container management interface
|
||||||
@@ -185,7 +185,7 @@ HOPS supports a comprehensive collection of homelab services across multiple cat
|
|||||||
**Documentation**: [containrrr.dev/watchtower](https://containrrr.dev/watchtower)
|
**Documentation**: [containrrr.dev/watchtower](https://containrrr.dev/watchtower)
|
||||||
**Features**: Scheduled updates, notifications, selective updating
|
**Features**: Scheduled updates, notifications, selective updating
|
||||||
|
|
||||||
## 🔧 Service Dependencies
|
## Service Dependencies
|
||||||
|
|
||||||
### Common Dependencies
|
### Common Dependencies
|
||||||
Most services depend on:
|
Most services depend on:
|
||||||
@@ -213,7 +213,7 @@ Most services depend on:
|
|||||||
2. **Uptime Kuma** → Service monitoring
|
2. **Uptime Kuma** → Service monitoring
|
||||||
3. **Watchtower** → Automatic updates
|
3. **Watchtower** → Automatic updates
|
||||||
|
|
||||||
## ⚠️ Important Service Notes
|
## Important Service Notes
|
||||||
|
|
||||||
### Caddy Configuration
|
### Caddy Configuration
|
||||||
HOPS provides the Caddy container but **does not include Caddyfile configuration**. Users must:
|
HOPS provides the Caddy container but **does not include Caddyfile configuration**. Users must:
|
||||||
@@ -234,8 +234,6 @@ radarr.example.com {
|
|||||||
|
|
||||||
### GPU Support
|
### GPU Support
|
||||||
- **Linux**: Intel GPU support via `/dev/dri` passthrough
|
- **Linux**: Intel GPU support via `/dev/dri` passthrough
|
||||||
- **macOS**: No GPU acceleration available
|
|
||||||
- **Windows**: Limited GPU support in WSL2
|
|
||||||
|
|
||||||
### Service Health Checks
|
### Service Health Checks
|
||||||
All web-based services include health checks for:
|
All web-based services include health checks for:
|
||||||
@@ -243,7 +241,7 @@ All web-based services include health checks for:
|
|||||||
- Automatic restart on failure
|
- Automatic restart on failure
|
||||||
- Status monitoring integration
|
- Status monitoring integration
|
||||||
|
|
||||||
## 🆘 Service-Specific Support
|
## Service-Specific Support
|
||||||
|
|
||||||
### When to Contact Service Developers
|
### When to Contact Service Developers
|
||||||
|
|
||||||
@@ -257,7 +255,6 @@ All web-based services include health checks for:
|
|||||||
**Contact HOPS for:**
|
**Contact HOPS for:**
|
||||||
- Docker Compose generation issues
|
- Docker Compose generation issues
|
||||||
- Service deployment problems
|
- Service deployment problems
|
||||||
- Cross-platform compatibility
|
|
||||||
- Installation and automation issues
|
- Installation and automation issues
|
||||||
|
|
||||||
### Getting Service Help
|
### Getting Service Help
|
||||||
@@ -272,11 +269,7 @@ All web-based services include health checks for:
|
|||||||
|
|
||||||
#### Permission Problems
|
#### Permission Problems
|
||||||
```bash
|
```bash
|
||||||
# Fix ownership for Linux
|
|
||||||
sudo chown -R $USER:$USER /opt/appdata/[service-name]
|
sudo chown -R $USER:$USER /opt/appdata/[service-name]
|
||||||
|
|
||||||
# Fix ownership for macOS
|
|
||||||
sudo chown -R $USER:$USER ~/hops/config/[service-name]
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Service Won't Start
|
#### Service Won't Start
|
||||||
@@ -289,10 +282,7 @@ docker compose restart [service-name]
|
|||||||
```
|
```
|
||||||
|
|
||||||
#### Configuration Issues
|
#### Configuration Issues
|
||||||
Most services store configuration in:
|
Service configuration is stored in `/opt/appdata/[service-name]/`.
|
||||||
- **Linux**: `/opt/appdata/[service-name]/`
|
|
||||||
- **macOS**: `~/hops/config/[service-name]/`
|
|
||||||
- **Windows**: `/opt/appdata/[service-name]/` (in WSL2)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,286 @@
|
|||||||
|
# HOPS TODO
|
||||||
|
|
||||||
|
Generated by codebase audit (2026-06-10). Ranked by severity.
|
||||||
|
|
||||||
|
## Decisions (2026-06-10)
|
||||||
|
|
||||||
|
- **Canonical pipeline**: Path A (`hops` -> `install` -> `services`). Path B deleted.
|
||||||
|
- **Deleted**: `setup`, `privileged-setup`, `user-operations`, `services-improved`,
|
||||||
|
`lib/privileges.sh` -- all Path B artifacts, gone.
|
||||||
|
- **Service catalog**: `services` is the single source of truth. Latest tags kept.
|
||||||
|
- **`lib/secrets.sh`**: keep and fix. Goal is to encrypt the `.env` file at rest
|
||||||
|
(passwords/API keys written by `install`). Fix the broken AES-GCM crypto and
|
||||||
|
wire encryption/decryption into the install flow.
|
||||||
|
- **macOS**: future roadmap. Linux is the target for now.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CRITICAL BUGS (breaks primary use cases)
|
||||||
|
|
||||||
|
### B1 -- Infinite recursion in `services` on Linux [CRITICAL] -- RESOLVED
|
||||||
|
- File: `services:25-46`
|
||||||
|
- `get_timezone_mount()` and `get_gpu_devices()` called themselves on the non-Darwin
|
||||||
|
branch. Fixed: both functions now return literal YAML strings directly.
|
||||||
|
|
||||||
|
### B2 -- Brace mismatch in `lib/privileges.sh` [CRITICAL] -- RESOLVED: delete file
|
||||||
|
- File: `lib/privileges.sh:429,612`
|
||||||
|
- Moot -- `lib/privileges.sh` is Path B dead code, scheduled for deletion (see A3).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## HIGH BUGS
|
||||||
|
|
||||||
|
### B3 -- Glob stored as string, directory detection always fails [HIGH] -- RESOLVED
|
||||||
|
- Files: `hops:154-166`, `uninstall:127-147`
|
||||||
|
- Glob removed from array; expanded separately via `compgen -G "/home/*/hops"`.
|
||||||
|
Also fixed `eval echo "~$SUDO_USER"` -> `getent passwd` in `uninstall`.
|
||||||
|
|
||||||
|
### B4 -- Missing service definitions file reference [HIGH] -- RESOLVED
|
||||||
|
- File: `install:916`
|
||||||
|
- Corrected source path from `hops_service_definitions.sh` to `services`.
|
||||||
|
|
||||||
|
### B5 -- `((x++))` aborts script under `set -e` [HIGH] -- RESOLVED
|
||||||
|
- Files: `hops`, `install`
|
||||||
|
- All `((x++))` occurrences replaced with `x=$((x + 1))`.
|
||||||
|
|
||||||
|
### B6 -- `hops` entry point is Linux-only despite macOS library support [HIGH] -- RESOLVED
|
||||||
|
- File: `hops`
|
||||||
|
- Added Linux-only guard at top of script; exits immediately with a clear error on non-Linux.
|
||||||
|
|
||||||
|
### B7 -- Port collisions not detected within a selection [HIGH]
|
||||||
|
- File: `services` (port map)
|
||||||
|
- sabnzbd and traefik dashboard both use 8080; traefik and nginx-proxy-manager
|
||||||
|
both bind 80/443; authelia and transmission both use 9091.
|
||||||
|
- `check_all_ports` only checks host listeners, not intra-selection conflicts,
|
||||||
|
so users can generate an un-startable compose silently.
|
||||||
|
- Fix: add intra-selection conflict check before compose generation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MEDIUM BUGS
|
||||||
|
|
||||||
|
### B8 -- Watchtower assigned a bogus port [MED]
|
||||||
|
- Files: `lib/docker.sh:47`, `services-improved:90`
|
||||||
|
- Watchtower has no web UI. Assigning it port 8080 emits a spurious `ports:`
|
||||||
|
block and broken healthcheck in the generated compose.
|
||||||
|
|
||||||
|
### B9 -- Update backup copies into itself [MED]
|
||||||
|
- File: `hops:586-595`
|
||||||
|
- `update_hops` does `cp -r "$SCRIPT_DIR" "$backup_dir"` where `$backup_dir`
|
||||||
|
is inside `$SCRIPT_DIR`. Results in recursive self-copy including `.git/`.
|
||||||
|
- Fix: create the backup dir outside the script directory.
|
||||||
|
|
||||||
|
### B10 -- `secure_delete` `stat` flag wrong on macOS [MED]
|
||||||
|
- File: `lib/secrets.sh:146`
|
||||||
|
- Uses `stat -c%s` (GNU) which fails on macOS (`stat -f%z`).
|
||||||
|
Manual-overwrite fallback silently no-ops on macOS.
|
||||||
|
|
||||||
|
### B11 -- `jellystat` generated with wrong template in `services-improved` [MED]
|
||||||
|
- File: `services-improved:422`
|
||||||
|
- Routed through the generic media-server template; gets no postgres DB and no
|
||||||
|
JWT_SECRET, so it cannot run. The hand-written `services` version is correct.
|
||||||
|
|
||||||
|
### B12 -- Empty-password detection regex broken [LOW]
|
||||||
|
- File: `lib/security.sh:361-384`
|
||||||
|
- `grep "PASSWORD=\s*$"` without `-E` or `-P` means `\s` is matched literally,
|
||||||
|
not as whitespace. Empty-password detection is dead.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SECURITY
|
||||||
|
|
||||||
|
### S1 -- Broken/unauthenticated encryption [HIGH]
|
||||||
|
- File: `lib/secrets.sh:85,115`
|
||||||
|
- `openssl enc -aes-256-gcm` via the CLI does not handle the GCM auth tag.
|
||||||
|
This is not authenticated encryption and round-trips unreliably.
|
||||||
|
- Fix: use a supported openssl mode or switch to `gpg --symmetric`.
|
||||||
|
|
||||||
|
### S2 -- Passphrases/keys exposed in process list [HIGH]
|
||||||
|
- Files: `lib/secrets.sh:85,115`, `lib/security.sh:140,156,175,204,416,442`
|
||||||
|
- `-pass pass:"$key"` and `--passphrase "$x"` on the command line are visible
|
||||||
|
to any local user via `ps`.
|
||||||
|
- Fix: use `-pass fd:N` or `--passphrase-fd N` with a file descriptor.
|
||||||
|
|
||||||
|
### S3 -- Committed default Authelia credential [HIGH]
|
||||||
|
- File: `services:1148-1157`
|
||||||
|
- `users_database.yml` ships a default admin account with a known password hash
|
||||||
|
(hash of literal "password"). Every Authelia deploy has this credential.
|
||||||
|
- Fix: force password change on first login or generate the hash at deploy time.
|
||||||
|
|
||||||
|
### S4 -- Traefik dashboard exposed with no auth [MED]
|
||||||
|
- File: `services:672-673,684`
|
||||||
|
- `api.insecure=true` exposes the Traefik dashboard on :8080 with no auth.
|
||||||
|
Consider disabling or requiring middleware auth.
|
||||||
|
|
||||||
|
### S5 -- `eval` on environment-derived value [MED]
|
||||||
|
- Files: `install:598,671`, `uninstall:136,462`, `lib/system.sh:306`, others
|
||||||
|
- `eval echo "~$SUDO_USER"` expands an env-sourced value through eval.
|
||||||
|
- Fix: `getent passwd "$SUDO_USER" | cut -d: -f6`
|
||||||
|
|
||||||
|
### S6 -- Predictable temp file paths [MED]
|
||||||
|
- Files: `lib/secrets.sh:16,188,288`, `uninstall:374`
|
||||||
|
- `/tmp/hops_env_$$` etc. in world-writable `/tmp` are symlink-race targets
|
||||||
|
before the `chmod 600` runs.
|
||||||
|
- Fix: use `mktemp` and assign before use.
|
||||||
|
|
||||||
|
### S7 -- Commands built as strings, run unquoted [MED]
|
||||||
|
- File: `install:731-736,755,773-779`
|
||||||
|
- `pull_cmd="sudo -u $SUDO_USER docker compose pull"` run as `$pull_cmd`
|
||||||
|
is fragile with unusual usernames and bypasses quoting.
|
||||||
|
- Fix: use bash arrays.
|
||||||
|
|
||||||
|
### S9 -- Non-idempotent sysctl append [LOW]
|
||||||
|
- File: `privileged-setup:224`
|
||||||
|
- `echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf` appended every run.
|
||||||
|
Accumulates duplicate lines.
|
||||||
|
- Fix: check before appending (`grep -q ... || echo ... >>`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ARCHITECTURE / DESIGN
|
||||||
|
|
||||||
|
### A1 -- Two divergent install pipelines [HIGH] -- RESOLVED
|
||||||
|
- Path A chosen. Delete: `setup`, `privileged-setup`, `user-operations`,
|
||||||
|
`services-improved`.
|
||||||
|
|
||||||
|
### A2 -- Three sources of truth for the service catalog [HIGH] -- DO FIRST
|
||||||
|
- `services`: `get_service_ports()` + inline image strings (CANONICAL)
|
||||||
|
- `services-improved`: scheduled for deletion (Path B)
|
||||||
|
- `lib/docker.sh`: `HOPS_SERVICES` array -- reconcile or remove duplicates
|
||||||
|
- Fix: `lib/docker.sh` service maps must match `services`; remove anything
|
||||||
|
only used by Path B.
|
||||||
|
- Must be done before port collision fix (B7) so there is one authoritative map.
|
||||||
|
|
||||||
|
### A3 -- `lib/privileges.sh` is dead code [MED] -- RESOLVED: delete
|
||||||
|
- Path B artifact. Delete it.
|
||||||
|
|
||||||
|
### A4 -- `lib/secrets.sh` crypto needs fixing and wiring in [MED]
|
||||||
|
- Goal: encrypt the `.env` file at rest after `install` writes it.
|
||||||
|
- Fix broken AES-GCM (use `gpg --symmetric` or a supported openssl mode).
|
||||||
|
- Fix passphrase-on-command-line exposure (S1, S2).
|
||||||
|
- Wire encrypt/decrypt calls into `install` flow.
|
||||||
|
|
||||||
|
### A5 -- `hops` duplicates functions from `lib/common.sh` [HIGH] -- RESOLVED
|
||||||
|
- `log`, `error_exit`, `warning`, `success`, `info`, `validate_timezone`,
|
||||||
|
`validate_password`, `generate_secure_password`, `create_docker_networks`
|
||||||
|
removed from `hops`, `uninstall`, and `install`. Canonical copies kept in
|
||||||
|
`lib/common.sh`, `lib/security.sh`, `lib/validation.sh`, `lib/docker.sh`.
|
||||||
|
- Fixed `lib/docker.sh`, `lib/validation.sh`, `lib/security.sh` to use `LIB_DIR`
|
||||||
|
instead of `SCRIPT_DIR` so sourcing them inside a function doesn't clobber
|
||||||
|
the caller's `SCRIPT_DIR`.
|
||||||
|
- `validate_timezone` updated to warn-and-default instead of error_exit.
|
||||||
|
- `validate_password` updated to handle empty input (return 3).
|
||||||
|
|
||||||
|
### A6 -- Caddy is unreachable via the menu [LOW]
|
||||||
|
- `services` defines `generate_caddy` but the `select_services` menu in
|
||||||
|
`install` never lists caddy as a selectable option.
|
||||||
|
|
||||||
|
### A7 -- Committed dev artifacts [LOW]
|
||||||
|
- `summary7-19.txt` and `discord-header.md` should not be in the repo.
|
||||||
|
Add to `.gitignore` or delete.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## MISSING / INCOMPLETE
|
||||||
|
|
||||||
|
### M1 -- RESOLVED (Path B deleted)
|
||||||
|
|
||||||
|
### M2 -- RESOLVED (Path B deleted)
|
||||||
|
|
||||||
|
### M3 -- RESOLVED (Path B deleted)
|
||||||
|
|
||||||
|
### M4 -- RESOLVED (`lib/privileges.sh` deleted)
|
||||||
|
|
||||||
|
### M6 -- Deprecate Watchtower [LOW]
|
||||||
|
- Watchtower auto-updates running containers without user review, which can
|
||||||
|
silently break working setups. At odds with HOPS managing its own deploys.
|
||||||
|
- Remove from the selectable service list and SERVICES.md.
|
||||||
|
- Users who want auto-updates can add it manually.
|
||||||
|
|
||||||
|
### M8 -- Deprecate Readarr [LOW]
|
||||||
|
- Readarr project has been retired upstream. Remove from the selectable service
|
||||||
|
list, services definition, and SERVICES.md.
|
||||||
|
- Add a note in SERVICES.md under the *arr section if users ask why it's gone.
|
||||||
|
|
||||||
|
### M7 -- Investigate adding Profilarr [LOW]
|
||||||
|
- Profilarr manages and syncs quality profiles across Radarr/Sonarr instances.
|
||||||
|
- Evaluate: image source, default port, dependencies, config requirements.
|
||||||
|
- Determine if it fits the existing *arr service template or needs a custom definition.
|
||||||
|
|
||||||
|
### M5 -- Review Huntarr inclusion -- switch to elfhosted rebuild [MED]
|
||||||
|
- The current `services` definition uses the original plexguide/Huntarr.io image.
|
||||||
|
The project has been rebuilt by elfhosted under a new image/repo.
|
||||||
|
- Review the new elfhosted image name, default port, and any config changes.
|
||||||
|
- Update the service definition in `services` and the entry in SERVICES.md.
|
||||||
|
- Verify the new image is actively maintained before shipping 1.0.0.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## PLATFORM SUPPORT
|
||||||
|
|
||||||
|
### P2 -- `uninstall` is Linux-only [HIGH] -- deferred (Linux-first)
|
||||||
|
- Unconditional `apt-get`, `dpkg`, `systemctl`, `groupdel`, `ufw` with no
|
||||||
|
OS branching. Acceptable for now; revisit when macOS support is scoped.
|
||||||
|
|
||||||
|
### P3 -- RESOLVED (Path B deleted)
|
||||||
|
|
||||||
|
### P4 -- No WSL2 detection [MED]
|
||||||
|
- README claims WSL2 support but there is no WSL2 detection.
|
||||||
|
`systemctl`-based service management fails on WSL distros without systemd.
|
||||||
|
|
||||||
|
### P5 -- Inconsistent port-check tools [MED]
|
||||||
|
- `lib/common.sh` uses `ss`; `install` uses `lsof`. `ss` is absent on macOS.
|
||||||
|
|
||||||
|
### P6 -- Hardcoded render GID for Jellyfin GPU [LOW]
|
||||||
|
- File: `services:435`
|
||||||
|
- `group_add: "109"` is the render GID on a specific distro, wrong on most
|
||||||
|
systems and meaningless on macOS.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## CODE QUALITY
|
||||||
|
|
||||||
|
### Q1 -- Three separate error-handling implementations [HIGH] -- RESOLVED
|
||||||
|
- Covered by A5; all resolved.
|
||||||
|
|
||||||
|
### Q2 -- `set -e` + intentional non-zero returns is a minefield [MED]
|
||||||
|
- `validate_password` returns 1/2/3, `check_port` returns 1 -- these work only
|
||||||
|
because they happen to be in conditionals. Combined with B5 this is fragile.
|
||||||
|
Consider `set -euo pipefail` with explicit `|| true` where non-zero is intended.
|
||||||
|
|
||||||
|
### Q3 -- Debug `echo` statements left in production code [MED] -- DO FIRST
|
||||||
|
- Files: `lib/system.sh:605,823,1043,1046,1084,1089,1149-1156`
|
||||||
|
- `DEBUG:` prefixed echo lines should be removed or gated behind a `$DEBUG` flag.
|
||||||
|
- Clean these before bug fixes so signal isn't buried in debug noise.
|
||||||
|
|
||||||
|
### Q4 -- `services-improved` leaks `set -e` when sourced [LOW]
|
||||||
|
- File: `services-improved` top of file
|
||||||
|
- File sets `set -e` then is sourced by `user-operations` and `privileges`,
|
||||||
|
leaking the option into the caller's shell.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## SUGGESTED ORDER OF ATTACK
|
||||||
|
|
||||||
|
### Cleanup first (do before any bug fixes)
|
||||||
|
1. [DONE] Delete Path B files (A1/A3)
|
||||||
|
2. [DONE] Consolidate duplicate functions into `lib/common.sh` (A5, Q1)
|
||||||
|
3. Reconcile `lib/docker.sh` service maps with `services` (A2) -- one catalog to fix
|
||||||
|
4. Remove debug echo statements from `lib/system.sh` (Q3) -- reduce noise
|
||||||
|
|
||||||
|
### Bug fixes
|
||||||
|
5. [DONE] Fix B1 (infinite recursion in `services`)
|
||||||
|
6. [DONE] Fix B5 (`((x++))` under `set -e`)
|
||||||
|
7. [DONE] Fix B3 (glob directory detection)
|
||||||
|
8. [DONE] Fix B4 (wrong filename in firewall setup)
|
||||||
|
9. Fix B7 (intra-selection port collision detection)
|
||||||
|
|
||||||
|
### Security pass
|
||||||
|
10. S3 (default Authelia cred), S5 (eval on env var), S6 (mktemp),
|
||||||
|
S7 (string-built commands), S2 (passphrase on cmdline)
|
||||||
|
11. Fix and wire in `lib/secrets.sh`: replace broken crypto, hook into install
|
||||||
|
flow to encrypt `.env` at rest (A4/S1/S2)
|
||||||
|
|
||||||
|
### Remaining
|
||||||
|
12. Fix B12 empty-password regex, B8 watchtower port, B9 backup self-copy
|
||||||
|
13. macOS / WSL2 support (B6, P4) -- future roadmap
|
||||||
+56
-275
@@ -1,314 +1,95 @@
|
|||||||
# HOPS Troubleshooting Guide
|
# Troubleshooting
|
||||||
|
|
||||||
This document provides solutions to common issues encountered when installing and running HOPS.
|
## Docker Not Starting
|
||||||
|
|
||||||
## Docker Repository Issues
|
|
||||||
|
|
||||||
### Linux Mint Docker Repository Error
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
```
|
|
||||||
E: The repository 'https://download.docker.com/linux/ubuntu xia Release' does not have a Release file.
|
|
||||||
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
|
|
||||||
```
|
|
||||||
|
|
||||||
**Root Cause:**
|
|
||||||
Linux Mint uses its own codenames (e.g., "xia", "vera", "vanessa") that don't exist in Docker's Ubuntu repositories. Docker only supports Ubuntu codenames like "noble", "jammy", "focal".
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
This issue has been resolved in HOPS v3.3.0+. The system now automatically detects the correct Ubuntu codename for Linux Mint installations using `UBUNTU_CODENAME` from `/etc/os-release`.
|
|
||||||
|
|
||||||
**Manual Fix (if needed):**
|
|
||||||
1. **Clean existing Docker repositories:**
|
|
||||||
```bash
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/docker*
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/*docker*
|
|
||||||
sudo rm -f /usr/share/keyrings/docker*
|
|
||||||
sudo rm -f /etc/apt/keyrings/docker*
|
|
||||||
sudo apt clean
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Verify Ubuntu codename detection:**
|
|
||||||
```bash
|
|
||||||
grep '^UBUNTU_CODENAME=' /etc/os-release
|
|
||||||
# Should return: UBUNTU_CODENAME=noble (for Linux Mint 22.x)
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Update HOPS to latest version:**
|
|
||||||
```bash
|
|
||||||
cd ~/hops
|
|
||||||
git pull
|
|
||||||
sudo ./hops --update
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker Installation Fails
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
- `Package 'docker-ce' has no installation candidate`
|
|
||||||
- `Cannot connect to the Docker daemon at unix:///var/run/docker.sock`
|
|
||||||
|
|
||||||
**Solutions:**
|
|
||||||
|
|
||||||
1. **Check Docker service status:**
|
|
||||||
```bash
|
```bash
|
||||||
sudo systemctl status docker
|
sudo systemctl status docker
|
||||||
sudo systemctl status docker.socket
|
sudo systemctl restart docker
|
||||||
|
journalctl -u docker --since "1 hour ago"
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Fix missing Docker group:**
|
If Docker is missing or broken:
|
||||||
```bash
|
```bash
|
||||||
sudo groupadd docker
|
curl -fsSL https://get.docker.com | sh
|
||||||
sudo usermod -aG docker $USER
|
sudo usermod -aG docker $USER
|
||||||
sudo chown root:docker /var/run/docker.sock
|
|
||||||
```
|
|
||||||
|
|
||||||
3. **Start Docker services:**
|
|
||||||
```bash
|
|
||||||
sudo systemctl start docker.socket
|
|
||||||
sudo systemctl start docker
|
|
||||||
sudo systemctl enable docker
|
|
||||||
```
|
|
||||||
|
|
||||||
4. **Verify Docker installation:**
|
|
||||||
```bash
|
|
||||||
sudo docker --version
|
|
||||||
sudo docker compose version
|
|
||||||
sudo docker info
|
|
||||||
```
|
|
||||||
|
|
||||||
## Directory and Permission Issues
|
|
||||||
|
|
||||||
### Homelab Directory Not Found
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
```
|
|
||||||
ERROR: Homelab directory not found: /root/hops
|
|
||||||
```
|
|
||||||
|
|
||||||
**Root Cause:**
|
|
||||||
When running with `sudo`, the script may look for files in `/root/hops` instead of the user's home directory.
|
|
||||||
|
|
||||||
**Solution:**
|
|
||||||
This issue has been resolved in HOPS v3.3.0+. The system now properly detects the original user's home directory when running with sudo.
|
|
||||||
|
|
||||||
**Manual Workaround:**
|
|
||||||
```bash
|
|
||||||
# Ensure you're in the correct directory
|
|
||||||
cd ~/hops
|
|
||||||
sudo ./hops
|
|
||||||
```
|
|
||||||
|
|
||||||
### Permission Denied Errors
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
- Permission denied accessing Docker socket
|
|
||||||
- Cannot create directories in `/opt/appdata`
|
|
||||||
|
|
||||||
**Solutions:**
|
|
||||||
|
|
||||||
1. **Fix Docker permissions:**
|
|
||||||
```bash
|
|
||||||
sudo usermod -aG docker $USER
|
|
||||||
# Log out and log back in, or run:
|
|
||||||
newgrp docker
|
newgrp docker
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Fix directory permissions:**
|
## Linux Mint: Docker Repository Error
|
||||||
|
|
||||||
|
**Symptom:** `The repository 'https://download.docker.com/linux/ubuntu xia Release' does not have a Release file`
|
||||||
|
|
||||||
|
Linux Mint uses its own codenames that Docker's repo doesn't recognise. HOPS
|
||||||
|
detects the underlying Ubuntu codename via `UBUNTU_CODENAME` in `/etc/os-release`.
|
||||||
|
|
||||||
|
Manual fix:
|
||||||
```bash
|
```bash
|
||||||
|
sudo rm -f /etc/apt/sources.list.d/docker* /etc/apt/keyrings/docker*
|
||||||
|
sudo apt clean
|
||||||
|
grep '^UBUNTU_CODENAME=' /etc/os-release # confirm value is present
|
||||||
|
sudo ./hops # re-run installation
|
||||||
|
```
|
||||||
|
|
||||||
|
## Permission Denied
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Docker socket access
|
||||||
|
sudo usermod -aG docker $USER
|
||||||
|
newgrp docker
|
||||||
|
|
||||||
|
# Directory ownership
|
||||||
sudo chown -R $USER:$USER ~/hops
|
sudo chown -R $USER:$USER ~/hops
|
||||||
sudo chmod -R 755 ~/hops
|
sudo chown -R $USER:$USER /opt/appdata
|
||||||
```
|
```
|
||||||
|
|
||||||
## Service Access Issues
|
## Service Won't Start
|
||||||
|
|
||||||
### Cannot Access Service Web UI
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
- Service shows as running but web UI is not accessible
|
|
||||||
- Connection refused errors
|
|
||||||
|
|
||||||
**Solutions:**
|
|
||||||
|
|
||||||
1. **Check container status:**
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/hops
|
cd ~/hops
|
||||||
sudo docker compose ps
|
docker compose logs [service-name]
|
||||||
sudo docker compose logs [service-name]
|
docker compose ps
|
||||||
|
docker compose restart [service-name]
|
||||||
```
|
```
|
||||||
|
|
||||||
2. **Verify port availability:**
|
## Port Already in Use
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo netstat -tlnp | grep :8989 # For Sonarr
|
sudo lsof -i :[port]
|
||||||
sudo ss -tlnp | grep :8989 # Alternative command
|
sudo ss -tlnp | grep :[port]
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Check firewall settings:**
|
## Cannot Access Service Web UI
|
||||||
```bash
|
|
||||||
sudo ufw status
|
1. Confirm container is running: `docker compose ps`
|
||||||
# If needed, allow ports:
|
2. Check for port conflicts (above)
|
||||||
sudo ufw allow 8989 # Example for Sonarr
|
3. Check firewall: `sudo ufw status`
|
||||||
```
|
4. Allow port if needed: `sudo ufw allow [port]/tcp`
|
||||||
|
|
||||||
4. **Restart services:**
|
## Docker Network Errors
|
||||||
```bash
|
|
||||||
cd ~/hops
|
|
||||||
sudo docker compose restart [service-name]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Network Issues
|
|
||||||
|
|
||||||
### Docker Network Creation Fails
|
|
||||||
|
|
||||||
**Symptoms:**
|
|
||||||
- `Error response from daemon: network with name [network] already exists`
|
|
||||||
- Network connectivity issues between containers
|
|
||||||
|
|
||||||
**Solutions:**
|
|
||||||
|
|
||||||
1. **List existing networks:**
|
|
||||||
```bash
|
|
||||||
sudo docker network ls
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Remove conflicting networks:**
|
|
||||||
```bash
|
```bash
|
||||||
|
# Remove conflicting networks and recreate
|
||||||
sudo docker network rm traefik homelab
|
sudo docker network rm traefik homelab
|
||||||
|
cd ~/hops && docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
3. **Recreate networks:**
|
## Log Locations
|
||||||
```bash
|
|
||||||
cd ~/hops
|
|
||||||
sudo docker compose up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
## System Requirements
|
- **HOPS logs**: `/var/log/hops/hops-main-*.log`
|
||||||
|
- **Service logs**: `docker compose logs [service-name]`
|
||||||
|
- **System logs**: `journalctl -u docker`
|
||||||
|
|
||||||
### Insufficient Resources
|
## Reporting Issues
|
||||||
|
|
||||||
**Symptoms:**
|
Collect this info before reporting:
|
||||||
- Services randomly stopping
|
|
||||||
- Out of memory errors
|
|
||||||
- Slow performance
|
|
||||||
|
|
||||||
**Solutions:**
|
|
||||||
|
|
||||||
1. **Check system resources:**
|
|
||||||
```bash
|
|
||||||
free -h # Memory usage
|
|
||||||
df -h # Disk usage
|
|
||||||
top # CPU and memory usage
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **Minimum requirements:**
|
|
||||||
- RAM: 2GB minimum, 4GB+ recommended
|
|
||||||
- Disk: 10GB minimum, 50GB+ recommended for media
|
|
||||||
- CPU: 2 cores minimum
|
|
||||||
|
|
||||||
3. **Optimize services:**
|
|
||||||
- Disable unnecessary services
|
|
||||||
- Adjust container resource limits
|
|
||||||
- Use external storage for media files
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
### Log Files
|
|
||||||
|
|
||||||
Check HOPS log files for detailed error information:
|
|
||||||
|
|
||||||
**Linux:**
|
|
||||||
```bash
|
|
||||||
sudo tail -f /var/log/hops/hops-main-*.log
|
|
||||||
```
|
|
||||||
|
|
||||||
**macOS:**
|
|
||||||
```bash
|
|
||||||
sudo tail -f /usr/local/var/log/hops/hops-main-*.log
|
|
||||||
```
|
|
||||||
|
|
||||||
### Docker Compose Logs
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~/hops
|
|
||||||
sudo docker compose logs -f [service-name]
|
|
||||||
```
|
|
||||||
|
|
||||||
### System Information
|
|
||||||
|
|
||||||
When reporting issues, include:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# System information
|
|
||||||
lsb_release -a
|
lsb_release -a
|
||||||
uname -a
|
uname -a
|
||||||
docker --version
|
docker --version
|
||||||
docker compose version
|
docker compose version
|
||||||
|
|
||||||
# HOPS version
|
|
||||||
./hops --version
|
./hops --version
|
||||||
|
cd ~/hops && docker compose ps
|
||||||
# Container status
|
|
||||||
cd ~/hops && sudo docker compose ps
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Reporting Issues
|
Report at: [git.canadabot.net/canadabot/hops/issues](https://git.canadabot.net/canadabot/hops/issues)
|
||||||
|
|
||||||
1. Check this troubleshooting guide first
|
|
||||||
2. Search existing [GitHub Issues](https://github.com/skiercm/hops/issues)
|
|
||||||
3. Create a new issue with:
|
|
||||||
- Complete error messages
|
|
||||||
- System information (above commands)
|
|
||||||
- Steps to reproduce
|
|
||||||
- Log file excerpts
|
|
||||||
|
|
||||||
## Recovery Commands
|
|
||||||
|
|
||||||
### Complete Reset
|
|
||||||
|
|
||||||
If HOPS installation is completely broken:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stop all containers
|
|
||||||
cd ~/hops && sudo docker compose down
|
|
||||||
|
|
||||||
# Remove containers and images (CAUTION: This removes all data)
|
|
||||||
sudo docker system prune -a --volumes
|
|
||||||
|
|
||||||
# Clean up repositories
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/docker*
|
|
||||||
sudo apt clean
|
|
||||||
|
|
||||||
# Reinstall HOPS
|
|
||||||
cd ~/hops
|
|
||||||
git pull
|
|
||||||
sudo ./hops
|
|
||||||
```
|
|
||||||
|
|
||||||
### Partial Reset
|
|
||||||
|
|
||||||
To reset only HOPS configuration:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stop services
|
|
||||||
cd ~/hops && sudo docker compose down
|
|
||||||
|
|
||||||
# Remove generated files
|
|
||||||
rm -f ~/hops/docker-compose.yml ~/hops/.env
|
|
||||||
|
|
||||||
# Restart HOPS
|
|
||||||
sudo ./hops
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Version History
|
|
||||||
|
|
||||||
- **v3.3.0+**: Fixed Linux Mint Docker repository detection
|
|
||||||
- **v3.2.0+**: Added macOS support and improved error handling
|
|
||||||
- **v3.1.0+**: Initial multi-platform support
|
|
||||||
|
|
||||||
For the latest updates and fixes, always ensure you're running the latest version:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd ~/hops
|
|
||||||
sudo ./hops --update
|
|
||||||
```
|
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
# HOPS Discord Channel Header
|
|
||||||
|
|
||||||
**HOPS - Homelab Orchestration Provisioning Script** 🏠
|
|
||||||
Cross-platform automation tool for deploying homelab infrastructure using Docker Compose. Menu-driven installation and management of media servers, download clients, monitoring tools, and more. Supports Linux, macOS, and Windows (WSL2).
|
|
||||||
🔗 **GitHub**: https://github.com/skiercm/hops
|
|
||||||
@@ -2,13 +2,17 @@
|
|||||||
|
|
||||||
# HOPS - Homelab Orchestration Provisioning Script
|
# HOPS - Homelab Orchestration Provisioning Script
|
||||||
# Primary Management Script
|
# Primary Management Script
|
||||||
# Version: 3.3.0
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Exit on any error
|
# Exit on any error
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Script version and metadata
|
if [[ "$(uname -s)" != "Linux" ]]; then
|
||||||
readonly SCRIPT_VERSION="3.3.0"
|
echo "ERROR: HOPS requires Linux (Ubuntu 20.04+, Debian 11+, or Linux Mint 20+)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Script metadata (SCRIPT_VERSION defined in lib/common.sh)
|
||||||
readonly SCRIPT_NAME="HOPS"
|
readonly SCRIPT_NAME="HOPS"
|
||||||
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
@@ -17,53 +21,15 @@ readonly INSTALLER_SCRIPT="$SCRIPT_DIR/install"
|
|||||||
readonly UNINSTALLER_SCRIPT="$SCRIPT_DIR/uninstall"
|
readonly UNINSTALLER_SCRIPT="$SCRIPT_DIR/uninstall"
|
||||||
readonly SERVICE_DEFINITIONS="$SCRIPT_DIR/services"
|
readonly SERVICE_DEFINITIONS="$SCRIPT_DIR/services"
|
||||||
|
|
||||||
# Load system utilities
|
# Load shared utilities
|
||||||
|
source "$SCRIPT_DIR/lib/common.sh"
|
||||||
source "$SCRIPT_DIR/lib/system.sh"
|
source "$SCRIPT_DIR/lib/system.sh"
|
||||||
|
|
||||||
# Color codes are defined in lib/common.sh
|
|
||||||
|
|
||||||
# Logging setup (will be set by setup_logging)
|
|
||||||
LOG_DIR=""
|
|
||||||
LOG_FILE=""
|
|
||||||
|
|
||||||
# Initialize logging
|
# Initialize logging
|
||||||
init_logging() {
|
init_logging() {
|
||||||
setup_logging "hops-main"
|
setup_logging "hops-main"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Logging function
|
|
||||||
log() {
|
|
||||||
local message="$1"
|
|
||||||
local timestamp="$(date '+%Y-%m-%d %T')"
|
|
||||||
|
|
||||||
if [[ -w "$LOG_FILE" ]]; then
|
|
||||||
echo "$timestamp - $message" >> "$LOG_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo -e "$message"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Error handling
|
|
||||||
error_exit() {
|
|
||||||
log "${RED}❌ ERROR: $1${NC}"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Warning function
|
|
||||||
warning() {
|
|
||||||
log "${YELLOW}⚠️ WARNING: $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Success function
|
|
||||||
success() {
|
|
||||||
log "${GREEN}✅ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Info function
|
|
||||||
info() {
|
|
||||||
log "${BLUE}ℹ️ $1${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Clear screen and show header
|
# Clear screen and show header
|
||||||
show_header() {
|
show_header() {
|
||||||
clear
|
clear
|
||||||
@@ -151,10 +117,10 @@ get_installation_status() {
|
|||||||
local status="not_installed"
|
local status="not_installed"
|
||||||
local homelab_dirs=(
|
local homelab_dirs=(
|
||||||
"$HOME/hops"
|
"$HOME/hops"
|
||||||
"/home/*/hops"
|
|
||||||
"/opt/hops"
|
"/opt/hops"
|
||||||
"/srv/hops"
|
"/srv/hops"
|
||||||
)
|
)
|
||||||
|
while IFS= read -r d; do homelab_dirs+=("$d"); done < <(compgen -G "/home/*/hops" 2>/dev/null)
|
||||||
|
|
||||||
# Check for existing installation
|
# Check for existing installation
|
||||||
for dir in "${homelab_dirs[@]}"; do
|
for dir in "${homelab_dirs[@]}"; do
|
||||||
@@ -291,14 +257,14 @@ show_service_status() {
|
|||||||
local service_port="${service_info#*:}"
|
local service_port="${service_info#*:}"
|
||||||
|
|
||||||
if docker ps --format "{{.Names}}" | grep -q "^${service_name}$"; then
|
if docker ps --format "{{.Names}}" | grep -q "^${service_name}$"; then
|
||||||
((total_count++))
|
total_count=$((total_count + 1))
|
||||||
local status_symbol="${GREEN}●${NC}"
|
local status_symbol="${GREEN}●${NC}"
|
||||||
local status_text="Running"
|
local status_text="Running"
|
||||||
|
|
||||||
# Check if port is accessible
|
# Check if port is accessible
|
||||||
if curl -sSf --max-time 2 --connect-timeout 1 "http://localhost:${service_port}" >/dev/null 2>&1; then
|
if curl -sSf --max-time 2 --connect-timeout 1 "http://localhost:${service_port}" >/dev/null 2>&1; then
|
||||||
status_text="Running & Accessible"
|
status_text="Running & Accessible"
|
||||||
((running_count++))
|
running_count=$((running_count + 1))
|
||||||
else
|
else
|
||||||
status_text="Running (starting up)"
|
status_text="Running (starting up)"
|
||||||
status_symbol="${YELLOW}●${NC}"
|
status_symbol="${YELLOW}●${NC}"
|
||||||
@@ -306,7 +272,7 @@ show_service_status() {
|
|||||||
|
|
||||||
printf " %s %-20s %s (:%s)\n" "$status_symbol" "$service_name" "$status_text" "$service_port"
|
printf " %s %-20s %s (:%s)\n" "$status_symbol" "$service_name" "$status_text" "$service_port"
|
||||||
elif docker ps -a --format "{{.Names}}" | grep -q "^${service_name}$"; then
|
elif docker ps -a --format "{{.Names}}" | grep -q "^${service_name}$"; then
|
||||||
((total_count++))
|
total_count=$((total_count + 1))
|
||||||
printf " %s %-20s %s\n" "${RED}●${NC}" "$service_name" "Stopped"
|
printf " %s %-20s %s\n" "${RED}●${NC}" "$service_name" "Stopped"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
@@ -471,7 +437,7 @@ show_access_info() {
|
|||||||
if docker ps --format "{{.Names}}" | grep -qi "${service_name,,}"; then
|
if docker ps --format "{{.Names}}" | grep -qi "${service_name,,}"; then
|
||||||
local url="http://${local_ip}:${service_port}${service_path}"
|
local url="http://${local_ip}:${service_port}${service_path}"
|
||||||
printf " ${GREEN}●${NC} %-15s %s\n" "$service_name" "$url"
|
printf " ${GREEN}●${NC} %-15s %s\n" "$service_name" "$url"
|
||||||
((active_services++))
|
active_services=$((active_services + 1))
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
@@ -518,7 +484,7 @@ show_logs() {
|
|||||||
local date=$(stat -c %y "$log_file" | cut -d' ' -f1)
|
local date=$(stat -c %y "$log_file" | cut -d' ' -f1)
|
||||||
|
|
||||||
printf " %d) %-40s (%s, %s)\n" "$count" "$basename_log" "$size" "$date"
|
printf " %d) %-40s (%s, %s)\n" "$count" "$basename_log" "$size" "$date"
|
||||||
((count++))
|
count=$((count + 1))
|
||||||
done
|
done
|
||||||
|
|
||||||
echo -e "\n${WHITE}Select a log file to view [1-${#log_files[@]}] or 0 to go back: ${NC}"
|
echo -e "\n${WHITE}Select a log file to view [1-${#log_files[@]}] or 0 to go back: ${NC}"
|
||||||
@@ -627,7 +593,7 @@ update_hops() {
|
|||||||
|
|
||||||
# Source the updated script to get new version
|
# Source the updated script to get new version
|
||||||
if [[ -f "$SCRIPT_DIR/hops" ]]; then
|
if [[ -f "$SCRIPT_DIR/hops" ]]; then
|
||||||
local new_version=$(grep '^readonly SCRIPT_VERSION=' "$SCRIPT_DIR/hops" | cut -d'"' -f2)
|
local new_version=$(grep '^readonly SCRIPT_VERSION=' "$SCRIPT_DIR/lib/common.sh" | cut -d'"' -f2)
|
||||||
success "Updated to version $new_version"
|
success "Updated to version $new_version"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ install_hops() {
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Script version for update tracking
|
# Script version for update tracking
|
||||||
local SCRIPT_VERSION="3.2.0"
|
|
||||||
local SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
local SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
# Load system utilities
|
# Load shared utilities
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
source "$SCRIPT_DIR/lib/common.sh"
|
||||||
source "$SCRIPT_DIR/lib/system.sh"
|
source "$SCRIPT_DIR/lib/system.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/validation.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/security.sh"
|
||||||
|
source "$SCRIPT_DIR/lib/docker.sh"
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# LOGGING SETUP
|
# LOGGING SETUP
|
||||||
@@ -174,8 +176,7 @@ EOF
|
|||||||
if [[ "$keep_tz" =~ ^[Nn]$ ]]; then
|
if [[ "$keep_tz" =~ ^[Nn]$ ]]; then
|
||||||
echo -e "Enter timezone (e.g., America/New_York, Europe/London): "
|
echo -e "Enter timezone (e.g., America/New_York, Europe/London): "
|
||||||
read -r user_timezone
|
read -r user_timezone
|
||||||
validate_timezone "$user_timezone"
|
TIMEZONE=$(validate_timezone "$user_timezone")
|
||||||
TIMEZONE="$user_timezone"
|
|
||||||
else
|
else
|
||||||
TIMEZONE=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "America/New_York")
|
TIMEZONE=$(timedatectl show --property=Timezone --value 2>/dev/null || echo "America/New_York")
|
||||||
fi
|
fi
|
||||||
@@ -210,42 +211,6 @@ EOF
|
|||||||
log " AppData: $APPDATA_DIR"
|
log " AppData: $APPDATA_DIR"
|
||||||
}
|
}
|
||||||
|
|
||||||
# --------------------------------------------
|
|
||||||
# VALIDATION FUNCTIONS
|
|
||||||
# --------------------------------------------
|
|
||||||
validate_timezone() {
|
|
||||||
if ! timedatectl list-timezones | grep -qx "$1" 2>/dev/null; then
|
|
||||||
log "⚠️ Timezone '$1' invalid, defaulting to 'America/New_York'"
|
|
||||||
TIMEZONE="America/New_York"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
validate_password() {
|
|
||||||
local password="$1"
|
|
||||||
local min_length="${2:-12}"
|
|
||||||
|
|
||||||
if [[ -z "$password" ]]; then
|
|
||||||
echo -e "\n🔐 Password must meet these requirements:"
|
|
||||||
echo " • Minimum $min_length characters"
|
|
||||||
echo " • At least one uppercase letter"
|
|
||||||
echo " • At least one lowercase letter"
|
|
||||||
echo " • At least one number"
|
|
||||||
echo " • At least one special character"
|
|
||||||
return 3
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ${#password} -lt $min_length ]]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ ! "$password" =~ [A-Z] ]] || [[ ! "$password" =~ [a-z] ]] || \
|
|
||||||
[[ ! "$password" =~ [0-9] ]] || [[ ! "$password" =~ [^A-Za-z0-9] ]]; then
|
|
||||||
return 2
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
check_port() {
|
check_port() {
|
||||||
local PORT=$1
|
local PORT=$1
|
||||||
local SERVICE=$2
|
local SERVICE=$2
|
||||||
@@ -315,42 +280,6 @@ EOF
|
|||||||
error_exit "No Docker Compose detected. Please install Docker first."
|
error_exit "No Docker Compose detected. Please install Docker first."
|
||||||
}
|
}
|
||||||
|
|
||||||
# --------------------------------------------
|
|
||||||
# IMPROVED PASSWORD GENERATION
|
|
||||||
# --------------------------------------------
|
|
||||||
generate_secure_password() {
|
|
||||||
local length="${1:-16}"
|
|
||||||
local max_attempts=5
|
|
||||||
local attempt=1
|
|
||||||
|
|
||||||
while [[ $attempt -le $max_attempts ]]; do
|
|
||||||
# Generate password with mixed case, numbers, and symbols
|
|
||||||
local password=$(openssl rand -base64 32 | tr -d "=+/" | cut -c1-${length})
|
|
||||||
|
|
||||||
# Ensure it meets complexity requirements
|
|
||||||
if validate_password "$password" "$length"; then
|
|
||||||
echo "$password"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
((attempt++))
|
|
||||||
done
|
|
||||||
|
|
||||||
# Fallback: construct a guaranteed compliant password
|
|
||||||
local upper=$(generate_chars 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 2)
|
|
||||||
local lower=$(generate_chars 'abcdefghijklmnopqrstuvwxyz' 4)
|
|
||||||
local digits=$(generate_chars '0123456789' 2)
|
|
||||||
local symbols=$(generate_chars '!@#$%^&*' 2)
|
|
||||||
local remaining_length=$((length - 10))
|
|
||||||
|
|
||||||
if [[ $remaining_length -gt 0 ]]; then
|
|
||||||
local remaining=$(generate_chars 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' $remaining_length)
|
|
||||||
shuffle_string "${upper}${lower}${digits}${symbols}${remaining}"
|
|
||||||
else
|
|
||||||
shuffle_string "${upper}${lower}${digits}${symbols}"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# ENVIRONMENT FILE GENERATION
|
# ENVIRONMENT FILE GENERATION
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
@@ -642,22 +571,6 @@ EOF
|
|||||||
create_docker_networks
|
create_docker_networks
|
||||||
}
|
}
|
||||||
|
|
||||||
# --------------------------------------------
|
|
||||||
# NETWORK CREATION
|
|
||||||
# --------------------------------------------
|
|
||||||
create_docker_networks() {
|
|
||||||
log "🌐 Creating Docker networks..."
|
|
||||||
|
|
||||||
# Create traefik network if it doesn't exist
|
|
||||||
if ! docker network ls --format "{{.Name}}" | grep -q "^traefik$"; then
|
|
||||||
if docker network create traefik 2>/dev/null; then
|
|
||||||
log "✅ Created traefik network"
|
|
||||||
else
|
|
||||||
log "⚠️ Could not create traefik network (may already exist)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# ENHANCED DEPLOYMENT WITH ROLLBACK
|
# ENHANCED DEPLOYMENT WITH ROLLBACK
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
@@ -913,8 +826,8 @@ EOF
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Allow service ports based on selection
|
# Allow service ports based on selection
|
||||||
if [[ -f "$SCRIPT_DIR/hops_service_definitions.sh" ]]; then
|
if [[ -f "$SCRIPT_DIR/services" ]]; then
|
||||||
source "$SCRIPT_DIR/hops_service_definitions.sh"
|
source "$SCRIPT_DIR/services"
|
||||||
|
|
||||||
for svc in "${SERVICES[@]}"; do
|
for svc in "${SERVICES[@]}"; do
|
||||||
local ports=$(get_service_ports "$svc")
|
local ports=$(get_service_ports "$svc")
|
||||||
@@ -1015,7 +928,7 @@ EOF
|
|||||||
local main_port=$(echo $ports | cut -d' ' -f1)
|
local main_port=$(echo $ports | cut -d' ' -f1)
|
||||||
if [[ -n "$main_port" ]]; then
|
if [[ -n "$main_port" ]]; then
|
||||||
echo " • $svc: http://$(get_primary_ip):$main_port"
|
echo " • $svc: http://$(get_primary_ip):$main_port"
|
||||||
((service_count++))
|
service_count=$((service_count + 1))
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
+3
-2
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
# HOPS - Common Utility Functions
|
# HOPS - Common Utility Functions
|
||||||
# Shared functions for logging, error handling, and UI
|
# Shared functions for logging, error handling, and UI
|
||||||
# Version: 3.2.0
|
|
||||||
|
|
||||||
# Prevent multiple sourcing
|
# Prevent multiple sourcing
|
||||||
if [[ -n "${HOPS_COMMON_LOADED:-}" ]]; then
|
if [[ -n "${HOPS_COMMON_LOADED:-}" ]]; then
|
||||||
@@ -10,6 +9,8 @@ if [[ -n "${HOPS_COMMON_LOADED:-}" ]]; then
|
|||||||
fi
|
fi
|
||||||
readonly HOPS_COMMON_LOADED=1
|
readonly HOPS_COMMON_LOADED=1
|
||||||
|
|
||||||
|
readonly SCRIPT_VERSION="1.0.1"
|
||||||
|
|
||||||
# Color codes for output
|
# Color codes for output
|
||||||
readonly RED='\033[0;31m'
|
readonly RED='\033[0;31m'
|
||||||
readonly GREEN='\033[0;32m'
|
readonly GREEN='\033[0;32m'
|
||||||
@@ -101,7 +102,7 @@ show_hops_header() {
|
|||||||
local subtitle="$2"
|
local subtitle="$2"
|
||||||
|
|
||||||
if [[ -z "$version" ]]; then
|
if [[ -z "$version" ]]; then
|
||||||
version="3.2.0"
|
version="1.0.0"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
clear
|
clear
|
||||||
|
|||||||
+3
-3
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
# HOPS - Docker Service Management
|
# HOPS - Docker Service Management
|
||||||
# Functions for Docker service management and monitoring
|
# Functions for Docker service management and monitoring
|
||||||
# Version: 3.1.0-beta
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Source common functions
|
# Source common functions
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/common.sh"
|
source "$LIB_DIR/common.sh"
|
||||||
|
|
||||||
# Service definitions with pinned versions
|
# Service definitions with pinned versions
|
||||||
declare -A HOPS_SERVICES=(
|
declare -A HOPS_SERVICES=(
|
||||||
|
|||||||
@@ -1,775 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS - Privilege Management System
|
|
||||||
# Split operations into privileged and non-privileged components
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/common.sh"
|
|
||||||
|
|
||||||
# Operations that require root privileges
|
|
||||||
PRIVILEGED_OPERATIONS=(
|
|
||||||
"install_docker"
|
|
||||||
"configure_firewall"
|
|
||||||
"create_system_directories"
|
|
||||||
"install_packages"
|
|
||||||
"configure_systemd"
|
|
||||||
"setup_secrets_directory"
|
|
||||||
"modify_system_files"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Operations that can run as regular user
|
|
||||||
NON_PRIVILEGED_OPERATIONS=(
|
|
||||||
"generate_docker_compose"
|
|
||||||
"pull_docker_images"
|
|
||||||
"start_containers"
|
|
||||||
"stop_containers"
|
|
||||||
"view_logs"
|
|
||||||
"check_service_status"
|
|
||||||
"validate_configuration"
|
|
||||||
"backup_user_data"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Check if operation requires privileges
|
|
||||||
requires_privileges() {
|
|
||||||
local operation="$1"
|
|
||||||
|
|
||||||
for priv_op in "${PRIVILEGED_OPERATIONS[@]}"; do
|
|
||||||
if [[ "$operation" == "$priv_op" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if operation can run as regular user
|
|
||||||
can_run_as_user() {
|
|
||||||
local operation="$1"
|
|
||||||
|
|
||||||
for user_op in "${NON_PRIVILEGED_OPERATIONS[@]}"; do
|
|
||||||
if [[ "$operation" == "$user_op" ]]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get current user information
|
|
||||||
get_current_user_info() {
|
|
||||||
local -A user_info
|
|
||||||
|
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
|
||||||
user_info["username"]="$SUDO_USER"
|
|
||||||
user_info["uid"]=$(id -u "$SUDO_USER")
|
|
||||||
user_info["gid"]=$(id -g "$SUDO_USER")
|
|
||||||
user_info["home"]=$(eval echo "~$SUDO_USER")
|
|
||||||
user_info["is_sudo"]="true"
|
|
||||||
else
|
|
||||||
user_info["username"]="$USER"
|
|
||||||
user_info["uid"]=$(id -u)
|
|
||||||
user_info["gid"]=$(id -g)
|
|
||||||
user_info["home"]="$HOME"
|
|
||||||
user_info["is_sudo"]="false"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Return as key=value pairs
|
|
||||||
for key in "${!user_info[@]}"; do
|
|
||||||
echo "${key}=${user_info[$key]}"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Drop privileges to regular user
|
|
||||||
drop_privileges() {
|
|
||||||
local command="$1"
|
|
||||||
shift
|
|
||||||
local args=("$@")
|
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
debug "Already running as non-root user"
|
|
||||||
exec "$command" "${args[@]}"
|
|
||||||
return $?
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$SUDO_USER" ]]; then
|
|
||||||
error_exit "Cannot drop privileges: SUDO_USER not set"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local user_info
|
|
||||||
user_info=$(get_current_user_info)
|
|
||||||
|
|
||||||
local uid=$(echo "$user_info" | grep "uid=" | cut -d= -f2)
|
|
||||||
local gid=$(echo "$user_info" | grep "gid=" | cut -d= -f2)
|
|
||||||
local home=$(echo "$user_info" | grep "home=" | cut -d= -f2)
|
|
||||||
|
|
||||||
debug "Dropping privileges to user: $SUDO_USER (uid=$uid, gid=$gid)"
|
|
||||||
|
|
||||||
# Set environment variables for the user
|
|
||||||
local env_vars=(
|
|
||||||
"HOME=$home"
|
|
||||||
"USER=$SUDO_USER"
|
|
||||||
"LOGNAME=$SUDO_USER"
|
|
||||||
"PATH=/usr/local/bin:/usr/bin:/bin"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Execute command as user
|
|
||||||
sudo -u "$SUDO_USER" env "${env_vars[@]}" "$command" "${args[@]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run operation with appropriate privileges
|
|
||||||
run_with_privileges() {
|
|
||||||
local operation="$1"
|
|
||||||
local command="$2"
|
|
||||||
shift 2
|
|
||||||
local args=("$@")
|
|
||||||
|
|
||||||
if requires_privileges "$operation"; then
|
|
||||||
debug "Operation '$operation' requires root privileges"
|
|
||||||
|
|
||||||
if [[ $EUID -ne 0 ]]; then
|
|
||||||
error_exit "Operation '$operation' requires root privileges. Please run with sudo."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run as root
|
|
||||||
exec "$command" "${args[@]}"
|
|
||||||
elif can_run_as_user "$operation"; then
|
|
||||||
debug "Operation '$operation' can run as regular user"
|
|
||||||
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
|
||||||
# Drop privileges
|
|
||||||
drop_privileges "$command" "${args[@]}"
|
|
||||||
else
|
|
||||||
# Run as current user
|
|
||||||
exec "$command" "${args[@]}"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error_exit "Unknown operation: $operation"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create privileged setup script
|
|
||||||
create_privileged_setup() {
|
|
||||||
local setup_script="$1"
|
|
||||||
|
|
||||||
cat > "$setup_script" << 'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS Privileged Setup Script
|
|
||||||
# This script handles operations that require root privileges
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/system.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/security.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "privileged-setup"
|
|
||||||
|
|
||||||
# Check root privileges
|
|
||||||
check_root
|
|
||||||
|
|
||||||
# Install Docker if not present
|
|
||||||
install_docker() {
|
|
||||||
info "🐳 Installing Docker..."
|
|
||||||
|
|
||||||
if command_exists docker; then
|
|
||||||
success "Docker already installed"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update package index
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
# Install prerequisites
|
|
||||||
apt-get install -y \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
gnupg \
|
|
||||||
lsb-release
|
|
||||||
|
|
||||||
# Add Docker GPG key
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|
||||||
|
|
||||||
# Add Docker repository with proper Ubuntu codename detection for Linux Mint
|
|
||||||
local ubuntu_codename
|
|
||||||
if [[ "$(lsb_release -is)" == "Linuxmint" ]]; then
|
|
||||||
# Linux Mint provides UBUNTU_CODENAME in /etc/os-release
|
|
||||||
if [[ -f /etc/os-release ]]; then
|
|
||||||
ubuntu_codename=$(grep '^UBUNTU_CODENAME=' /etc/os-release | cut -d= -f2)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fallback to version mapping if UBUNTU_CODENAME not found
|
|
||||||
if [[ -z "$ubuntu_codename" ]]; then
|
|
||||||
case "$(lsb_release -rs)" in
|
|
||||||
"22"|"22.1"|"22.2"|"22.3")
|
|
||||||
ubuntu_codename="noble" # Ubuntu 24.04
|
|
||||||
;;
|
|
||||||
"21"|"21.1"|"21.2"|"21.3")
|
|
||||||
ubuntu_codename="jammy" # Ubuntu 22.04
|
|
||||||
;;
|
|
||||||
"20"|"20.1"|"20.2"|"20.3")
|
|
||||||
ubuntu_codename="focal" # Ubuntu 20.04
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
ubuntu_codename="noble" # Default to latest LTS
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
ubuntu_codename=$(lsb_release -cs)
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $ubuntu_codename stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
||||||
|
|
||||||
# Update package index with Docker packages
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
# Install Docker
|
|
||||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
||||||
|
|
||||||
# Start and enable Docker service
|
|
||||||
systemctl start docker
|
|
||||||
systemctl enable docker
|
|
||||||
|
|
||||||
success "Docker installed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure firewall
|
|
||||||
configure_firewall() {
|
|
||||||
info "🔥 Configuring firewall..."
|
|
||||||
|
|
||||||
# Install UFW if not present
|
|
||||||
if ! command_exists ufw; then
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y ufw
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Reset firewall to defaults
|
|
||||||
ufw --force reset
|
|
||||||
|
|
||||||
# Set default policies
|
|
||||||
ufw default deny incoming
|
|
||||||
ufw default allow outgoing
|
|
||||||
|
|
||||||
# Allow SSH (prevent lockout)
|
|
||||||
ufw allow ssh
|
|
||||||
|
|
||||||
# Allow HTTP and HTTPS
|
|
||||||
ufw allow 80/tcp
|
|
||||||
ufw allow 443/tcp
|
|
||||||
|
|
||||||
# Enable firewall
|
|
||||||
ufw --force enable
|
|
||||||
|
|
||||||
success "Firewall configured successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create system directories
|
|
||||||
create_system_directories() {
|
|
||||||
info "📁 Creating system directories..."
|
|
||||||
|
|
||||||
local directories=(
|
|
||||||
"/opt/appdata"
|
|
||||||
"/mnt/media"
|
|
||||||
"/mnt/media/movies"
|
|
||||||
"/mnt/media/tv"
|
|
||||||
"/mnt/media/music"
|
|
||||||
"/mnt/media/downloads"
|
|
||||||
"/var/log/hops"
|
|
||||||
)
|
|
||||||
|
|
||||||
for dir in "${directories[@]}"; do
|
|
||||||
if mkdir -p "$dir"; then
|
|
||||||
success "Created directory: $dir"
|
|
||||||
else
|
|
||||||
error_exit "Failed to create directory: $dir"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Set ownership to the user who ran sudo
|
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
|
||||||
local user_info
|
|
||||||
user_info=$(get_user_info)
|
|
||||||
|
|
||||||
local uid=$(echo "$user_info" | grep "uid=" | cut -d= -f2)
|
|
||||||
local gid=$(echo "$user_info" | grep "gid=" | cut -d= -f2)
|
|
||||||
|
|
||||||
chown -R "$uid:$gid" /opt/appdata /mnt/media
|
|
||||||
success "Set ownership of directories to $SUDO_USER"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add user to docker group
|
|
||||||
add_user_to_docker_group() {
|
|
||||||
if [[ -z "$SUDO_USER" ]]; then
|
|
||||||
warning "No SUDO_USER set, skipping docker group addition"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "👥 Adding user to docker group..."
|
|
||||||
|
|
||||||
if usermod -aG docker "$SUDO_USER"; then
|
|
||||||
success "User $SUDO_USER added to docker group"
|
|
||||||
warning "User must log out and back in for group changes to take effect"
|
|
||||||
else
|
|
||||||
error_exit "Failed to add user to docker group"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install required packages
|
|
||||||
install_packages() {
|
|
||||||
info "📦 Installing required packages..."
|
|
||||||
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
local packages=(
|
|
||||||
"curl"
|
|
||||||
"wget"
|
|
||||||
"git"
|
|
||||||
"jq"
|
|
||||||
"htop"
|
|
||||||
"tree"
|
|
||||||
"unzip"
|
|
||||||
"gnupg"
|
|
||||||
"software-properties-common"
|
|
||||||
"apt-transport-https"
|
|
||||||
"ca-certificates"
|
|
||||||
"lsb-release"
|
|
||||||
)
|
|
||||||
|
|
||||||
for package in "${packages[@]}"; do
|
|
||||||
if apt-get install -y "$package"; then
|
|
||||||
success "Installed package: $package"
|
|
||||||
else
|
|
||||||
warning "Failed to install package: $package"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup secrets directory
|
|
||||||
setup_secrets_directory() {
|
|
||||||
info "🔐 Setting up secrets directory..."
|
|
||||||
|
|
||||||
local secrets_dir="/etc/hops/secrets"
|
|
||||||
|
|
||||||
if mkdir -p "$secrets_dir"; then
|
|
||||||
chmod 700 "$secrets_dir"
|
|
||||||
success "Secrets directory created: $secrets_dir"
|
|
||||||
else
|
|
||||||
error_exit "Failed to create secrets directory"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure system settings
|
|
||||||
configure_system() {
|
|
||||||
info "⚙️ Configuring system settings..."
|
|
||||||
|
|
||||||
# Set timezone if not already set
|
|
||||||
if [[ -n "$TZ" ]]; then
|
|
||||||
timedatectl set-timezone "$TZ" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Enable IP forwarding for Docker
|
|
||||||
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
|
|
||||||
sysctl -p /etc/sysctl.conf
|
|
||||||
|
|
||||||
success "System configuration completed"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main privileged setup
|
|
||||||
main() {
|
|
||||||
info "🚀 Starting privileged setup..."
|
|
||||||
|
|
||||||
# System checks
|
|
||||||
detect_os
|
|
||||||
check_system_requirements
|
|
||||||
|
|
||||||
# Install packages
|
|
||||||
install_packages
|
|
||||||
|
|
||||||
# Install Docker
|
|
||||||
install_docker
|
|
||||||
|
|
||||||
# Configure firewall
|
|
||||||
configure_firewall
|
|
||||||
|
|
||||||
# Create directories
|
|
||||||
create_system_directories
|
|
||||||
|
|
||||||
# Add user to docker group
|
|
||||||
add_user_to_docker_group
|
|
||||||
|
|
||||||
# Setup secrets
|
|
||||||
setup_secrets_directory
|
|
||||||
|
|
||||||
# Configure system
|
|
||||||
configure_system
|
|
||||||
|
|
||||||
success "Privileged setup completed successfully"
|
|
||||||
success "Please log out and back in for group changes to take effect"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run if executed directly
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$setup_script"
|
|
||||||
success "Privileged setup script created: $setup_script"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create non-privileged user script
|
|
||||||
create_user_script() {
|
|
||||||
local user_script="$1"
|
|
||||||
|
|
||||||
cat > "$user_script" << 'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS User Script
|
|
||||||
# This script handles operations that can run as regular user
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/docker.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/validation.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "user-operations"
|
|
||||||
|
|
||||||
# Check if user is in docker group
|
|
||||||
check_docker_access() {
|
|
||||||
if ! groups "\$USER" | grep -q docker; then
|
|
||||||
error_exit "User not in docker group. Please run the privileged setup first and log out/in."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! docker info >/dev/null 2>&1; then
|
|
||||||
error_exit "Cannot access Docker daemon. Please ensure Docker is running."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate Docker Compose configuration
|
|
||||||
generate_docker_compose() {
|
|
||||||
local services=("$@")
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
info "📝 Generating Docker Compose configuration..."
|
|
||||||
|
|
||||||
# Create homelab directory
|
|
||||||
mkdir -p "$HOME/hops"
|
|
||||||
|
|
||||||
# Generate compose file header
|
|
||||||
cat > "$compose_file" << EOF
|
|
||||||
services:
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Generate service definitions
|
|
||||||
for service in "${services[@]}"; do
|
|
||||||
if "$SCRIPT_DIR/services-improved" generate "$service" >> "$compose_file"; then
|
|
||||||
success "Added service: $service"
|
|
||||||
else
|
|
||||||
error_exit "Failed to generate service definition for: $service"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Add networks section
|
|
||||||
cat >> "$compose_file" << EOF
|
|
||||||
|
|
||||||
networks:
|
|
||||||
homelab:
|
|
||||||
driver: bridge
|
|
||||||
traefik:
|
|
||||||
driver: bridge
|
|
||||||
database:
|
|
||||||
driver: bridge
|
|
||||||
EOF
|
|
||||||
|
|
||||||
success "Docker Compose configuration generated: $compose_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Deploy services
|
|
||||||
deploy_services() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "🚀 Deploying services..."
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
|
|
||||||
# Pull images
|
|
||||||
if docker compose pull; then
|
|
||||||
success "Docker images pulled successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to pull Docker images"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start services
|
|
||||||
if docker compose up -d; then
|
|
||||||
success "Services deployed successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to deploy services"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Stop services
|
|
||||||
stop_services() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "🛑 Stopping services..."
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
|
|
||||||
if docker compose down; then
|
|
||||||
success "Services stopped successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to stop services"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Show service status
|
|
||||||
show_service_status() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "📊 Service status:"
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
docker compose ps
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main user operations
|
|
||||||
main() {
|
|
||||||
local action="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
# Check Docker access
|
|
||||||
check_docker_access
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
"generate")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 generate <service1> [service2] ..."
|
|
||||||
fi
|
|
||||||
generate_docker_compose "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"deploy")
|
|
||||||
deploy_services
|
|
||||||
;;
|
|
||||||
|
|
||||||
"stop")
|
|
||||||
stop_services
|
|
||||||
;;
|
|
||||||
|
|
||||||
"status")
|
|
||||||
show_service_status
|
|
||||||
;;
|
|
||||||
|
|
||||||
"logs")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 logs <service_name>"
|
|
||||||
fi
|
|
||||||
cd "$HOME/hops"
|
|
||||||
docker compose logs -f "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
error_exit "Unknown action: $action"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run if executed directly
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$user_script"
|
|
||||||
success "User script created: $user_script"
|
|
||||||
|
|
||||||
# Create installation wrapper
|
|
||||||
create_installation_wrapper() {
|
|
||||||
local wrapper_script="$1"
|
|
||||||
|
|
||||||
cat > "$wrapper_script" << 'EOF'
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS Installation Wrapper
|
|
||||||
# Orchestrates privileged and non-privileged installation steps
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "installation-wrapper"
|
|
||||||
|
|
||||||
# Show header
|
|
||||||
show_hops_header "3.1.0" "Installation Wrapper"
|
|
||||||
|
|
||||||
# Check if we're running as root
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
|
||||||
if [[ -z "$SUDO_USER" ]]; then
|
|
||||||
error_exit "Please run with sudo, not as root directly"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error_exit "This script must be run with sudo"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Phase 1: Privileged setup
|
|
||||||
info "📋 Phase 1: Privileged setup (requires root)"
|
|
||||||
if "$SCRIPT_DIR/privileged-setup"; then
|
|
||||||
success "Privileged setup completed"
|
|
||||||
else
|
|
||||||
error_exit "Privileged setup failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Phase 2: User setup
|
|
||||||
info "📋 Phase 2: User setup (running as $SUDO_USER)"
|
|
||||||
|
|
||||||
# Drop privileges and run user setup
|
|
||||||
sudo -u "$SUDO_USER" bash << 'USERSCRIPT'
|
|
||||||
cd "$HOME"
|
|
||||||
echo "Running as user: $(whoami)"
|
|
||||||
|
|
||||||
# Interactive service selection
|
|
||||||
echo "Select services to install:"
|
|
||||||
echo "1) Media Server Stack (Jellyfin, Sonarr, Radarr, Prowlarr)"
|
|
||||||
echo "2) Download Client Stack (qBittorrent, Transmission)"
|
|
||||||
echo "3) Monitoring Stack (Portainer, Uptime Kuma)"
|
|
||||||
echo "4) Custom selection"
|
|
||||||
|
|
||||||
read -p "Enter your choice (1-4): " choice
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1)
|
|
||||||
services=("jellyfin" "sonarr" "radarr" "prowlarr")
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
services=("qbittorrent" "transmission")
|
|
||||||
;;
|
|
||||||
3)
|
|
||||||
services=("portainer" "uptime-kuma")
|
|
||||||
;;
|
|
||||||
4)
|
|
||||||
echo "Available services:"
|
|
||||||
"$SCRIPT_DIR/services-improved" list
|
|
||||||
read -p "Enter service names (space-separated): " -a services
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Invalid choice"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Generate and deploy
|
|
||||||
if "$SCRIPT_DIR/user-operations" generate "${services[@]}"; then
|
|
||||||
echo "Configuration generated successfully"
|
|
||||||
|
|
||||||
if "$SCRIPT_DIR/user-operations" deploy; then
|
|
||||||
echo "Services deployed successfully"
|
|
||||||
else
|
|
||||||
echo "Deployment failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Configuration generation failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
USERSCRIPT
|
|
||||||
|
|
||||||
success "Installation completed successfully"
|
|
||||||
success "Services are now running. Check status with: ./user-operations status"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
chmod +x "$wrapper_script"
|
|
||||||
success "Installation wrapper created: $wrapper_script"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main function
|
|
||||||
main() {
|
|
||||||
local action="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
"create-setup")
|
|
||||||
create_privileged_setup "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"create-user")
|
|
||||||
create_user_script "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"create-wrapper")
|
|
||||||
create_installation_wrapper "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"create-all")
|
|
||||||
create_privileged_setup "privileged-setup"
|
|
||||||
create_user_script "user-operations"
|
|
||||||
create_installation_wrapper "setup"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"run")
|
|
||||||
local operation="$1"
|
|
||||||
local command="$2"
|
|
||||||
shift 2
|
|
||||||
run_with_privileges "$operation" "$command" "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"help"|"--help"|"-h")
|
|
||||||
cat <<EOF
|
|
||||||
HOPS Privilege Management System
|
|
||||||
|
|
||||||
Usage: $0 <action> [options]
|
|
||||||
|
|
||||||
Actions:
|
|
||||||
create-setup <file> Create privileged setup script
|
|
||||||
create-user <file> Create non-privileged user script
|
|
||||||
create-wrapper <file> Create installation wrapper
|
|
||||||
create-all Create all scripts
|
|
||||||
run <op> <cmd> [args] Run operation with appropriate privileges
|
|
||||||
help Show this help message
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
$0 create-all
|
|
||||||
$0 run install_docker /usr/bin/apt-get install docker-ce
|
|
||||||
$0 run generate_docker_compose ./compose-gen.sh
|
|
||||||
|
|
||||||
EOF
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
error_exit "Unknown action: $action. Use 'help' for usage information."
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# If script is run directly (not sourced)
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
setup_logging "privileges"
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
+2
-2
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# HOPS - Secret Management System
|
# HOPS - Secret Management System
|
||||||
# Secure encryption and management of sensitive configuration data
|
# Secure encryption and management of sensitive configuration data
|
||||||
# Version: 3.1.0-beta
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Source common functions
|
# Source common functions
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
@@ -190,7 +190,7 @@ create_encrypted_environment() {
|
|||||||
cat > "$temp_env_file" << EOF
|
cat > "$temp_env_file" << EOF
|
||||||
# HOPS Environment Configuration
|
# HOPS Environment Configuration
|
||||||
# Generated on: $(date)
|
# Generated on: $(date)
|
||||||
# Version: 3.1.0-beta
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Core Configuration
|
# Core Configuration
|
||||||
PUID=$puid
|
PUID=$puid
|
||||||
|
|||||||
+13
-3
@@ -2,17 +2,27 @@
|
|||||||
|
|
||||||
# HOPS - Security Functions
|
# HOPS - Security Functions
|
||||||
# Password generation, validation, and security utilities
|
# Password generation, validation, and security utilities
|
||||||
# Version: 3.2.0
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Source common functions
|
# Source common functions
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/common.sh"
|
source "$LIB_DIR/common.sh"
|
||||||
|
|
||||||
# Password validation
|
# Password validation
|
||||||
validate_password() {
|
validate_password() {
|
||||||
local password="$1"
|
local password="$1"
|
||||||
local min_length="${2:-12}"
|
local min_length="${2:-12}"
|
||||||
|
|
||||||
|
if [[ -z "$password" ]]; then
|
||||||
|
echo -e "\n Password must meet these requirements:"
|
||||||
|
echo " - Minimum $min_length characters"
|
||||||
|
echo " - At least one uppercase letter"
|
||||||
|
echo " - At least one lowercase letter"
|
||||||
|
echo " - At least one number"
|
||||||
|
echo " - At least one special character"
|
||||||
|
return 3
|
||||||
|
fi
|
||||||
|
|
||||||
# Check minimum length
|
# Check minimum length
|
||||||
if [[ ${#password} -lt $min_length ]]; then
|
if [[ ${#password} -lt $min_length ]]; then
|
||||||
debug "Password too short: ${#password} < $min_length"
|
debug "Password too short: ${#password} < $min_length"
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# HOPS - System Validation Functions
|
# HOPS - System Validation Functions
|
||||||
# Functions for system checks, OS detection, and requirements validation
|
# Functions for system checks, OS detection, and requirements validation
|
||||||
# Version: 3.2.0
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Source common functions
|
# Source common functions
|
||||||
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|||||||
+10
-16
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
# HOPS - Input Validation and Sanitization Functions
|
# HOPS - Input Validation and Sanitization Functions
|
||||||
# Comprehensive input validation and sanitization utilities
|
# Comprehensive input validation and sanitization utilities
|
||||||
# Version: 3.1.0-beta
|
# Version: 1.0.0
|
||||||
|
|
||||||
# Source common functions
|
# Source common functions
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
source "$SCRIPT_DIR/common.sh"
|
source "$LIB_DIR/common.sh"
|
||||||
|
|
||||||
# Validate and sanitize directory path
|
# Validate and sanitize directory path
|
||||||
validate_directory_path() {
|
validate_directory_path() {
|
||||||
@@ -60,22 +60,16 @@ validate_directory_path() {
|
|||||||
echo "$path"
|
echo "$path"
|
||||||
}
|
}
|
||||||
|
|
||||||
# Validate timezone
|
# Validate timezone -- returns the timezone string, defaulting to America/New_York if invalid
|
||||||
validate_timezone() {
|
validate_timezone() {
|
||||||
local timezone="$1"
|
local timezone="$1"
|
||||||
|
local default="America/New_York"
|
||||||
|
|
||||||
if [[ -z "$timezone" ]]; then
|
if [[ -z "$timezone" ]] || \
|
||||||
error_exit "Timezone cannot be empty"
|
[[ ! "$timezone" =~ ^[A-Za-z_]+(/[A-Za-z_]+)*$ ]] || \
|
||||||
fi
|
! timedatectl list-timezones 2>/dev/null | grep -qx "$timezone"; then
|
||||||
|
warning "Timezone '${timezone}' invalid, defaulting to '${default}'"
|
||||||
# Basic format validation
|
timezone="$default"
|
||||||
if [[ ! "$timezone" =~ ^[A-Za-z_]+(/[A-Za-z_]+)*$ ]]; then
|
|
||||||
error_exit "Invalid timezone format: $timezone"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if timezone file exists
|
|
||||||
if [[ ! -f "/usr/share/zoneinfo/$timezone" ]]; then
|
|
||||||
error_exit "Unknown timezone: $timezone"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "$timezone"
|
echo "$timezone"
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS Privileged Setup Script
|
|
||||||
# This script handles operations that require root privileges
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/system.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/security.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "privileged-setup"
|
|
||||||
|
|
||||||
# Check root privileges
|
|
||||||
check_root
|
|
||||||
|
|
||||||
# Install Docker if not present
|
|
||||||
install_docker() {
|
|
||||||
info "🐳 Installing Docker..."
|
|
||||||
|
|
||||||
if command_exists docker; then
|
|
||||||
success "Docker already installed"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Update package index
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
# Install prerequisites
|
|
||||||
apt-get install -y \
|
|
||||||
ca-certificates \
|
|
||||||
curl \
|
|
||||||
gnupg \
|
|
||||||
lsb-release
|
|
||||||
|
|
||||||
# Add Docker GPG key
|
|
||||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
|
|
||||||
|
|
||||||
# Add Docker repository with proper Ubuntu codename detection for Linux Mint
|
|
||||||
local ubuntu_codename
|
|
||||||
if [[ "$(lsb_release -is)" == "Linuxmint" ]]; then
|
|
||||||
# Linux Mint provides UBUNTU_CODENAME in /etc/os-release
|
|
||||||
if [[ -f /etc/os-release ]]; then
|
|
||||||
ubuntu_codename=$(grep '^UBUNTU_CODENAME=' /etc/os-release | cut -d= -f2)
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Fallback to version mapping if UBUNTU_CODENAME not found
|
|
||||||
if [[ -z "$ubuntu_codename" ]]; then
|
|
||||||
case "$(lsb_release -rs)" in
|
|
||||||
"22"|"22.1"|"22.2"|"22.3")
|
|
||||||
ubuntu_codename="noble" # Ubuntu 24.04
|
|
||||||
;;
|
|
||||||
"21"|"21.1"|"21.2"|"21.3")
|
|
||||||
ubuntu_codename="jammy" # Ubuntu 22.04
|
|
||||||
;;
|
|
||||||
"20"|"20.1"|"20.2"|"20.3")
|
|
||||||
ubuntu_codename="focal" # Ubuntu 20.04
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
ubuntu_codename="noble" # Default to latest LTS
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
ubuntu_codename=$(lsb_release -cs)
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "DEBUG: Detected OS: $(lsb_release -is), Ubuntu codename: $ubuntu_codename"
|
|
||||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $ubuntu_codename stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
|
|
||||||
|
|
||||||
# Update package index with Docker packages
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
# Install Docker
|
|
||||||
apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
|
|
||||||
|
|
||||||
# Start and enable Docker service
|
|
||||||
systemctl start docker
|
|
||||||
systemctl enable docker
|
|
||||||
|
|
||||||
success "Docker installed successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure firewall
|
|
||||||
configure_firewall() {
|
|
||||||
info "🔥 Configuring firewall..."
|
|
||||||
|
|
||||||
# Install UFW if not present
|
|
||||||
if ! command_exists ufw; then
|
|
||||||
apt-get update
|
|
||||||
apt-get install -y ufw
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Reset firewall to defaults
|
|
||||||
ufw --force reset
|
|
||||||
|
|
||||||
# Set default policies
|
|
||||||
ufw default deny incoming
|
|
||||||
ufw default allow outgoing
|
|
||||||
|
|
||||||
# Allow SSH (prevent lockout)
|
|
||||||
ufw allow ssh
|
|
||||||
|
|
||||||
# Allow HTTP and HTTPS
|
|
||||||
ufw allow 80/tcp
|
|
||||||
ufw allow 443/tcp
|
|
||||||
|
|
||||||
# Enable firewall
|
|
||||||
ufw --force enable
|
|
||||||
|
|
||||||
success "Firewall configured successfully"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create system directories
|
|
||||||
create_system_directories() {
|
|
||||||
info "📁 Creating system directories..."
|
|
||||||
|
|
||||||
local directories=(
|
|
||||||
"/opt/appdata"
|
|
||||||
"/mnt/media"
|
|
||||||
"/mnt/media/movies"
|
|
||||||
"/mnt/media/tv"
|
|
||||||
"/mnt/media/music"
|
|
||||||
"/mnt/media/downloads"
|
|
||||||
"/var/log/hops"
|
|
||||||
)
|
|
||||||
|
|
||||||
for dir in "${directories[@]}"; do
|
|
||||||
if mkdir -p "$dir"; then
|
|
||||||
success "Created directory: $dir"
|
|
||||||
else
|
|
||||||
error_exit "Failed to create directory: $dir"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Set ownership to the user who ran sudo
|
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
|
||||||
local user_info
|
|
||||||
user_info=$(get_user_info)
|
|
||||||
|
|
||||||
local uid=$(echo "$user_info" | grep "uid=" | cut -d= -f2)
|
|
||||||
local gid=$(echo "$user_info" | grep "gid=" | cut -d= -f2)
|
|
||||||
|
|
||||||
chown -R "$uid:$gid" /opt/appdata /mnt/media
|
|
||||||
success "Set ownership of directories to $SUDO_USER"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Add user to docker group
|
|
||||||
add_user_to_docker_group() {
|
|
||||||
if [[ -z "$SUDO_USER" ]]; then
|
|
||||||
warning "No SUDO_USER set, skipping docker group addition"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "👥 Adding user to docker group..."
|
|
||||||
|
|
||||||
if usermod -aG docker "$SUDO_USER"; then
|
|
||||||
success "User $SUDO_USER added to docker group"
|
|
||||||
warning "User must log out and back in for group changes to take effect"
|
|
||||||
else
|
|
||||||
error_exit "Failed to add user to docker group"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Install required packages
|
|
||||||
install_packages() {
|
|
||||||
info "📦 Installing required packages..."
|
|
||||||
|
|
||||||
apt-get update
|
|
||||||
|
|
||||||
local packages=(
|
|
||||||
"curl"
|
|
||||||
"wget"
|
|
||||||
"git"
|
|
||||||
"jq"
|
|
||||||
"htop"
|
|
||||||
"tree"
|
|
||||||
"unzip"
|
|
||||||
"gnupg"
|
|
||||||
"software-properties-common"
|
|
||||||
"apt-transport-https"
|
|
||||||
"ca-certificates"
|
|
||||||
"lsb-release"
|
|
||||||
)
|
|
||||||
|
|
||||||
for package in "${packages[@]}"; do
|
|
||||||
if apt-get install -y "$package"; then
|
|
||||||
success "Installed package: $package"
|
|
||||||
else
|
|
||||||
warning "Failed to install package: $package"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Setup secrets directory
|
|
||||||
setup_secrets_directory() {
|
|
||||||
info "🔐 Setting up secrets directory..."
|
|
||||||
|
|
||||||
local secrets_dir="/etc/hops/secrets"
|
|
||||||
|
|
||||||
if mkdir -p "$secrets_dir"; then
|
|
||||||
chmod 700 "$secrets_dir"
|
|
||||||
success "Secrets directory created: $secrets_dir"
|
|
||||||
else
|
|
||||||
error_exit "Failed to create secrets directory"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Configure system settings
|
|
||||||
configure_system() {
|
|
||||||
info "⚙️ Configuring system settings..."
|
|
||||||
|
|
||||||
# Set timezone if not already set
|
|
||||||
if [[ -n "$TZ" ]]; then
|
|
||||||
timedatectl set-timezone "$TZ" 2>/dev/null || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Enable IP forwarding for Docker
|
|
||||||
echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf
|
|
||||||
sysctl -p /etc/sysctl.conf
|
|
||||||
|
|
||||||
success "System configuration completed"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main privileged setup
|
|
||||||
main() {
|
|
||||||
info "🚀 Starting privileged setup..."
|
|
||||||
|
|
||||||
# System checks
|
|
||||||
detect_os
|
|
||||||
check_system_requirements
|
|
||||||
|
|
||||||
# Install packages
|
|
||||||
install_packages
|
|
||||||
|
|
||||||
# Install Docker
|
|
||||||
install_docker
|
|
||||||
|
|
||||||
# Configure firewall
|
|
||||||
configure_firewall
|
|
||||||
|
|
||||||
# Create directories
|
|
||||||
create_system_directories
|
|
||||||
|
|
||||||
# Add user to docker group
|
|
||||||
add_user_to_docker_group
|
|
||||||
|
|
||||||
# Setup secrets
|
|
||||||
setup_secrets_directory
|
|
||||||
|
|
||||||
# Configure system
|
|
||||||
configure_system
|
|
||||||
|
|
||||||
success "Privileged setup completed successfully"
|
|
||||||
success "Please log out and back in for group changes to take effect"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run if executed directly
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
# HOPS Service Definitions
|
# HOPS Service Definitions
|
||||||
# Contains all Docker Compose service configurations
|
# Contains all Docker Compose service configurations
|
||||||
# Version: 3.2.0
|
# Version: 1.0.0
|
||||||
|
|
||||||
# This script provides functions to generate Docker Compose service definitions
|
# This script provides functions to generate Docker Compose service definitions
|
||||||
# Usage: Source this script and call generate_service_definition <service_name>
|
# Usage: Source this script and call generate_service_definition <service_name>
|
||||||
@@ -24,24 +24,18 @@ EOF
|
|||||||
# Get timezone mount path for current platform
|
# Get timezone mount path for current platform
|
||||||
get_timezone_mount() {
|
get_timezone_mount() {
|
||||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||||
# macOS doesn't need timezone mount, use TZ environment variable
|
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
# Linux timezone mount
|
echo " - /etc/localtime:/etc/localtime:ro"
|
||||||
echo "$(get_timezone_mount)"
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
# Get GPU device access for current platform
|
# Get GPU device access for current platform
|
||||||
get_gpu_devices() {
|
get_gpu_devices() {
|
||||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||||
# macOS doesn't support GPU passthrough to Docker containers
|
|
||||||
echo ""
|
echo ""
|
||||||
else
|
else
|
||||||
# Linux GPU device access
|
printf " devices:\n - /dev/dri:/dev/dri\n"
|
||||||
cat <<EOF
|
|
||||||
$(get_gpu_devices)
|
|
||||||
EOF
|
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1349,7 +1343,7 @@ list_available_services() {
|
|||||||
# Usage information
|
# Usage information
|
||||||
show_usage() {
|
show_usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
HOPS Service Definitions Script v3.2.0
|
HOPS Service Definitions Script v1.0.0
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
source services
|
source services
|
||||||
|
|||||||
@@ -1,572 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS Service Definitions - Improved Version
|
|
||||||
# Contains all Docker Compose service configurations with error handling and pinned versions
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
# Exit on any error
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
if [[ -f "$SCRIPT_DIR/lib/common.sh" ]]; then
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
else
|
|
||||||
echo "ERROR: Common library not found. Please ensure lib/common.sh exists." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -f "$SCRIPT_DIR/lib/security.sh" ]]; then
|
|
||||||
source "$SCRIPT_DIR/lib/security.sh"
|
|
||||||
else
|
|
||||||
echo "ERROR: Security library not found. Please ensure lib/security.sh exists." >&2
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Service definitions with pinned versions (from docker.sh)
|
|
||||||
declare -A SERVICE_IMAGES=(
|
|
||||||
# Media Management (*arr Stack)
|
|
||||||
["sonarr"]="lscr.io/linuxserver/sonarr:4.0.10"
|
|
||||||
["radarr"]="lscr.io/linuxserver/radarr:5.8.3"
|
|
||||||
["lidarr"]="lscr.io/linuxserver/lidarr:2.5.3"
|
|
||||||
["readarr"]="lscr.io/linuxserver/readarr:0.3.32-develop"
|
|
||||||
["bazarr"]="lscr.io/linuxserver/bazarr:1.4.3"
|
|
||||||
["prowlarr"]="lscr.io/linuxserver/prowlarr:1.24.3"
|
|
||||||
["tdarr"]="ghcr.io/haveagitgat/tdarr:2.26.01"
|
|
||||||
|
|
||||||
# Download Clients
|
|
||||||
["qbittorrent"]="lscr.io/linuxserver/qbittorrent:4.6.7"
|
|
||||||
["transmission"]="lscr.io/linuxserver/transmission:4.0.6"
|
|
||||||
["nzbget"]="lscr.io/linuxserver/nzbget:24.3"
|
|
||||||
["sabnzbd"]="lscr.io/linuxserver/sabnzbd:4.3.3"
|
|
||||||
|
|
||||||
# Media Servers
|
|
||||||
["jellyfin"]="lscr.io/linuxserver/jellyfin:10.9.11"
|
|
||||||
["plex"]="lscr.io/linuxserver/plex:1.40.5"
|
|
||||||
["emby"]="lscr.io/linuxserver/emby:4.8.8"
|
|
||||||
["jellystat"]="cyfershepard/jellystat:1.1.0"
|
|
||||||
|
|
||||||
# Request Management
|
|
||||||
["overseerr"]="lscr.io/linuxserver/overseerr:1.33.2"
|
|
||||||
["jellyseerr"]="fallenbagel/jellyseerr:1.9.2"
|
|
||||||
["ombi"]="lscr.io/linuxserver/ombi:4.43.5"
|
|
||||||
|
|
||||||
# Reverse Proxy & Security
|
|
||||||
["traefik"]="traefik:v3.1.6"
|
|
||||||
["nginx-proxy-manager"]="jc21/nginx-proxy-manager:2.11.3"
|
|
||||||
["authelia"]="authelia/authelia:4.38.16"
|
|
||||||
|
|
||||||
# Monitoring & Management
|
|
||||||
["portainer"]="portainer/portainer-ce:2.21.4"
|
|
||||||
["uptime-kuma"]="louislam/uptime-kuma:1.23.15"
|
|
||||||
["watchtower"]="containrrr/watchtower:1.7.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Service port mapping
|
|
||||||
declare -A SERVICE_PORTS=(
|
|
||||||
["sonarr"]="8989"
|
|
||||||
["radarr"]="7878"
|
|
||||||
["lidarr"]="8686"
|
|
||||||
["readarr"]="8787"
|
|
||||||
["bazarr"]="6767"
|
|
||||||
["prowlarr"]="9696"
|
|
||||||
["tdarr"]="8265"
|
|
||||||
["qbittorrent"]="8082"
|
|
||||||
["transmission"]="9091"
|
|
||||||
["nzbget"]="6789"
|
|
||||||
["sabnzbd"]="8080"
|
|
||||||
["jellyfin"]="8096"
|
|
||||||
["plex"]="32400"
|
|
||||||
["emby"]="8096"
|
|
||||||
["jellystat"]="3000"
|
|
||||||
["overseerr"]="5055"
|
|
||||||
["jellyseerr"]="5056"
|
|
||||||
["ombi"]="3579"
|
|
||||||
["traefik"]="8080"
|
|
||||||
["nginx-proxy-manager"]="81"
|
|
||||||
["authelia"]="9091"
|
|
||||||
["portainer"]="9000"
|
|
||||||
["uptime-kuma"]="3001"
|
|
||||||
["watchtower"]="8080"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "service-definitions"
|
|
||||||
|
|
||||||
# Validate service name
|
|
||||||
validate_service_name_internal() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
if [[ -z "$service_name" ]]; then
|
|
||||||
error_exit "Service name is required"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! validate_service_name "$service_name"; then
|
|
||||||
error_exit "Invalid service name: $service_name"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "${SERVICE_IMAGES[$service_name]}" ]]; then
|
|
||||||
error_exit "Unknown service: $service_name"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get service image with validation
|
|
||||||
get_service_image() {
|
|
||||||
local service_name="$1"
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
echo "${SERVICE_IMAGES[$service_name]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get service port with validation
|
|
||||||
get_service_port() {
|
|
||||||
local service_name="$1"
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
echo "${SERVICE_PORTS[$service_name]}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common environment variables for LinuxServer containers
|
|
||||||
get_linuxserver_env() {
|
|
||||||
cat <<EOF
|
|
||||||
- PUID=\${PUID:-1000}
|
|
||||||
- PGID=\${PGID:-1000}
|
|
||||||
- TZ=\${TZ:-UTC}
|
|
||||||
- UMASK=002
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common restart policy
|
|
||||||
get_restart_policy() {
|
|
||||||
echo " restart: unless-stopped"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common network configuration
|
|
||||||
get_homelab_network() {
|
|
||||||
cat <<EOF
|
|
||||||
networks:
|
|
||||||
- homelab
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common healthcheck for web services
|
|
||||||
get_web_healthcheck() {
|
|
||||||
local service_name="$1"
|
|
||||||
local path="${2:-/}"
|
|
||||||
|
|
||||||
if [[ -z "$service_name" ]]; then
|
|
||||||
error_exit "Service name required for healthcheck"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "curl -f http://localhost:$port$path || exit 1"]
|
|
||||||
interval: 30s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 3
|
|
||||||
start_period: 60s
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common volume configuration
|
|
||||||
get_common_volumes() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
if [[ -z "$service_name" ]]; then
|
|
||||||
error_exit "Service name required for volumes"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
volumes:
|
|
||||||
- \${CONFIG_ROOT:-/opt/appdata}/$service_name:/config
|
|
||||||
- \${DATA_ROOT:-/mnt/media}:/data
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Common Traefik labels
|
|
||||||
get_traefik_labels() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
if [[ -z "$service_name" ]]; then
|
|
||||||
error_exit "Service name required for Traefik labels"
|
|
||||||
fi
|
|
||||||
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.$service_name.rule=Host(\`$service_name.\${DOMAIN:-localhost}\`)"
|
|
||||||
- "traefik.http.routers.$service_name.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.$service_name.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.services.$service_name.loadbalancer.server.port=$port"
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# --------------------------------------------
|
|
||||||
# SERVICE GENERATORS
|
|
||||||
# --------------------------------------------
|
|
||||||
|
|
||||||
# Generate *arr service (template for Sonarr, Radarr, etc.)
|
|
||||||
generate_arr_service() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
$service_name:
|
|
||||||
image: $image
|
|
||||||
container_name: $service_name
|
|
||||||
$(get_restart_policy)
|
|
||||||
ports:
|
|
||||||
- "$port:$port"
|
|
||||||
environment:
|
|
||||||
$(get_linuxserver_env)
|
|
||||||
$(get_common_volumes "$service_name")
|
|
||||||
$(get_web_healthcheck "$service_name")
|
|
||||||
$(get_homelab_network)
|
|
||||||
$(get_traefik_labels "$service_name")
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate media server service
|
|
||||||
generate_media_server() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
# Special handling for Plex
|
|
||||||
local additional_config=""
|
|
||||||
if [[ "$service_name" == "plex" ]]; then
|
|
||||||
additional_config=" - PLEX_CLAIM=\${PLEX_CLAIM:-}
|
|
||||||
- ADVERTISE_IP=\${ADVERTISE_IP:-}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
$service_name:
|
|
||||||
image: $image
|
|
||||||
container_name: $service_name
|
|
||||||
$(get_restart_policy)
|
|
||||||
ports:
|
|
||||||
- "$port:$port"
|
|
||||||
environment:
|
|
||||||
$(get_linuxserver_env)
|
|
||||||
$additional_config
|
|
||||||
$(get_common_volumes "$service_name")
|
|
||||||
$(get_web_healthcheck "$service_name")
|
|
||||||
$(get_homelab_network)
|
|
||||||
$(get_traefik_labels "$service_name")
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate download client service
|
|
||||||
generate_download_client() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
# Special handling for qBittorrent
|
|
||||||
local additional_config=""
|
|
||||||
if [[ "$service_name" == "qbittorrent" ]]; then
|
|
||||||
additional_config=" - WEBUI_PORT=$port"
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
$service_name:
|
|
||||||
image: $image
|
|
||||||
container_name: $service_name
|
|
||||||
$(get_restart_policy)
|
|
||||||
ports:
|
|
||||||
- "$port:$port"
|
|
||||||
environment:
|
|
||||||
$(get_linuxserver_env)
|
|
||||||
$additional_config
|
|
||||||
$(get_common_volumes "$service_name")
|
|
||||||
$(get_web_healthcheck "$service_name")
|
|
||||||
$(get_homelab_network)
|
|
||||||
$(get_traefik_labels "$service_name")
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate monitoring service
|
|
||||||
generate_monitoring_service() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
# Special handling for Portainer
|
|
||||||
local additional_config=""
|
|
||||||
local volume_config=""
|
|
||||||
|
|
||||||
if [[ "$service_name" == "portainer" ]]; then
|
|
||||||
volume_config=" volumes:
|
|
||||||
- \${CONFIG_ROOT:-/opt/appdata}/$service_name:/data
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- /etc/localtime:/etc/localtime:ro"
|
|
||||||
elif [[ "$service_name" == "watchtower" ]]; then
|
|
||||||
volume_config=" volumes:
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- /etc/localtime:/etc/localtime:ro"
|
|
||||||
additional_config=" - WATCHTOWER_CLEANUP=true
|
|
||||||
- WATCHTOWER_SCHEDULE=0 0 4 * * *"
|
|
||||||
else
|
|
||||||
volume_config=$(get_common_volumes "$service_name")
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
$service_name:
|
|
||||||
image: $image
|
|
||||||
container_name: $service_name
|
|
||||||
$(get_restart_policy)
|
|
||||||
ports:
|
|
||||||
- "$port:$port"
|
|
||||||
environment:
|
|
||||||
$(get_linuxserver_env)
|
|
||||||
$additional_config
|
|
||||||
$volume_config
|
|
||||||
$(get_web_healthcheck "$service_name")
|
|
||||||
$(get_homelab_network)
|
|
||||||
$(get_traefik_labels "$service_name")
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate Traefik service
|
|
||||||
generate_traefik() {
|
|
||||||
local service_name="traefik"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
traefik:
|
|
||||||
image: $image
|
|
||||||
container_name: traefik
|
|
||||||
$(get_restart_policy)
|
|
||||||
ports:
|
|
||||||
- "80:80"
|
|
||||||
- "443:443"
|
|
||||||
- "$port:$port"
|
|
||||||
environment:
|
|
||||||
- CF_API_EMAIL=\${CF_API_EMAIL:-}
|
|
||||||
- CF_API_KEY=\${CF_API_KEY:-}
|
|
||||||
command:
|
|
||||||
- "--api.dashboard=true"
|
|
||||||
- "--api.insecure=true"
|
|
||||||
- "--entrypoints.web.address=:80"
|
|
||||||
- "--entrypoints.websecure.address=:443"
|
|
||||||
- "--providers.docker=true"
|
|
||||||
- "--providers.docker.exposedbydefault=false"
|
|
||||||
- "--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
|
|
||||||
- "--certificatesresolvers.letsencrypt.acme.email=\${ACME_EMAIL:-admin@localhost}"
|
|
||||||
- "--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json"
|
|
||||||
volumes:
|
|
||||||
- \${CONFIG_ROOT:-/opt/appdata}/traefik:/letsencrypt
|
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
|
||||||
- /etc/localtime:/etc/localtime:ro
|
|
||||||
networks:
|
|
||||||
- homelab
|
|
||||||
- traefik
|
|
||||||
labels:
|
|
||||||
- "traefik.enable=true"
|
|
||||||
- "traefik.http.routers.traefik.rule=Host(\`traefik.\${DOMAIN:-localhost}\`)"
|
|
||||||
- "traefik.http.routers.traefik.entrypoints=websecure"
|
|
||||||
- "traefik.http.routers.traefik.tls.certresolver=letsencrypt"
|
|
||||||
- "traefik.http.services.traefik.loadbalancer.server.port=$port"
|
|
||||||
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main service generator function
|
|
||||||
generate_service_definition() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
if [[ -z "$service_name" ]]; then
|
|
||||||
error_exit "Service name is required"
|
|
||||||
fi
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
debug "Generating service definition for: $service_name"
|
|
||||||
|
|
||||||
case "$service_name" in
|
|
||||||
# Media Management (*arr Stack)
|
|
||||||
"sonarr"|"radarr"|"lidarr"|"readarr"|"bazarr"|"prowlarr")
|
|
||||||
generate_arr_service "$service_name"
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Download Clients
|
|
||||||
"qbittorrent"|"transmission"|"nzbget"|"sabnzbd")
|
|
||||||
generate_download_client "$service_name"
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Media Servers
|
|
||||||
"jellyfin"|"plex"|"emby"|"jellystat")
|
|
||||||
generate_media_server "$service_name"
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Request Management
|
|
||||||
"overseerr"|"jellyseerr"|"ombi")
|
|
||||||
generate_arr_service "$service_name" # They use similar patterns
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Reverse Proxy & Security
|
|
||||||
"traefik")
|
|
||||||
generate_traefik
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Monitoring & Management
|
|
||||||
"portainer"|"uptime-kuma"|"watchtower")
|
|
||||||
generate_monitoring_service "$service_name"
|
|
||||||
;;
|
|
||||||
|
|
||||||
# Other services
|
|
||||||
"nginx-proxy-manager"|"authelia"|"tdarr")
|
|
||||||
generate_arr_service "$service_name" # Use generic template
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
error_exit "Unknown service: $service_name"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate multiple services
|
|
||||||
generate_multiple_services() {
|
|
||||||
local services=("$@")
|
|
||||||
|
|
||||||
if [[ ${#services[@]} -eq 0 ]]; then
|
|
||||||
error_exit "No services specified"
|
|
||||||
fi
|
|
||||||
|
|
||||||
debug "Generating definitions for ${#services[@]} services"
|
|
||||||
|
|
||||||
for service in "${services[@]}"; do
|
|
||||||
generate_service_definition "$service"
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# List all available services
|
|
||||||
list_available_services() {
|
|
||||||
echo "Available services:"
|
|
||||||
echo
|
|
||||||
|
|
||||||
local categories=(
|
|
||||||
"Media Management:sonarr,radarr,lidarr,readarr,bazarr,prowlarr,tdarr"
|
|
||||||
"Download Clients:qbittorrent,transmission,nzbget,sabnzbd"
|
|
||||||
"Media Servers:jellyfin,plex,emby,jellystat"
|
|
||||||
"Request Management:overseerr,jellyseerr,ombi"
|
|
||||||
"Reverse Proxy & Security:traefik,nginx-proxy-manager,authelia"
|
|
||||||
"Monitoring & Management:portainer,uptime-kuma,watchtower"
|
|
||||||
)
|
|
||||||
|
|
||||||
for category in "${categories[@]}"; do
|
|
||||||
local category_name="${category%%:*}"
|
|
||||||
local services="${category#*:}"
|
|
||||||
|
|
||||||
echo -e "${CYAN}${category_name}:${NC}"
|
|
||||||
IFS=',' read -ra service_list <<< "$services"
|
|
||||||
|
|
||||||
for service in "${service_list[@]}"; do
|
|
||||||
local port=$(get_service_port "$service")
|
|
||||||
local image=$(get_service_image "$service")
|
|
||||||
printf " %-20s Port: %-6s Image: %s\n" "$service" "$port" "$image"
|
|
||||||
done
|
|
||||||
echo
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Validate service configuration
|
|
||||||
validate_service_config() {
|
|
||||||
local service_name="$1"
|
|
||||||
|
|
||||||
validate_service_name_internal "$service_name"
|
|
||||||
|
|
||||||
local image=$(get_service_image "$service_name")
|
|
||||||
local port=$(get_service_port "$service_name")
|
|
||||||
|
|
||||||
# Check if image exists (basic validation)
|
|
||||||
if [[ -z "$image" ]]; then
|
|
||||||
error_exit "No image defined for service: $service_name"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if port is valid
|
|
||||||
if [[ ! "$port" =~ ^[0-9]+$ ]] || [[ "$port" -lt 1 ]] || [[ "$port" -gt 65535 ]]; then
|
|
||||||
error_exit "Invalid port for service $service_name: $port"
|
|
||||||
fi
|
|
||||||
|
|
||||||
success "Service configuration valid: $service_name"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main function for command line usage
|
|
||||||
main() {
|
|
||||||
local action="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
"generate")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 generate <service_name> [service_name...]"
|
|
||||||
fi
|
|
||||||
generate_multiple_services "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"list")
|
|
||||||
list_available_services
|
|
||||||
;;
|
|
||||||
|
|
||||||
"validate")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 validate <service_name>"
|
|
||||||
fi
|
|
||||||
validate_service_config "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"help"|"--help"|"-h")
|
|
||||||
cat <<EOF
|
|
||||||
HOPS Service Definitions - Improved Version
|
|
||||||
|
|
||||||
Usage: $0 <action> [options]
|
|
||||||
|
|
||||||
Actions:
|
|
||||||
generate <service>... Generate service definitions
|
|
||||||
list List all available services
|
|
||||||
validate <service> Validate service configuration
|
|
||||||
help Show this help message
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
$0 generate sonarr radarr jellyfin
|
|
||||||
$0 list
|
|
||||||
$0 validate traefik
|
|
||||||
|
|
||||||
EOF
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
error_exit "Unknown action: $action. Use 'help' for usage information."
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# If script is run directly (not sourced)
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS Installation Wrapper
|
|
||||||
# Orchestrates privileged and non-privileged installation steps
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "installation-wrapper"
|
|
||||||
|
|
||||||
# Show header
|
|
||||||
show_hops_header "3.1.0-beta" "Installation Wrapper"
|
|
||||||
|
|
||||||
# Check if we're running as root
|
|
||||||
if [[ $EUID -eq 0 ]]; then
|
|
||||||
if [[ -z "$SUDO_USER" ]]; then
|
|
||||||
error_exit "Please run with sudo, not as root directly"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
error_exit "This script must be run with sudo"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Phase 1: Privileged setup
|
|
||||||
info "📋 Phase 1: Privileged setup (requires root)"
|
|
||||||
if "$SCRIPT_DIR/privileged-setup"; then
|
|
||||||
success "Privileged setup completed"
|
|
||||||
else
|
|
||||||
error_exit "Privileged setup failed"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Phase 2: User setup
|
|
||||||
info "📋 Phase 2: User setup (running as $SUDO_USER)"
|
|
||||||
|
|
||||||
# Drop privileges and run user setup
|
|
||||||
sudo -u "$SUDO_USER" bash << 'USERSCRIPT'
|
|
||||||
cd "$HOME"
|
|
||||||
echo "Running as user: $(whoami)"
|
|
||||||
|
|
||||||
# Interactive service selection
|
|
||||||
echo "Select services to install:"
|
|
||||||
echo "1) Media Server Stack (Jellyfin, Sonarr, Radarr, Prowlarr)"
|
|
||||||
echo "2) Download Client Stack (qBittorrent, Transmission)"
|
|
||||||
echo "3) Monitoring Stack (Portainer, Uptime Kuma)"
|
|
||||||
echo "4) Custom selection"
|
|
||||||
|
|
||||||
read -p "Enter your choice (1-4): " choice
|
|
||||||
|
|
||||||
case "$choice" in
|
|
||||||
1)
|
|
||||||
services=("jellyfin" "sonarr" "radarr" "prowlarr")
|
|
||||||
;;
|
|
||||||
2)
|
|
||||||
services=("qbittorrent" "transmission")
|
|
||||||
;;
|
|
||||||
3)
|
|
||||||
services=("portainer" "uptime-kuma")
|
|
||||||
;;
|
|
||||||
4)
|
|
||||||
echo "Available services:"
|
|
||||||
"$SCRIPT_DIR/services-improved" list
|
|
||||||
read -p "Enter service names (space-separated): " -a services
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Invalid choice"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Generate and deploy
|
|
||||||
if "$SCRIPT_DIR/user-operations" generate "${services[@]}"; then
|
|
||||||
echo "Configuration generated successfully"
|
|
||||||
|
|
||||||
if "$SCRIPT_DIR/user-operations" deploy; then
|
|
||||||
echo "Services deployed successfully"
|
|
||||||
else
|
|
||||||
echo "Deployment failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
echo "Configuration generation failed"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
USERSCRIPT
|
|
||||||
|
|
||||||
success "Installation completed successfully"
|
|
||||||
success "Services are now running. Check status with: ./user-operations status"
|
|
||||||
-170
@@ -1,170 +0,0 @@
|
|||||||
# HOPS Linux Mint Docker Repository Troubleshooting Summary
|
|
||||||
## Date: July 19, 2025
|
|
||||||
|
|
||||||
### Problem Description
|
|
||||||
HOPS installation failing on Linux Mint 22.1 with Docker repository error:
|
|
||||||
```
|
|
||||||
E: The repository 'https://download.docker.com/linux/ubuntu xia Release' does not have a Release file.
|
|
||||||
N: Updating from such a repository can't be done securely, and is therefore disabled by default.
|
|
||||||
```
|
|
||||||
|
|
||||||
### Root Cause Analysis
|
|
||||||
- Linux Mint uses its own codenames (e.g., "xia" for version 22.1)
|
|
||||||
- Docker repositories are structured around Ubuntu codenames (e.g., "noble", "jammy", "focal")
|
|
||||||
- HOPS was using `lsb_release -cs` which returns "xia" instead of the Ubuntu base codename
|
|
||||||
- Docker doesn't have a repository for Linux Mint's "xia" codename
|
|
||||||
|
|
||||||
### System Information (Linux Mint 22.1)
|
|
||||||
```
|
|
||||||
Distributor ID: LinuxMint
|
|
||||||
Description: Linux Mint 22.1
|
|
||||||
Release: 22.1
|
|
||||||
Codename: xia
|
|
||||||
UBUNTU_CODENAME=noble (from /etc/os-release)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Troubleshooting Steps Attempted
|
|
||||||
|
|
||||||
#### 1. Initial Fix Attempt (Commit af57a77)
|
|
||||||
**Files Modified:** `privileged-setup`, `lib/privileges.sh`
|
|
||||||
**Approach:** Added Linux Mint version to Ubuntu codename mapping
|
|
||||||
- Mint 22.x → Ubuntu 24.04 (noble)
|
|
||||||
- Mint 21.x → Ubuntu 22.04 (jammy)
|
|
||||||
- Mint 20.x → Ubuntu 20.04 (focal)
|
|
||||||
|
|
||||||
**Result:** Still failed with same error
|
|
||||||
|
|
||||||
#### 2. System Cleanup
|
|
||||||
**Commands Executed:**
|
|
||||||
```bash
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/docker*
|
|
||||||
sudo rm -f /etc/apt/sources.list.d/*docker*
|
|
||||||
sudo rm -f /usr/share/keyrings/docker*
|
|
||||||
sudo rm -f /etc/apt/keyrings/docker*
|
|
||||||
sudo grep -i docker /etc/apt/sources.list
|
|
||||||
sudo apt clean
|
|
||||||
sudo apt autoclean
|
|
||||||
```
|
|
||||||
|
|
||||||
**Result:** Confirmed clean state, but error persisted
|
|
||||||
|
|
||||||
#### 3. Improved Fix (Commit 4fd78ec)
|
|
||||||
**Files Modified:** `privileged-setup`, `lib/privileges.sh`
|
|
||||||
**Approach:** Use `UBUNTU_CODENAME` from `/etc/os-release` with fallback to version mapping
|
|
||||||
```bash
|
|
||||||
# Primary method: Read UBUNTU_CODENAME from /etc/os-release
|
|
||||||
ubuntu_codename=$(grep '^UBUNTU_CODENAME=' /etc/os-release | cut -d= -f2)
|
|
||||||
|
|
||||||
# Fallback: Version mapping if UBUNTU_CODENAME not found
|
|
||||||
```
|
|
||||||
|
|
||||||
**Result:** Still experiencing same error after system cleanup
|
|
||||||
|
|
||||||
#### 4. Discovery of UBUNTU_CODENAME
|
|
||||||
**Key Finding:** Linux Mint 22.1 provides `UBUNTU_CODENAME=noble` in `/etc/os-release`
|
|
||||||
- This is the correct Ubuntu codename that Docker repositories support
|
|
||||||
- Should eliminate need for manual version mapping
|
|
||||||
|
|
||||||
#### 5. Root Cause Discovery - Wrong Installation Path (Session 2)
|
|
||||||
**Date:** July 19, 2025 (Evening)
|
|
||||||
**Discovery:** User was running `./setup` script, but fixes were applied to different code paths
|
|
||||||
**Investigation Steps:**
|
|
||||||
- Confirmed user had latest code with fixes (commit 4fd78ec)
|
|
||||||
- System was completely clean of Docker repositories
|
|
||||||
- `UBUNTU_CODENAME=noble` correctly detected
|
|
||||||
- Still getting "xia" error despite fixes
|
|
||||||
|
|
||||||
**Key Finding:** The `setup` script uses `lib/system.sh::install_docker()` which was calling Docker's convenience script `curl -fsSL https://get.docker.com | sh`, NOT the fixed installation functions in `privileged-setup` or `lib/privileges.sh`.
|
|
||||||
|
|
||||||
#### 6. Fix Applied to Correct Installation Path (Commit ce0f7f2)
|
|
||||||
**Files Modified:** `lib/system.sh` (lines 1122-1168)
|
|
||||||
**Approach:**
|
|
||||||
- Replaced Docker convenience script with manual repository setup
|
|
||||||
- Added Linux Mint Ubuntu codename detection logic to `lib/system.sh`
|
|
||||||
- Included same UBUNTU_CODENAME detection and fallback mapping
|
|
||||||
- Added debug output: "Using Ubuntu codename: X for Docker repository"
|
|
||||||
|
|
||||||
**Result:** User tested after pulling latest code - still experiencing same "xia" error
|
|
||||||
|
|
||||||
### Current Status (End of Session 2)
|
|
||||||
**Problem State:** Persistent "xia" repository error despite comprehensive fixes
|
|
||||||
**Fixes Applied:**
|
|
||||||
- Three different installation paths updated with Linux Mint detection
|
|
||||||
- Complete Docker repository cleanup performed multiple times
|
|
||||||
- Debug output added to track codename detection
|
|
||||||
- Manual testing confirmed UBUNTU_CODENAME=noble is available
|
|
||||||
|
|
||||||
**Unresolved Questions:**
|
|
||||||
1. Why debug output from fixed code is not appearing in installation logs
|
|
||||||
2. Whether there's a fourth Docker installation path not yet discovered
|
|
||||||
3. Possible system-level caching or existing Docker installation interfering
|
|
||||||
4. Whether the correct script path is actually being executed
|
|
||||||
|
|
||||||
### Next Steps (For Tomorrow)
|
|
||||||
1. **Execution Path Verification:** Add debug traces to determine which exact functions are being called during `./setup`
|
|
||||||
2. **Docker Installation Check:** Verify if Docker is already installed and causing early function returns
|
|
||||||
3. **Complete Docker Removal:** If Docker exists, completely remove it before testing fixes
|
|
||||||
4. **Alternative Installation Methods:** Test other entry points (`./hops`, `./install`, `./privileged-setup` directly)
|
|
||||||
5. **System State Analysis:** Check for any persistent apt configurations or cached repository information
|
|
||||||
|
|
||||||
### Technical Notes
|
|
||||||
- Linux Mint consistently provides `UBUNTU_CODENAME` in modern versions
|
|
||||||
- Using this field is more reliable than version-based mapping
|
|
||||||
- Docker installation uses Ubuntu repositories for Debian-based distributions
|
|
||||||
- Issue affects all Linux Mint installations using HOPS
|
|
||||||
- The Docker convenience script `get.docker.com` has its own broken Linux Mint detection
|
|
||||||
|
|
||||||
### Files Modified
|
|
||||||
- `privileged-setup` (lines 43-70, 72) - ✅ Fixed
|
|
||||||
- `lib/privileges.sh` (lines 199-226, 228) - ✅ Fixed
|
|
||||||
- `lib/system.sh` (lines 1122-1168) - ✅ Fixed
|
|
||||||
|
|
||||||
#### 7. Final Resolution (Session 3 - July 20, 2025)
|
|
||||||
**Root Cause Identified:** Critical case sensitivity bug in Linux Mint detection
|
|
||||||
- `lsb_release -is` returns `"Linuxmint"` (lowercase 'm')
|
|
||||||
- All code was checking for `"LinuxMint"` (uppercase 'M')
|
|
||||||
- This caused Linux Mint detection to fail completely, falling back to "xia" codename
|
|
||||||
|
|
||||||
**Final Fixes Applied:**
|
|
||||||
1. **Case Sensitivity Fix (Commit 736ed1b):**
|
|
||||||
- Fixed `lib/system.sh:1151`: `"LinuxMint"` → `"Linuxmint"`
|
|
||||||
- Fixed `privileged-setup:45`: `"LinuxMint"` → `"Linuxmint"`
|
|
||||||
- Fixed `lib/privileges.sh:201`: `"LinuxMint"` → `"Linuxmint"`
|
|
||||||
|
|
||||||
2. **Debug Tracing Added (Commit d2e9a69):**
|
|
||||||
- Added comprehensive debug output to trace execution paths
|
|
||||||
- Fixed Docker repository cleanup order in `remove_docker_linux()`
|
|
||||||
- Added specific cleanup for Linux Mint codenames (xia, vera, vanessa)
|
|
||||||
|
|
||||||
3. **Docker Service Issues (Manual Fix):**
|
|
||||||
- Created missing `docker` group: `sudo groupadd docker`
|
|
||||||
- Added user to docker group: `sudo usermod -aG docker skier`
|
|
||||||
- Started Docker services: `sudo systemctl start docker.socket docker`
|
|
||||||
|
|
||||||
4. **Directory Detection Fix (Commit a28a6e5):**
|
|
||||||
- Fixed sudo home directory resolution in `install` script
|
|
||||||
- Changed `$HOME/hops` to use `$SUDO_USER` home directory
|
|
||||||
- Resolved `/root/hops` vs `/home/skier/hops` issue
|
|
||||||
|
|
||||||
**Final Result:** ✅ **COMPLETE SUCCESS**
|
|
||||||
- Docker repositories now use correct Ubuntu codename "noble"
|
|
||||||
- Sonarr container deployed and running successfully
|
|
||||||
- Web UI accessible at localhost:8989
|
|
||||||
- All Linux Mint Docker repository issues resolved
|
|
||||||
|
|
||||||
### Current Status (RESOLVED)
|
|
||||||
**Problem State:** ✅ **COMPLETELY RESOLVED**
|
|
||||||
**Final Working State:**
|
|
||||||
- Linux Mint detection working: `DEBUG: Linux Mint detected, checking for UBUNTU_CODENAME`
|
|
||||||
- Ubuntu codename detection: `DEBUG: Found UBUNTU_CODENAME=noble in /etc/os-release`
|
|
||||||
- Repository configuration: `ℹ️ Using Ubuntu codename: noble for Docker repository`
|
|
||||||
- Docker installation: Downloads from `https://download.docker.com/linux/ubuntu noble`
|
|
||||||
- Service deployment: Sonarr running and accessible
|
|
||||||
|
|
||||||
### Git Commits
|
|
||||||
- `af57a77`: Initial Linux Mint version mapping fix
|
|
||||||
- `4fd78ec`: Improved fix using UBUNTU_CODENAME detection
|
|
||||||
- `ce0f7f2`: Fix lib/system.sh Docker installation path with manual repository setup
|
|
||||||
- `d2e9a69`: Fix Docker repository issues with debug tracing and cleanup order
|
|
||||||
- `736ed1b`: Fix critical Linux Mint case sensitivity bug in repository detection
|
|
||||||
- `a28a6e5`: Fix homelab directory detection when running with sudo
|
|
||||||
@@ -8,29 +8,15 @@ uninstall_hops() {
|
|||||||
set +e
|
set +e
|
||||||
|
|
||||||
# Script version for consistency
|
# Script version for consistency
|
||||||
local SCRIPT_VERSION="3.1.0-beta"
|
local _UNINSTALL_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Load shared utilities if not already loaded (allows standalone execution)
|
||||||
|
source "$_UNINSTALL_DIR/lib/common.sh"
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# LOGGING SETUP
|
# LOGGING SETUP
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
local LOG_DIR="/var/log/hops"
|
setup_logging "hops-uninstall"
|
||||||
local LOG_FILE="$LOG_DIR/hops-uninstall-$(date +%Y%m%d-%H%M%S).log"
|
|
||||||
mkdir -p "$LOG_DIR"
|
|
||||||
touch "$LOG_FILE"
|
|
||||||
|
|
||||||
log() {
|
|
||||||
echo -e "$(date '+%Y-%m-%d %T') - $1" | tee -a "$LOG_FILE"
|
|
||||||
}
|
|
||||||
|
|
||||||
error_exit() {
|
|
||||||
log "❌ ERROR: $1"
|
|
||||||
log "❌ Uninstallation failed. Check logs at: $LOG_FILE"
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
warning() {
|
|
||||||
log "⚠️ WARNING: $1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# --------------------------------------------
|
# --------------------------------------------
|
||||||
# HEADER
|
# HEADER
|
||||||
@@ -126,14 +112,14 @@ EOF
|
|||||||
find_homelab_directory() {
|
find_homelab_directory() {
|
||||||
local POSSIBLE_DIRS=(
|
local POSSIBLE_DIRS=(
|
||||||
"$HOME/hops"
|
"$HOME/hops"
|
||||||
"/home/*/hops"
|
|
||||||
"/opt/hops"
|
"/opt/hops"
|
||||||
"/srv/hops"
|
"/srv/hops"
|
||||||
)
|
)
|
||||||
|
while IFS= read -r d; do POSSIBLE_DIRS+=("$d"); done < <(compgen -G "/home/*/hops" 2>/dev/null)
|
||||||
|
|
||||||
# Try to find from running user's home first
|
|
||||||
if [[ -n "$SUDO_USER" ]]; then
|
if [[ -n "$SUDO_USER" ]]; then
|
||||||
local user_home=$(eval echo "~$SUDO_USER")
|
local user_home
|
||||||
|
user_home=$(getent passwd "$SUDO_USER" | cut -d: -f6)
|
||||||
POSSIBLE_DIRS=("$user_home/hops" "${POSSIBLE_DIRS[@]}")
|
POSSIBLE_DIRS=("$user_home/hops" "${POSSIBLE_DIRS[@]}")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
-173
@@ -1,173 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# HOPS User Script
|
|
||||||
# This script handles operations that can run as regular user
|
|
||||||
# Version: 3.1.0-beta
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Source common functions
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
||||||
source "$SCRIPT_DIR/lib/common.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/docker.sh"
|
|
||||||
source "$SCRIPT_DIR/lib/validation.sh"
|
|
||||||
|
|
||||||
# Initialize logging
|
|
||||||
setup_logging "user-operations"
|
|
||||||
|
|
||||||
# Check if user is in docker group
|
|
||||||
check_docker_access() {
|
|
||||||
if ! groups "$USER" | grep -q docker; then
|
|
||||||
error_exit "User not in docker group. Please run the privileged setup first and log out/in."
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! docker info >/dev/null 2>&1; then
|
|
||||||
error_exit "Cannot access Docker daemon. Please ensure Docker is running."
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Generate Docker Compose configuration
|
|
||||||
generate_docker_compose() {
|
|
||||||
local services=("$@")
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
info "📝 Generating Docker Compose configuration..."
|
|
||||||
|
|
||||||
# Create homelab directory
|
|
||||||
mkdir -p "$HOME/hops"
|
|
||||||
|
|
||||||
# Generate compose file header
|
|
||||||
cat > "$compose_file" << EOF
|
|
||||||
services:
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Generate service definitions
|
|
||||||
for service in "${services[@]}"; do
|
|
||||||
if "$SCRIPT_DIR/services-improved" generate "$service" >> "$compose_file"; then
|
|
||||||
success "Added service: $service"
|
|
||||||
else
|
|
||||||
error_exit "Failed to generate service definition for: $service"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
# Add networks section
|
|
||||||
cat >> "$compose_file" << EOF
|
|
||||||
|
|
||||||
networks:
|
|
||||||
homelab:
|
|
||||||
driver: bridge
|
|
||||||
traefik:
|
|
||||||
driver: bridge
|
|
||||||
database:
|
|
||||||
driver: bridge
|
|
||||||
EOF
|
|
||||||
|
|
||||||
success "Docker Compose configuration generated: $compose_file"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Deploy services
|
|
||||||
deploy_services() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "🚀 Deploying services..."
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
|
|
||||||
# Pull images
|
|
||||||
if docker compose pull; then
|
|
||||||
success "Docker images pulled successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to pull Docker images"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Start services
|
|
||||||
if docker compose up -d; then
|
|
||||||
success "Services deployed successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to deploy services"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Stop services
|
|
||||||
stop_services() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "🛑 Stopping services..."
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
|
|
||||||
if docker compose down; then
|
|
||||||
success "Services stopped successfully"
|
|
||||||
else
|
|
||||||
error_exit "Failed to stop services"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Show service status
|
|
||||||
show_service_status() {
|
|
||||||
local compose_file="$HOME/hops/docker-compose.yml"
|
|
||||||
|
|
||||||
if [[ ! -f "$compose_file" ]]; then
|
|
||||||
error_exit "Docker Compose file not found: $compose_file"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "📊 Service status:"
|
|
||||||
|
|
||||||
cd "$HOME/hops"
|
|
||||||
docker compose ps
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main user operations
|
|
||||||
main() {
|
|
||||||
local action="$1"
|
|
||||||
shift
|
|
||||||
|
|
||||||
# Check Docker access
|
|
||||||
check_docker_access
|
|
||||||
|
|
||||||
case "$action" in
|
|
||||||
"generate")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 generate <service1> [service2] ..."
|
|
||||||
fi
|
|
||||||
generate_docker_compose "$@"
|
|
||||||
;;
|
|
||||||
|
|
||||||
"deploy")
|
|
||||||
deploy_services
|
|
||||||
;;
|
|
||||||
|
|
||||||
"stop")
|
|
||||||
stop_services
|
|
||||||
;;
|
|
||||||
|
|
||||||
"status")
|
|
||||||
show_service_status
|
|
||||||
;;
|
|
||||||
|
|
||||||
"logs")
|
|
||||||
if [[ $# -eq 0 ]]; then
|
|
||||||
error_exit "Usage: $0 logs <service_name>"
|
|
||||||
fi
|
|
||||||
cd "$HOME/hops"
|
|
||||||
docker compose logs -f "$1"
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
error_exit "Unknown action: $action"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run if executed directly
|
|
||||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
||||||
main "$@"
|
|
||||||
fi
|
|
||||||
Reference in New Issue
Block a user