Initial template repository

🎬 ARR Suite Template Bootstrap - Complete Media Automation Stack

Features:
- 16 production services (Prowlarr, Sonarr, Radarr, Plex, etc.)
- One-command Ansible deployment
- VPN-protected downloads via Gluetun
- Tailscale secure access
- Production-ready security (UFW, Fail2Ban)
- Automated backups and monitoring
- Comprehensive documentation

Ready for customization and deployment to any VPS.

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
openhands
2025-11-28 04:26:12 +00:00
commit 24f2cd64e9
71 changed files with 9983 additions and 0 deletions

221
scripts/backup.sh Executable file
View File

@@ -0,0 +1,221 @@
#!/bin/bash
# Synology Arrs Stack Backup Script
# This script creates backups of your Arrs configurations
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Load environment variables
load_env() {
if [ -f ".env" ]; then
set -a
source .env
set +a
print_status "Environment loaded from .env"
else
print_warning ".env file not found, using defaults"
CONFIG_ROOT="/volume1/docker"
fi
}
# Create backup
create_backup() {
local backup_date=$(date +"%Y%m%d_%H%M%S")
local backup_dir="backups/arrs_backup_$backup_date"
print_status "Creating backup directory: $backup_dir"
mkdir -p "$backup_dir"
# Backup each service configuration
local services=("sonarr" "radarr" "lidarr" "bazarr" "prowlarr")
for service in "${services[@]}"; do
local config_path="$CONFIG_ROOT/$service"
if [ -d "$config_path" ]; then
print_status "Backing up $service configuration..."
cp -r "$config_path" "$backup_dir/"
else
print_warning "$service configuration directory not found: $config_path"
fi
done
# Backup environment file
if [ -f ".env" ]; then
print_status "Backing up .env file..."
cp ".env" "$backup_dir/"
fi
# Backup docker-compose files
if [ -d "compose" ]; then
print_status "Backing up compose files..."
cp -r "compose" "$backup_dir/"
fi
# Create archive
print_status "Creating compressed archive..."
tar -czf "arrs_backup_$backup_date.tar.gz" -C backups "arrs_backup_$backup_date"
# Remove uncompressed backup
rm -rf "$backup_dir"
print_status "Backup created: arrs_backup_$backup_date.tar.gz"
# Show backup size
local backup_size=$(du -h "arrs_backup_$backup_date.tar.gz" | cut -f1)
print_status "Backup size: $backup_size"
}
# Restore backup
restore_backup() {
local backup_file="$1"
if [ -z "$backup_file" ]; then
print_error "Please specify a backup file to restore"
echo "Usage: $0 restore <backup_file.tar.gz>"
exit 1
fi
if [ ! -f "$backup_file" ]; then
print_error "Backup file not found: $backup_file"
exit 1
fi
print_warning "This will overwrite existing configurations!"
read -p "Are you sure you want to continue? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
print_status "Restore cancelled"
exit 0
fi
# Extract backup
local temp_dir="temp_restore_$(date +%s)"
mkdir -p "$temp_dir"
print_status "Extracting backup..."
tar -xzf "$backup_file" -C "$temp_dir"
# Find the backup directory
local backup_dir=$(find "$temp_dir" -name "arrs_backup_*" -type d | head -n1)
if [ -z "$backup_dir" ]; then
print_error "Invalid backup file structure"
rm -rf "$temp_dir"
exit 1
fi
# Restore configurations
local services=("sonarr" "radarr" "lidarr" "bazarr" "prowlarr")
for service in "${services[@]}"; do
if [ -d "$backup_dir/$service" ]; then
print_status "Restoring $service configuration..."
rm -rf "$CONFIG_ROOT/$service"
cp -r "$backup_dir/$service" "$CONFIG_ROOT/"
fi
done
# Restore .env file
if [ -f "$backup_dir/.env" ]; then
print_status "Restoring .env file..."
cp "$backup_dir/.env" "./"
fi
# Clean up
rm -rf "$temp_dir"
print_status "Restore completed successfully!"
print_warning "You may need to restart the containers for changes to take effect"
}
# List backups
list_backups() {
print_status "Available backups:"
echo ""
if ls arrs_backup_*.tar.gz 1> /dev/null 2>&1; then
for backup in arrs_backup_*.tar.gz; do
local size=$(du -h "$backup" | cut -f1)
local date=$(echo "$backup" | sed 's/arrs_backup_\([0-9]\{8\}_[0-9]\{6\}\).tar.gz/\1/' | sed 's/_/ /')
echo -e "${BLUE}$backup${NC} - Size: $size - Date: $date"
done
else
print_warning "No backups found"
fi
echo ""
}
# Clean old backups
clean_backups() {
local keep_days=${1:-30}
print_status "Cleaning backups older than $keep_days days..."
find . -name "arrs_backup_*.tar.gz" -mtime +$keep_days -delete
print_status "Old backups cleaned"
}
# Main execution
main() {
echo -e "${BLUE}=== Synology Arrs Stack Backup Tool ===${NC}"
echo ""
# Create backups directory
mkdir -p backups
load_env
case "${1:-backup}" in
"backup"|"create")
create_backup
;;
"restore")
restore_backup "$2"
;;
"list")
list_backups
;;
"clean")
clean_backups "$2"
;;
"help"|"-h"|"--help")
echo "Usage: $0 [command] [options]"
echo ""
echo "Commands:"
echo " backup, create Create a new backup (default)"
echo " restore <file> Restore from backup file"
echo " list List available backups"
echo " clean [days] Clean backups older than X days (default: 30)"
echo " help Show this help"
;;
*)
print_error "Unknown command: $1"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
}
# Run main function
main "$@"

