Files
arr-suite-template-bootstrap/bootstrap.sh
openhands 24f2cd64e9 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>
2025-11-28 04:26:12 +00:00

524 lines
14 KiB
Bash
Executable File

#!/bin/bash
# 🚀 All-in-One Bootstrap Script for *arr Media Stack
# This script sets up everything from a fresh OS install to a fully deployed media stack
#
# Usage: curl -sSL https://github.com/your-username/arr-suite-template/raw/branch/main/bootstrap.sh | bash
# Or: wget -qO- https://github.com/your-username/arr-suite-template/raw/branch/main/bootstrap.sh | bash
#
# Tested on: Ubuntu 20.04+, Debian 11+
# Requirements: Fresh VPS with sudo access
set -euo pipefail
# Configuration
REPO_URL="https://github.com/your-username/arr-suite-template.git"
INSTALL_DIR="/opt/arr-stack"
SERVICE_USER="arrstack"
PYTHON_VERSION="3.9"
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
PURPLE='\033[0;35m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# Logging functions
log() {
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1" >&2
}
success() {
echo -e "${GREEN}[SUCCESS]${NC} $1"
}
warning() {
echo -e "${YELLOW}[WARNING]${NC} $1"
}
info() {
echo -e "${CYAN}[INFO]${NC} $1"
}
step() {
echo -e "${PURPLE}[STEP]${NC} $1"
}
# Check if running as root
check_root() {
if [[ $EUID -eq 0 ]]; then
error "This script should not be run as root. Please run as a regular user with sudo privileges."
exit 1
fi
# Check sudo access
if ! sudo -n true 2>/dev/null; then
error "This script requires sudo privileges. Please ensure your user can use sudo."
exit 1
fi
}
# Detect OS and version
detect_os() {
if [[ -f /etc/os-release ]]; then
. /etc/os-release
OS=$ID
OS_VERSION=$VERSION_ID
else
error "Cannot detect OS. This script supports Ubuntu 20.04+ and Debian 11+"
exit 1
fi
log "Detected OS: $OS $OS_VERSION"
# Verify supported OS
case $OS in
ubuntu)
if [[ $(echo "$OS_VERSION >= 20.04" | bc -l) -eq 0 ]]; then
error "Ubuntu 20.04 or higher required. Found: $OS_VERSION"
exit 1
fi
;;
debian)
if [[ $(echo "$OS_VERSION >= 11" | bc -l) -eq 0 ]]; then
error "Debian 11 or higher required. Found: $OS_VERSION"
exit 1
fi
;;
*)
error "Unsupported OS: $OS. This script supports Ubuntu 20.04+ and Debian 11+"
exit 1
;;
esac
success "OS compatibility verified"
}
# Check system requirements
check_requirements() {
step "Checking system requirements..."
# Check memory (minimum 4GB)
MEMORY_GB=$(free -g | awk '/^Mem:/{print $2}')
if [[ $MEMORY_GB -lt 4 ]]; then
error "Minimum 4GB RAM required. Found: ${MEMORY_GB}GB"
exit 1
fi
# Check disk space (minimum 50GB)
DISK_GB=$(df / | awk 'NR==2{print int($4/1024/1024)}')
if [[ $DISK_GB -lt 50 ]]; then
error "Minimum 50GB free disk space required. Found: ${DISK_GB}GB"
exit 1
fi
# Check architecture
ARCH=$(uname -m)
if [[ $ARCH != "x86_64" ]]; then
error "x86_64 architecture required. Found: $ARCH"
exit 1
fi
success "System requirements met: ${MEMORY_GB}GB RAM, ${DISK_GB}GB disk, $ARCH"
}
# Update system packages
update_system() {
step "Updating system packages..."
sudo apt-get update -qq
sudo apt-get upgrade -y -qq
sudo apt-get install -y -qq \
curl \
wget \
git \
unzip \
software-properties-common \
apt-transport-https \
ca-certificates \
gnupg \
lsb-release \
bc \
jq \
htop \
nano \
vim \
ufw \
fail2ban
success "System packages updated"
}
# Install Python and pip
install_python() {
step "Installing Python $PYTHON_VERSION and pip..."
# Add deadsnakes PPA for newer Python versions on Ubuntu
if [[ $OS == "ubuntu" ]]; then
sudo add-apt-repository ppa:deadsnakes/ppa -y
sudo apt-get update -qq
fi
sudo apt-get install -y -qq \
python${PYTHON_VERSION} \
python${PYTHON_VERSION}-pip \
python${PYTHON_VERSION}-venv \
python${PYTHON_VERSION}-dev
# Create symlinks
sudo ln -sf /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python3
sudo ln -sf /usr/bin/python${PYTHON_VERSION} /usr/local/bin/python
# Install pip if not available
if ! command -v pip3 &> /dev/null; then
curl -sSL https://bootstrap.pypa.io/get-pip.py | sudo python${PYTHON_VERSION}
fi
success "Python $PYTHON_VERSION installed"
}
# Install Docker
install_docker() {
step "Installing Docker..."
# Remove old Docker versions
sudo apt-get remove -y -qq docker docker-engine docker.io containerd runc 2>/dev/null || true
# Add Docker GPG key
curl -fsSL https://download.docker.com/linux/$OS/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
# Add Docker repository
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/$OS $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# Install Docker
sudo apt-get update -qq
sudo apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-compose-plugin
# Start and enable Docker
sudo systemctl start docker
sudo systemctl enable docker
# Add user to docker group
sudo usermod -aG docker $USER
success "Docker installed and configured"
}
# Install Ansible
install_ansible() {
step "Installing Ansible..."
# Install Ansible via pip for latest version
python3 -m pip install --user ansible ansible-core
# Add to PATH
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
export PATH="$HOME/.local/bin:$PATH"
# Verify installation
if command -v ansible &> /dev/null; then
ANSIBLE_VERSION=$(ansible --version | head -n1 | cut -d' ' -f3)
success "Ansible $ANSIBLE_VERSION installed"
else
error "Ansible installation failed"
exit 1
fi
}
# Install Tailscale (optional but recommended)
install_tailscale() {
step "Installing Tailscale..."
# Add Tailscale repository
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/focal.list | sudo tee /etc/apt/sources.list.d/tailscale.list
# Install Tailscale
sudo apt-get update -qq
sudo apt-get install -y -qq tailscale
success "Tailscale installed (run 'sudo tailscale up' to connect)"
}
# Clone repository
clone_repository() {
step "Cloning arr-stack repository..."
# Create install directory
sudo mkdir -p $INSTALL_DIR
sudo chown $USER:$USER $INSTALL_DIR
# Clone repository
git clone $REPO_URL $INSTALL_DIR
cd $INSTALL_DIR
success "Repository cloned to $INSTALL_DIR"
}
# Setup configuration
setup_configuration() {
step "Setting up configuration files..."
cd $INSTALL_DIR
# Create inventory from example
if [[ ! -f inventory/production.yml ]]; then
cp inventory/production.yml.example inventory/production.yml
# Get server IP
SERVER_IP=$(curl -s ifconfig.me || curl -s ipinfo.io/ip || hostname -I | awk '{print $1}')
# Update inventory with current server details
sed -i "s/your_server_ip/$SERVER_IP/g" inventory/production.yml
sed -i "s/your_ssh_user/$USER/g" inventory/production.yml
info "Updated inventory with server IP: $SERVER_IP"
fi
# Create vault file from example
if [[ ! -f group_vars/all/vault.yml ]]; then
cp group_vars/all/vault.yml.example group_vars/all/vault.yml
info "Created vault.yml from example - you'll need to edit this with your credentials"
fi
success "Configuration files created"
}
# Configure firewall
configure_firewall() {
step "Configuring UFW firewall..."
# Reset UFW to defaults
sudo ufw --force reset
# Set default policies
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Allow SSH
sudo ufw allow ssh
# Allow Plex (public access)
sudo ufw allow 32400/tcp
# Allow Tailscale
sudo ufw allow in on tailscale0
# Enable firewall
sudo ufw --force enable
success "UFW firewall configured"
}
# Configure Fail2Ban
configure_fail2ban() {
step "Configuring Fail2Ban..."
# Create custom jail configuration
sudo tee /etc/fail2ban/jail.local > /dev/null << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 5
backend = systemd
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 86400
[plex]
enabled = true
port = 32400
filter = plex
logpath = /opt/arr-stack/logs/plex.log
maxretry = 5
bantime = 3600
EOF
# Create Plex filter
sudo tee /etc/fail2ban/filter.d/plex.conf > /dev/null << 'EOF'
[Definition]
failregex = .*Plex.*Failed login attempt.*<HOST>
ignoreregex =
EOF
# Restart Fail2Ban
sudo systemctl restart fail2ban
sudo systemctl enable fail2ban
success "Fail2Ban configured"
}
# Create service user
create_service_user() {
step "Creating service user..."
# Create user if it doesn't exist
if ! id "$SERVICE_USER" &>/dev/null; then
sudo useradd -r -s /bin/bash -d /home/$SERVICE_USER -m $SERVICE_USER
sudo usermod -aG docker $SERVICE_USER
# Set up sudo access for service management
echo "$SERVICE_USER ALL=(ALL) NOPASSWD: /bin/systemctl start arr-stack, /bin/systemctl stop arr-stack, /bin/systemctl restart arr-stack, /bin/systemctl status arr-stack" | sudo tee /etc/sudoers.d/arr-stack
success "Service user '$SERVICE_USER' created"
else
info "Service user '$SERVICE_USER' already exists"
fi
}
# Install monitoring tools
install_monitoring() {
step "Installing monitoring tools..."
# Install system monitoring
sudo apt-get install -y -qq \
htop \
iotop \
nethogs \
ncdu \
tree \
lsof \
strace
# Install Docker monitoring
sudo curl -L "https://github.com/bcicen/ctop/releases/download/v0.7.7/ctop-0.7.7-linux-amd64" -o /usr/local/bin/ctop
sudo chmod +x /usr/local/bin/ctop
success "Monitoring tools installed"
}
# Create helpful aliases and scripts
create_aliases() {
step "Creating helpful aliases and scripts..."
# Create aliases file
tee ~/.bash_aliases > /dev/null << 'EOF'
# *arr Stack Management Aliases
alias arr-status='cd /opt/arr-stack && docker compose ps'
alias arr-logs='cd /opt/arr-stack && docker compose logs -f'
alias arr-restart='cd /opt/arr-stack && docker compose restart'
alias arr-update='cd /opt/arr-stack && docker compose pull && docker compose up -d'
alias arr-deploy='cd /opt/arr-stack && ./deploy.sh'
alias arr-backup='cd /opt/arr-stack && ./scripts/backup.sh'
# System monitoring
alias sysinfo='echo "=== System Info ===" && uname -a && echo && echo "=== Memory ===" && free -h && echo && echo "=== Disk ===" && df -h && echo && echo "=== Docker ===" && docker system df'
alias containers='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
alias logs-tail='docker compose logs -f --tail=100'
# Network tools
alias myip='curl -s ifconfig.me'
alias ports='sudo netstat -tulpn | grep LISTEN'
alias vpn-status='docker exec gluetun curl -s ifconfig.me'
# Quick navigation
alias arr='cd /opt/arr-stack'
alias logs='cd /opt/arr-stack/logs'
alias configs='cd /opt/arr-stack/configs'
EOF
# Source aliases
echo 'source ~/.bash_aliases' >> ~/.bashrc
success "Helpful aliases created"
}
# Display final instructions
show_final_instructions() {
echo
echo "🎉 =============================================="
echo "🎉 *arr Media Stack Bootstrap Complete!"
echo "🎉 =============================================="
echo
echo "📋 Next Steps:"
echo
echo "1. 🔐 Configure your secrets:"
echo " cd $INSTALL_DIR"
echo " ansible-vault edit group_vars/all/vault.yml"
echo " # Add your VPN credentials and other secrets"
echo
echo "2. 🌐 Connect to Tailscale (recommended):"
echo " sudo tailscale up"
echo " # Follow the authentication link"
echo
echo "3. 🚀 Deploy the stack:"
echo " cd $INSTALL_DIR"
echo " ./deploy.sh"
echo
echo "4. 🔧 Access your services:"
echo " - Get your Tailscale IP: tailscale ip -4"
echo " - Prowlarr: http://TAILSCALE_IP:9696"
echo " - Sonarr: http://TAILSCALE_IP:8989"
echo " - Radarr: http://TAILSCALE_IP:7878"
echo " - Plex: http://$(curl -s ifconfig.me):32400"
echo
echo "📖 Documentation:"
echo " - Full guide: $INSTALL_DIR/ANSIBLE_DEPLOYMENT.md"
echo " - Configuration: $INSTALL_DIR/README.md"
echo
echo "🔧 Useful commands:"
echo " arr-status # Check container status"
echo " arr-logs # View logs"
echo " arr-restart # Restart services"
echo " arr-update # Update containers"
echo " sysinfo # System information"
echo
echo "⚠️ Important:"
echo " - Reboot or logout/login to apply group changes"
echo " - Configure your VPN credentials before deploying"
echo " - Set up indexers in Prowlarr after deployment"
echo
echo "🎯 Ready to deploy your media automation empire!"
echo
}
# Main execution
main() {
echo "🚀 Starting *arr Media Stack Bootstrap..."
echo "This will install and configure everything needed for your media stack."
echo
# Confirmation
read -p "Continue with installation? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
warning "Installation cancelled by user."
exit 0
fi
# Run all setup steps
check_root
detect_os
check_requirements
update_system
install_python
install_docker
install_ansible
install_tailscale
clone_repository
setup_configuration
configure_firewall
configure_fail2ban
create_service_user
install_monitoring
create_aliases
show_final_instructions
}
# Handle script interruption
trap 'error "Installation interrupted. You may need to clean up manually."; exit 1' INT TERM
# Run main function
main "$@"