Clean up - simple zero-intervention bootstrap
- Removed ansible, compose, docs, scripts, tasks, templates - Simplified bootstrap.sh for all major distros - Works on Ubuntu, Debian, Fedora, Rocky, Arch, openSUSE - Installs Docker, Tailscale, essential tools - Configures firewall automatically Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
732
bootstrap.sh
732
bootstrap.sh
@@ -1,524 +1,324 @@
|
||||
#!/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
|
||||
# =============================================================================
|
||||
# Server Bootstrap Script
|
||||
# =============================================================================
|
||||
# Prepares a fresh server with Docker, Tailscale, and essential tools.
|
||||
# Zero intervention required - just run and go.
|
||||
#
|
||||
# Tested on: Ubuntu 20.04+, Debian 11+
|
||||
# Requirements: Fresh VPS with sudo access
|
||||
# Supported: Ubuntu, Debian, Fedora, Rocky/Alma/RHEL, Arch, openSUSE
|
||||
#
|
||||
# Usage:
|
||||
# curl -fsSL <url>/bootstrap.sh | sudo bash
|
||||
#
|
||||
# Options:
|
||||
# --no-tailscale Skip Tailscale installation
|
||||
# --no-firewall Skip firewall configuration
|
||||
# =============================================================================
|
||||
|
||||
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
|
||||
# Colors
|
||||
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
|
||||
NC='\033[0m'
|
||||
|
||||
# Logging functions
|
||||
log() {
|
||||
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1"
|
||||
}
|
||||
log() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
||||
success() { echo -e "${GREEN}[OK]${NC} $1"; }
|
||||
warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
||||
error() { echo -e "${RED}[ERROR]${NC} $1" >&2; exit 1; }
|
||||
|
||||
error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
# Configuration
|
||||
SKIP_TAILSCALE=false
|
||||
SKIP_FIREWALL=false
|
||||
|
||||
success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
||||
}
|
||||
# Parse arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--no-tailscale) SKIP_TAILSCALE=true; shift ;;
|
||||
--no-firewall) SKIP_FIREWALL=true; shift ;;
|
||||
--help|-h)
|
||||
echo "Usage: bootstrap.sh [--no-tailscale] [--no-firewall]"
|
||||
exit 0
|
||||
;;
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
|
||||
warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1"
|
||||
}
|
||||
# Check root
|
||||
[[ $EUID -ne 0 ]] && error "Run as root: sudo bash bootstrap.sh"
|
||||
|
||||
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
|
||||
detect_os() {
|
||||
if [[ -f /etc/os-release ]]; then
|
||||
. /etc/os-release
|
||||
OS=$ID
|
||||
OS_VERSION=$VERSION_ID
|
||||
OS_VERSION=${VERSION_ID:-}
|
||||
OS_NAME=$NAME
|
||||
else
|
||||
error "Cannot detect OS. This script supports Ubuntu 20.04+ and Debian 11+"
|
||||
exit 1
|
||||
error "Cannot detect OS"
|
||||
fi
|
||||
log "Detected: $OS_NAME $OS_VERSION"
|
||||
}
|
||||
|
||||
# Install essential packages
|
||||
install_essentials() {
|
||||
log "Installing essential packages..."
|
||||
|
||||
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
|
||||
ubuntu|debian|linuxmint|pop)
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq \
|
||||
curl wget git unzip htop nano vim \
|
||||
ca-certificates gnupg lsb-release \
|
||||
jq tree ncdu lsof net-tools
|
||||
;;
|
||||
debian)
|
||||
if [[ $(echo "$OS_VERSION >= 11" | bc -l) -eq 0 ]]; then
|
||||
error "Debian 11 or higher required. Found: $OS_VERSION"
|
||||
exit 1
|
||||
fi
|
||||
fedora)
|
||||
dnf install -y -q \
|
||||
curl wget git unzip htop nano vim \
|
||||
ca-certificates gnupg \
|
||||
jq tree ncdu lsof net-tools
|
||||
;;
|
||||
rocky|almalinux|rhel|centos)
|
||||
dnf install -y -q epel-release 2>/dev/null || true
|
||||
dnf install -y -q \
|
||||
curl wget git unzip htop nano vim \
|
||||
ca-certificates gnupg \
|
||||
jq tree ncdu lsof net-tools
|
||||
;;
|
||||
arch|manjaro|endeavouros)
|
||||
pacman -Sy --noconfirm \
|
||||
curl wget git unzip htop nano vim \
|
||||
ca-certificates gnupg \
|
||||
jq tree ncdu lsof net-tools
|
||||
;;
|
||||
opensuse*|sles)
|
||||
zypper install -y \
|
||||
curl wget git unzip htop nano vim \
|
||||
ca-certificates \
|
||||
jq tree ncdu lsof net-tools
|
||||
;;
|
||||
*)
|
||||
error "Unsupported OS: $OS. This script supports Ubuntu 20.04+ and Debian 11+"
|
||||
exit 1
|
||||
warn "Unknown OS: $OS - skipping package installation"
|
||||
;;
|
||||
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"
|
||||
success "Essential packages 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
|
||||
if command -v docker &>/dev/null; then
|
||||
success "Docker already installed"
|
||||
return
|
||||
fi
|
||||
|
||||
log "Installing Docker..."
|
||||
|
||||
case $OS in
|
||||
ubuntu|debian|linuxmint|pop)
|
||||
# Remove old versions
|
||||
apt-get remove -y -qq docker docker-engine docker.io containerd runc 2>/dev/null || true
|
||||
|
||||
install -m 0755 -d /etc/apt/keyrings
|
||||
|
||||
# Determine base OS for Docker repo
|
||||
DOCKER_OS=$OS
|
||||
if [[ "$OS" == "linuxmint" || "$OS" == "pop" ]]; then
|
||||
DOCKER_OS="ubuntu"
|
||||
fi
|
||||
|
||||
curl -fsSL https://download.docker.com/linux/$DOCKER_OS/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null
|
||||
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||
|
||||
# Get codename
|
||||
if [[ -n "${VERSION_CODENAME:-}" ]]; then
|
||||
CODENAME=$VERSION_CODENAME
|
||||
else
|
||||
CODENAME=$(lsb_release -cs 2>/dev/null || echo "jammy")
|
||||
fi
|
||||
|
||||
# Map derivative codenames to Ubuntu
|
||||
case $OS in
|
||||
linuxmint|pop)
|
||||
case $OS_VERSION in
|
||||
20*|21.0|21.1) CODENAME="focal" ;;
|
||||
21.2|21.3|22*) CODENAME="jammy" ;;
|
||||
*) CODENAME="jammy" ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
|
||||
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/$DOCKER_OS $CODENAME stable" > /etc/apt/sources.list.d/docker.list
|
||||
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
;;
|
||||
|
||||
fedora)
|
||||
dnf remove -y -q docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 2>/dev/null || true
|
||||
dnf install -y -q dnf-plugins-core
|
||||
dnf config-manager --add-repo https://download.docker.com/linux/fedora/docker-ce.repo
|
||||
dnf install -y -q docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
;;
|
||||
|
||||
rocky|almalinux|rhel|centos)
|
||||
dnf remove -y -q docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine 2>/dev/null || true
|
||||
dnf install -y -q dnf-plugins-core
|
||||
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
|
||||
dnf install -y -q docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||
;;
|
||||
|
||||
arch|manjaro|endeavouros)
|
||||
pacman -Sy --noconfirm docker docker-compose
|
||||
;;
|
||||
|
||||
opensuse*|sles)
|
||||
zypper install -y docker docker-compose
|
||||
;;
|
||||
|
||||
*)
|
||||
error "Unsupported OS for Docker: $OS"
|
||||
;;
|
||||
esac
|
||||
|
||||
systemctl enable --now docker
|
||||
success "Docker installed"
|
||||
}
|
||||
|
||||
# Install Tailscale (optional but recommended)
|
||||
# Install Tailscale
|
||||
install_tailscale() {
|
||||
step "Installing Tailscale..."
|
||||
$SKIP_TAILSCALE && return
|
||||
|
||||
# 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"
|
||||
if command -v tailscale &>/dev/null; then
|
||||
success "Tailscale already installed"
|
||||
return
|
||||
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"
|
||||
|
||||
log "Installing Tailscale..."
|
||||
curl -fsSL https://tailscale.com/install.sh | sh
|
||||
success "Tailscale installed - run 'sudo tailscale up' to connect"
|
||||
}
|
||||
|
||||
# Configure firewall
|
||||
configure_firewall() {
|
||||
step "Configuring UFW firewall..."
|
||||
$SKIP_FIREWALL && return
|
||||
|
||||
# Reset UFW to defaults
|
||||
sudo ufw --force reset
|
||||
log "Configuring firewall..."
|
||||
|
||||
# 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"
|
||||
case $OS in
|
||||
ubuntu|debian|linuxmint|pop)
|
||||
if ! command -v ufw &>/dev/null; then
|
||||
apt-get install -y -qq ufw
|
||||
fi
|
||||
|
||||
ufw --force reset >/dev/null 2>&1
|
||||
ufw default deny incoming >/dev/null
|
||||
ufw default allow outgoing >/dev/null
|
||||
ufw allow ssh >/dev/null
|
||||
ufw allow 32400/tcp >/dev/null # Plex
|
||||
ufw allow 8096/tcp >/dev/null # Jellyfin
|
||||
ufw --force enable >/dev/null
|
||||
success "UFW firewall configured"
|
||||
;;
|
||||
|
||||
fedora|rocky|almalinux|rhel|centos)
|
||||
if command -v firewall-cmd &>/dev/null; then
|
||||
firewall-cmd --permanent --add-service=ssh >/dev/null 2>&1
|
||||
firewall-cmd --permanent --add-port=32400/tcp >/dev/null 2>&1 # Plex
|
||||
firewall-cmd --permanent --add-port=8096/tcp >/dev/null 2>&1 # Jellyfin
|
||||
firewall-cmd --reload >/dev/null 2>&1
|
||||
success "firewalld configured"
|
||||
fi
|
||||
;;
|
||||
|
||||
*)
|
||||
warn "Firewall configuration skipped for $OS"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# 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 helpful aliases
|
||||
create_aliases() {
|
||||
step "Creating helpful aliases and scripts..."
|
||||
log "Creating helpful aliases..."
|
||||
|
||||
# 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'
|
||||
cat > /etc/profile.d/server-aliases.sh << 'EOF'
|
||||
# Server management aliases
|
||||
alias dps='docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"'
|
||||
alias dlogs='docker compose logs -f'
|
||||
alias dstop='docker compose stop'
|
||||
alias dstart='docker compose up -d'
|
||||
alias drestart='docker compose restart'
|
||||
alias dupdate='docker compose pull && docker compose up -d'
|
||||
|
||||
# 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
|
||||
# System info
|
||||
alias sysinfo='echo "=== System ===" && uname -a && echo && free -h && echo && df -h /'
|
||||
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'
|
||||
alias ports='ss -tulpn | grep LISTEN'
|
||||
EOF
|
||||
|
||||
# Source aliases
|
||||
echo 'source ~/.bash_aliases' >> ~/.bashrc
|
||||
|
||||
success "Helpful aliases created"
|
||||
chmod +x /etc/profile.d/server-aliases.sh
|
||||
success "Aliases created (reload shell to use)"
|
||||
}
|
||||
|
||||
# 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
|
||||
# Show completion message
|
||||
show_complete() {
|
||||
local IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}')
|
||||
|
||||
# Confirmation
|
||||
read -p "Continue with installation? (y/N): " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
warning "Installation cancelled by user."
|
||||
exit 0
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " Server Bootstrap Complete!"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
echo "Installed:"
|
||||
echo " ✅ Docker & Docker Compose"
|
||||
if ! $SKIP_TAILSCALE; then
|
||||
echo " ✅ Tailscale (run 'sudo tailscale up' to connect)"
|
||||
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
|
||||
if ! $SKIP_FIREWALL; then
|
||||
echo " ✅ Firewall (SSH, Plex, Jellyfin allowed)"
|
||||
fi
|
||||
echo " ✅ Essential tools (htop, git, curl, etc.)"
|
||||
echo ""
|
||||
echo "Server IP: $IP"
|
||||
echo ""
|
||||
echo "Next steps:"
|
||||
echo " 1. Connect Tailscale: sudo tailscale up"
|
||||
echo " 2. Install arr-suite:"
|
||||
echo ""
|
||||
echo " # Plex version:"
|
||||
echo " curl -fsSL -H \"Authorization: token YOUR_TOKEN\" \\"
|
||||
echo " \"https://git.vish.gg/Vish/arr-suite/raw/branch/main/install.sh\" | sudo bash"
|
||||
echo ""
|
||||
echo " # Jellyfin version:"
|
||||
echo " curl -fsSL -H \"Authorization: token YOUR_TOKEN\" \\"
|
||||
echo " \"https://git.vish.gg/Vish/arr-suite-jellyfin/raw/branch/main/install.sh\" | sudo bash"
|
||||
echo ""
|
||||
echo "Helpful commands:"
|
||||
echo " dps - Show running containers"
|
||||
echo " dlogs - View container logs"
|
||||
echo " dupdate - Update all containers"
|
||||
echo " sysinfo - System information"
|
||||
echo " myip - Show public IP"
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Handle script interruption
|
||||
trap 'error "Installation interrupted. You may need to clean up manually."; exit 1' INT TERM
|
||||
# Main
|
||||
main() {
|
||||
echo ""
|
||||
echo "========================================"
|
||||
echo " Server Bootstrap"
|
||||
echo "========================================"
|
||||
echo ""
|
||||
|
||||
detect_os
|
||||
install_essentials
|
||||
install_docker
|
||||
install_tailscale
|
||||
configure_firewall
|
||||
create_aliases
|
||||
show_complete
|
||||
}
|
||||
|
||||
# Run main function
|
||||
main "$@"
|
||||
main
|
||||
|
||||
Reference in New Issue
Block a user