263
scripts/logs.sh Executable file
View File

@@ -0,0 +1,263 @@
#!/bin/bash
# Synology Arrs Stack Log Viewer
# This script helps view and manage container logs
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Load environment variables
load_env() {
if [ -f ".env" ]; then
set -a
source .env
set +a
fi
}
# Check if docker-compose is available
check_docker_compose() {
if command -v docker-compose >/dev/null 2>&1; then
COMPOSE_CMD="docker-compose"
elif command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then
COMPOSE_CMD="docker compose"
else
print_error "Neither docker-compose nor docker compose found!"
echo "This script requires Docker Compose to be installed."
exit 1
fi
}
# Show all logs
show_all_logs() {
local follow=${1:-false}
local compose_file="compose/docker-compose.yml"
if [ "$follow" = "true" ]; then
print_status "Following logs for all containers (Ctrl+C to exit)..."
$COMPOSE_CMD -f "$compose_file" logs -f
else
print_status "Showing recent logs for all containers..."
$COMPOSE_CMD -f "$compose_file" logs --tail=100
fi
}
# Show logs for specific service
show_service_logs() {
local service="$1"
local follow=${2:-false}
local compose_file="compose/docker-compose.yml"
if [ -z "$service" ]; then
print_error "Please specify a service name"
echo "Available services: sonarr, radarr, lidarr, bazarr, prowlarr"
exit 1
fi
if [ "$follow" = "true" ]; then
print_status "Following logs for $service (Ctrl+C to exit)..."
$COMPOSE_CMD -f "$compose_file" logs -f "$service"
else
print_status "Showing recent logs for $service..."
$COMPOSE_CMD -f "$compose_file" logs --tail=100 "$service"
fi
}
# Show container status
show_status() {
local compose_file="compose/docker-compose.yml"
print_status "Container status:"
$COMPOSE_CMD -f "$compose_file" ps
echo ""
print_status "Container resource usage:"
if command -v docker >/dev/null 2>&1; then
docker stats --no-stream --format "table {{.Container}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.NetIO}}\t{{.BlockIO}}" \
sonarr radarr lidarr bazarr prowlarr 2>/dev/null || print_warning "Could not get resource usage"
fi
}
# Export logs to file
export_logs() {
local service="$1"
local compose_file="compose/docker-compose.yml"
local timestamp=$(date +"%Y%m%d_%H%M%S")
mkdir -p logs
if [ -z "$service" ]; then
# Export all logs
local log_file="logs/arrs_all_logs_$timestamp.txt"
print_status "Exporting all logs to $log_file..."
$COMPOSE_CMD -f "$compose_file" logs --no-color > "$log_file"
else
# Export specific service logs
local log_file="logs/${service}_logs_$timestamp.txt"
print_status "Exporting $service logs to $log_file..."
$COMPOSE_CMD -f "$compose_file" logs --no-color "$service" > "$log_file"
fi
print_status "Logs exported successfully!"
local file_size=$(du -h "$log_file" | cut -f1)
print_status "File size: $file_size"
}
# Interactive service selection
interactive_selection() {
echo ""
echo "Select a service to view logs:"
echo "1) All services"
echo "2) Sonarr"
echo "3) Radarr"
echo "4) Lidarr"
echo "5) Bazarr"
echo "6) Prowlarr"
echo ""
read -p "Enter choice [1]: " choice
choice=${choice:-1}
echo ""
echo "Log viewing options:"
echo "1) Show recent logs"
echo "2) Follow logs (live)"
echo "3) Export logs to file"
echo ""
read -p "Enter option [1]: " option
option=${option:-1}
local follow=false
if [ "$option" = "2" ]; then
follow=true
fi
case $choice in
1)
if [ "$option" = "3" ]; then
export_logs
else
show_all_logs "$follow"
fi
;;
2)
if [ "$option" = "3" ]; then
export_logs "sonarr"
else
show_service_logs "sonarr" "$follow"
fi
;;
3)
if [ "$option" = "3" ]; then
export_logs "radarr"
else
show_service_logs "radarr" "$follow"
fi
;;
4)
if [ "$option" = "3" ]; then
export_logs "lidarr"
else
show_service_logs "lidarr" "$follow"
fi
;;
5)
if [ "$option" = "3" ]; then
export_logs "bazarr"
else
show_service_logs "bazarr" "$follow"
fi
;;
6)
if [ "$option" = "3" ]; then
export_logs "prowlarr"
else
show_service_logs "prowlarr" "$follow"
fi
;;
*)
print_error "Invalid choice"
exit 1
;;
esac
}
# Main execution
main() {
echo -e "${BLUE}=== Synology Arrs Stack Log Viewer ===${NC}"
echo ""
load_env
check_docker_compose
case "${1:-}" in
"all")
show_all_logs "${2:-false}"
;;
"follow"|"-f")
if [ -n "$2" ]; then
show_service_logs "$2" true
else
show_all_logs true
fi
;;
"export")
export_logs "$2"
;;
"status")
show_status
;;
"sonarr"|"radarr"|"lidarr"|"bazarr"|"prowlarr")
show_service_logs "$1" "${2:-false}"
;;
"help"|"-h"|"--help")
echo "Usage: $0 [command] [options]"
echo ""
echo "Commands:"
echo " (no command) Interactive service selection"
echo " all [follow] Show logs for all services"
echo " follow, -f [svc] Follow logs (live) for service or all"
echo " export [service] Export logs to file"
echo " status Show container status and resource usage"
echo " <service> Show logs for specific service"
echo ""
echo "Services: sonarr, radarr, lidarr, bazarr, prowlarr"
echo ""
echo "Examples:"
echo " $0 # Interactive selection"
echo " $0 sonarr # Show Sonarr logs"
echo " $0 follow radarr # Follow Radarr logs"
echo " $0 export # Export all logs"
echo " $0 status # Show container status"
;;
"")
interactive_selection
;;
*)
print_error "Unknown command: $1"
echo "Use '$0 help' for usage information"
exit 1
;;
esac
}
# Run main function
main "$@"

