#!/bin/bash # ============================================================================= # Server Bootstrap Script # ============================================================================= # Prepares a fresh server with Docker, Tailscale, and essential tools. # Zero intervention required - just run and go. # # Supported: Ubuntu, Debian, Fedora, Rocky/Alma/RHEL, Arch, openSUSE # # Usage: # curl -fsSL /bootstrap.sh | sudo bash # # Options: # --no-tailscale Skip Tailscale installation # --no-firewall Skip firewall configuration # ============================================================================= set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' 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; } # Configuration SKIP_TAILSCALE=false SKIP_FIREWALL=false # 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 # Check root [[ $EUID -ne 0 ]] && error "Run as root: sudo bash bootstrap.sh" # Detect OS detect_os() { if [[ -f /etc/os-release ]]; then . /etc/os-release OS=$ID OS_VERSION=${VERSION_ID:-} OS_NAME=$NAME else error "Cannot detect OS" fi log "Detected: $OS_NAME $OS_VERSION" } # Install essential packages install_essentials() { log "Installing essential packages..." case $OS in 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 ;; 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 ;; *) warn "Unknown OS: $OS - skipping package installation" ;; esac success "Essential packages installed" } # Install Docker install_docker() { 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 install_tailscale() { $SKIP_TAILSCALE && return if command -v tailscale &>/dev/null; then success "Tailscale already installed" return fi log "Installing Tailscale..." curl -fsSL https://tailscale.com/install.sh | sh success "Tailscale installed - run 'sudo tailscale up' to connect" } # Configure firewall configure_firewall() { $SKIP_FIREWALL && return log "Configuring firewall..." 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 } # Create helpful aliases create_aliases() { log "Creating helpful aliases..." 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 info alias sysinfo='echo "=== System ===" && uname -a && echo && free -h && echo && df -h /' alias myip='curl -s ifconfig.me' alias ports='ss -tulpn | grep LISTEN' EOF chmod +x /etc/profile.d/server-aliases.sh success "Aliases created (reload shell to use)" } # Show completion message show_complete() { local IP=$(curl -s --max-time 5 ifconfig.me 2>/dev/null || hostname -I | awk '{print $1}') 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 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 "" } # Main main() { echo "" echo "========================================" echo " Server Bootstrap" echo "========================================" echo "" detect_os install_essentials install_docker install_tailscale configure_firewall create_aliases show_complete } main