#!/usr/bin/env bash # NixPanel Installer # https://github.com/NixReaper/nixpanel set -euo pipefail RED='\033[0;31m'; GREEN='\033[0;32m'; CYAN='\033[0;36m'; YELLOW='\033[1;33m' BOLD='\033[1m'; RESET='\033[0m' info() { echo -e "${CYAN}[INFO]${RESET} $*"; } success() { echo -e "${GREEN}[OK]${RESET} $*"; } warn() { echo -e "${YELLOW}[WARN]${RESET} $*"; } error() { echo -e "${RED}[ERROR]${RESET} $*" >&2; exit 1; } step() { echo -e "\n${BOLD}${CYAN}━━━ $* ━━━${RESET}"; } # ── Preflight ────────────────────────────────────────────────────────────────── [[ $EUID -ne 0 ]] && error "Must be run as root (sudo bash install.sh)" OS_ID=$(. /etc/os-release && echo "$ID") OS_VERSION=$(. /etc/os-release && echo "$VERSION_ID") ARCH=$(uname -m) [[ "$OS_ID" != "ubuntu" ]] && error "NixPanel requires Ubuntu 24.04 LTS (detected: $OS_ID)" [[ "$OS_VERSION" != "24.04" ]] && error "NixPanel requires Ubuntu 24.04 LTS (detected: Ubuntu $OS_VERSION)" [[ "$ARCH" != "x86_64" ]] && error "NixPanel requires x86_64 architecture (detected: $ARCH)" INSTALL_DIR="/opt/nixpanel" DATA_DIR="/opt/nixpanel/data" LOG_DIR="/opt/nixpanel/logs" STEP_DIR="/var/lib/nixpanel/install" GITHUB_REPO="NixReaper/nixpanel" mkdir -p "$STEP_DIR" "$DATA_DIR" "$LOG_DIR" mark_done() { touch "$STEP_DIR/$1.done"; } step_done() { [[ -f "$STEP_DIR/$1.done" ]]; } echo "" echo -e "${BOLD}${CYAN}" echo " ███╗ ██╗██╗██╗ ██╗██████╗ █████╗ ███╗ ██╗███████╗██╗ " echo " ████╗ ██║██║╚██╗██╔╝██╔══██╗██╔══██╗████╗ ██║██╔════╝██║ " echo " ██╔██╗ ██║██║ ╚███╔╝ ██████╔╝███████║██╔██╗ ██║█████╗ ██║ " echo " ██║╚██╗██║██║ ██╔██╗ ██╔═══╝ ██╔══██║██║╚██╗██║██╔══╝ ██║ " echo " ██║ ╚████║██║██╔╝ ██╗██║ ██║ ██║██║ ╚████║███████╗███████╗" echo " ╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝" echo -e "${RESET}" echo -e " Web Hosting Control Panel · Ubuntu 24.04 · x86_64" echo "" # ── Helper: latest release version ──────────────────────────────────────────── get_latest_version() { curl -fsSL "https://api.github.com/repos/${GITHUB_REPO}/releases/latest" \ | grep '"tag_name"' | cut -d'"' -f4 } # ── System packages ──────────────────────────────────────────────────────────── step "Updating system packages" if ! step_done "apt_update"; then export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get upgrade -y -qq mark_done "apt_update" fi success "System packages up to date" step "Freeing port 53 for PowerDNS" if ! step_done "resolved_stub"; then sed -i 's/#DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf 2>/dev/null || true sed -i 's/^DNSStubListener=yes/DNSStubListener=no/' /etc/systemd/resolved.conf 2>/dev/null || true echo "DNSStubListener=no" >> /etc/systemd/resolved.conf systemctl restart systemd-resolved 2>/dev/null || true mark_done "resolved_stub" fi success "Port 53 free" step "Installing service stack" if ! step_done "packages"; then export DEBIAN_FRONTEND=noninteractive apt-get install -y -qq \ apache2 \ php8.3 php8.3-fpm php8.3-cli php8.3-mysql php8.3-curl php8.3-mbstring \ php8.3-xml php8.3-zip php8.3-gd php8.3-intl php8.3-bcmath \ php8.2 php8.2-fpm php8.2-cli php8.2-mysql php8.2-curl php8.2-mbstring \ php8.2-xml php8.2-zip php8.2-gd php8.2-intl php8.2-bcmath \ mariadb-server mariadb-client \ pdns-server pdns-backend-bind \ exim4 exim4-daemon-light \ dovecot-imapd dovecot-pop3d dovecot-lmtpd \ spamassassin spamc \ certbot python3-certbot-apache \ fail2ban \ curl wget git unzip software-properties-common \ openssl ca-certificates \ nodejs npm 2>/dev/null || true mark_done "packages" fi success "Service stack installed" # ── Apache ───────────────────────────────────────────────────────────────────── step "Configuring Apache" if ! step_done "apache"; then a2enmod rewrite ssl proxy proxy_http proxy_wstunnel proxy_fcgi headers expires deflate 2>/dev/null || true systemctl enable apache2 systemctl start apache2 mark_done "apache" fi success "Apache configured" # ── MariaDB ──────────────────────────────────────────────────────────────────── step "Configuring MariaDB" if ! step_done "mariadb"; then systemctl enable mariadb systemctl start mariadb mark_done "mariadb" fi success "MariaDB ready" # ── PowerDNS ─────────────────────────────────────────────────────────────────── step "Configuring PowerDNS" if ! step_done "pdns"; then systemctl enable pdns systemctl start pdns 2>/dev/null || true mark_done "pdns" fi success "PowerDNS ready" # ── SpamAssassin ─────────────────────────────────────────────────────────────── step "Configuring SpamAssassin" if ! step_done "spamd"; then systemctl enable spamd systemctl start spamd 2>/dev/null || true mark_done "spamd" fi success "SpamAssassin ready" # ── Download NixPanel release ────────────────────────────────────────────────── step "Downloading NixPanel" if ! step_done "download"; then VERSION=$(get_latest_version) [[ -z "$VERSION" ]] && error "Could not determine latest NixPanel version" info "Latest version: $VERSION" mkdir -p "$INSTALL_DIR" TMP_DIR=$(mktemp -d) TARBALL_URL="https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/nixpanel-${VERSION}-linux-x64.tar.gz" info "Downloading release tarball..." curl -fsSL "$TARBALL_URL" -o "$TMP_DIR/nixpanel.tar.gz" \ || error "Failed to download NixPanel $VERSION from GitHub Releases" tar -xzf "$TMP_DIR/nixpanel.tar.gz" -C "$TMP_DIR" cp -a "$TMP_DIR/nixpanel-${VERSION}"/. "$INSTALL_DIR/" rm -rf "$TMP_DIR" mark_done "download" fi success "NixPanel downloaded" # ── Environment ──────────────────────────────────────────────────────────────── step "Configuring environment" if ! step_done "env"; then if [[ ! -f "$INSTALL_DIR/.env" ]]; then JWT_SECRET=$(openssl rand -hex 32) REFRESH_SECRET=$(openssl rand -hex 32) DB_PASS=$(openssl rand -hex 16) cat > "$INSTALL_DIR/.env" </dev/null || true mark_done "db_init" fi success "Database ready" # ── Admin account ────────────────────────────────────────────────────────────── step "Creating admin account" if ! step_done "admin_account"; then echo "" echo -e " ${BOLD}Create your NixServer admin account${RESET}" read -rp " Admin username [admin]: " ADMIN_USER ADMIN_USER="${ADMIN_USER:-admin}" read -rsp " Admin password: " ADMIN_PASS echo "" read -rsp " Confirm password: " ADMIN_PASS2 echo "" [[ "$ADMIN_PASS" != "$ADMIN_PASS2" ]] && error "Passwords do not match" read -rp " Admin email: " ADMIN_EMAIL node "$INSTALL_DIR/scripts/create-admin.js" \ --username "$ADMIN_USER" \ --password "$ADMIN_PASS" \ --email "$ADMIN_EMAIL" \ || error "Failed to create admin account" mark_done "admin_account" fi success "Admin account ready" # ── Permissions ──────────────────────────────────────────────────────────────── step "Setting permissions" chmod 755 "$INSTALL_DIR" [[ -d "$INSTALL_DIR/nixserver/dist" ]] && { chmod 755 "$INSTALL_DIR/nixserver" find "$INSTALL_DIR/nixserver/dist" -type d -exec chmod 755 {} \; find "$INSTALL_DIR/nixserver/dist" -type f -exec chmod 644 {} \; } [[ -d "$INSTALL_DIR/nixclient/dist" ]] && { chmod 755 "$INSTALL_DIR/nixclient" find "$INSTALL_DIR/nixclient/dist" -type d -exec chmod 755 {} \; find "$INSTALL_DIR/nixclient/dist" -type f -exec chmod 644 {} \; } chmod 755 "$INSTALL_DIR/bin/nixpanel" 2>/dev/null || true success "Permissions set" # ── Systemd service ──────────────────────────────────────────────────────────── step "Installing systemd service" if ! step_done "systemd"; then cat > /etc/systemd/system/nixpanel.service <<'EOF' [Unit] Description=NixPanel Control Panel After=network.target mariadb.service [Service] Type=simple User=root WorkingDirectory=/opt/nixpanel ExecStart=/opt/nixpanel/bin/nixpanel Restart=always RestartSec=5 EnvironmentFile=/opt/nixpanel/.env [Install] WantedBy=multi-user.target EOF systemctl daemon-reload systemctl enable nixpanel systemctl start nixpanel mark_done "systemd" fi success "Service installed and started" # ── Apache vhosts ────────────────────────────────────────────────────────────── step "Configuring Apache reverse proxy" if ! step_done "apache_vhost"; then cat > /etc/apache2/sites-available/nixpanel.conf <<'EOF' # NixServer — Admin Panel (port 2087) ProxyPreserveHost On ProxyPass /api/ http://127.0.0.1:4000/api/ ProxyPassReverse /api/ http://127.0.0.1:4000/api/ ProxyPass /ws/ ws://127.0.0.1:4000/ws/ ProxyPassReverse /ws/ ws://127.0.0.1:4000/ws/ DocumentRoot /opt/nixpanel/nixserver/dist Options -Indexes AllowOverride All Require all granted FallbackResource /index.html # NixClient — User Panel (port 2083) ProxyPreserveHost On ProxyPass /api/ http://127.0.0.1:4000/api/ ProxyPassReverse /api/ http://127.0.0.1:4000/api/ DocumentRoot /opt/nixpanel/nixclient/dist Options -Indexes AllowOverride All Require all granted FallbackResource /index.html EOF echo "Listen 2083" >> /etc/apache2/ports.conf echo "Listen 2087" >> /etc/apache2/ports.conf a2ensite nixpanel 2>/dev/null || true apache2ctl configtest && systemctl reload apache2 mark_done "apache_vhost" fi success "Apache vhosts configured" # ── Done ─────────────────────────────────────────────────────────────────────── SERVER_IP=$(hostname -I | awk '{print $1}') echo "" echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo -e "${BOLD}${GREEN} NixPanel Installation Complete!${RESET}" echo -e "${BOLD}${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo "" echo -e " ${BOLD}NixServer (Admin):${RESET} http://${SERVER_IP}:2087" echo -e " ${BOLD}NixClient (User):${RESET} http://${SERVER_IP}:2083" echo "" echo -e " Open your browser and log in with the admin credentials you just created." echo ""