232
scripts/setup.sh Executable file
View File

@@ -0,0 +1,232 @@
#!/bin/bash
# Synology Arrs Stack Setup Script
# This script helps set up the directory structure and environment for the Arrs stack
set -e
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Default values
DEFAULT_DATA_ROOT="/volume1/data"
DEFAULT_CONFIG_ROOT="/volume1/docker"
DEFAULT_PROJECT_PATH="/volume1/docker/projects/arrs-compose"
echo -e "${BLUE}=== Synology Arrs Stack Setup ===${NC}"
echo ""
# Function to print colored output
print_status() {
echo -e "${GREEN}[INFO]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
print_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Check if running on Synology
check_synology() {
if [ ! -f /etc/synoinfo.conf ]; then
print_warning "This doesn't appear to be a Synology system."
read -p "Continue anyway? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
}
# Get user input for configuration
get_user_config() {
echo -e "${BLUE}Configuration Setup${NC}"
echo "Please provide the following information:"
echo ""
# Get PUID and PGID
if command -v id >/dev/null 2>&1; then
if id dockerlimited >/dev/null 2>&1; then
DEFAULT_PUID=$(id -u dockerlimited)
DEFAULT_PGID=$(id -g dockerlimited)
print_status "Found dockerlimited user: PUID=$DEFAULT_PUID, PGID=$DEFAULT_PGID"
else
print_warning "dockerlimited user not found. Please create it first."
DEFAULT_PUID="1234"
DEFAULT_PGID="65432"
fi
else
DEFAULT_PUID="1234"
DEFAULT_PGID="65432"
fi
read -p "PUID (User ID) [$DEFAULT_PUID]: " PUID
PUID=${PUID:-$DEFAULT_PUID}
read -p "PGID (Group ID) [$DEFAULT_PGID]: " PGID
PGID=${PGID:-$DEFAULT_PGID}
# Get timezone
read -p "Timezone [Europe/London]: " TZ
TZ=${TZ:-"Europe/London"}
# Get paths
read -p "Data root directory [$DEFAULT_DATA_ROOT]: " DATA_ROOT
DATA_ROOT=${DATA_ROOT:-$DEFAULT_DATA_ROOT}
read -p "Config root directory [$DEFAULT_CONFIG_ROOT]: " CONFIG_ROOT
CONFIG_ROOT=${CONFIG_ROOT:-$DEFAULT_CONFIG_ROOT}
read -p "Project path [$DEFAULT_PROJECT_PATH]: " PROJECT_PATH
PROJECT_PATH=${PROJECT_PATH:-$DEFAULT_PROJECT_PATH}
}
# Create directory structure
create_directories() {
print_status "Creating directory structure..."
# Create data directories
mkdir -p "$DATA_ROOT/media/movies"
mkdir -p "$DATA_ROOT/media/tv"
mkdir -p "$DATA_ROOT/media/music"
mkdir -p "$DATA_ROOT/media/books"
mkdir -p "$DATA_ROOT/torrents/movies"
mkdir -p "$DATA_ROOT/torrents/tv"
mkdir -p "$DATA_ROOT/torrents/music"
mkdir -p "$DATA_ROOT/torrents/books"
# Create config directories
mkdir -p "$CONFIG_ROOT/sonarr"
mkdir -p "$CONFIG_ROOT/radarr"
mkdir -p "$CONFIG_ROOT/lidarr"
mkdir -p "$CONFIG_ROOT/bazarr"
mkdir -p "$CONFIG_ROOT/prowlarr"
mkdir -p "$PROJECT_PATH"
print_status "Directory structure created successfully!"
}
# Set permissions
set_permissions() {
print_status "Setting directory permissions..."
# Set ownership if dockerlimited user exists
if id dockerlimited >/dev/null 2>&1; then
chown -R dockerlimited:dockerlimited "$DATA_ROOT" 2>/dev/null || print_warning "Could not set ownership on $DATA_ROOT (may require sudo)"
chown -R dockerlimited:dockerlimited "$CONFIG_ROOT" 2>/dev/null || print_warning "Could not set ownership on $CONFIG_ROOT (may require sudo)"
else
print_warning "dockerlimited user not found. Skipping ownership changes."
fi
# Set permissions
chmod -R 755 "$DATA_ROOT" 2>/dev/null || print_warning "Could not set permissions on $DATA_ROOT (may require sudo)"
chmod -R 755 "$CONFIG_ROOT" 2>/dev/null || print_warning "Could not set permissions on $CONFIG_ROOT (may require sudo)"
print_status "Permissions set successfully!"
}
# Create .env file
create_env_file() {
print_status "Creating .env file..."
cat > .env << EOF
# Synology Arrs Stack Environment Configuration
# Generated by setup script on $(date)
# User and Group Configuration
PUID=$PUID
PGID=$PGID
# Timezone Configuration
TZ=$TZ
# Directory Paths
DATA_ROOT=$DATA_ROOT
CONFIG_ROOT=$CONFIG_ROOT
# Network Configuration
NETWORK_MODE=synobridge
# Port Configuration
SONARR_PORT=8989
RADARR_PORT=7878
LIDARR_PORT=8686
BAZARR_PORT=6767
PROWLARR_PORT=9696
# VPN Configuration (for docker-compose-vpn.yml)
VPN_PROVIDER=nordvpn
VPN_TYPE=openvpn
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password
VPN_COUNTRIES=Netherlands,Germany
EOF
print_status ".env file created successfully!"
}
# Copy docker-compose file to project directory
copy_compose_file() {
print_status "Copying docker-compose.yml to project directory..."
if [ -f "compose/docker-compose.yml" ]; then
cp "compose/docker-compose.yml" "$PROJECT_PATH/"
print_status "docker-compose.yml copied to $PROJECT_PATH/"
else
print_error "docker-compose.yml not found in compose/ directory"
return 1
fi
}
# Check prerequisites
check_prerequisites() {
print_status "Checking prerequisites..."
# Check if Container Manager is available
if [ -d "/var/packages/ContainerManager" ]; then
print_status "Container Manager package found"
else
print_warning "Container Manager package not found. Please install it from Package Center."
fi
# Check if synobridge network exists (this would require docker command)
print_status "Please ensure synobridge network is configured in Container Manager"
}
# Main execution
main() {
echo -e "${GREEN}Starting Synology Arrs Stack setup...${NC}"
echo ""
check_synology
get_user_config
create_directories
set_permissions
create_env_file
copy_compose_file
check_prerequisites
echo ""
echo -e "${GREEN}=== Setup Complete! ===${NC}"
echo ""
echo "Next steps:"
echo "1. Review and edit the .env file if needed"
echo "2. Ensure synobridge network is configured in Container Manager"
echo "3. Run './scripts/deploy.sh' to deploy the stack"
echo "4. Or manually create a project in Container Manager using:"
echo " - Project Name: media-stack"
echo " - Path: $PROJECT_PATH"
echo " - Use the docker-compose.yml file in that directory"
echo ""
echo -e "${BLUE}For more information, see the README.md file${NC}"
}
# Run main function
main "$@"