Installation

Production

Deploy XyraPanel on a bare-metal Ubuntu VPS using the official one-line installer.

Use the one-line installer

Run this as root on a fresh Ubuntu 22.04 / 24.04 or Debian 11 / 12 VPS:

bash <(curl -fsSL https://xyrapanel.com/install)

The installer prompts for your domain, admin credentials, and database password - then handles everything automatically: dependencies, database, build, PM2, Nginx, and TLS.

Point your domain's A record to the server IP before running the installer so Let's Encrypt can issue the certificate in one pass.

Install manually

Prefer full control? Follow these steps:

Install Node.js 22

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
sudo apt install -y nodejs git build-essential

Install pnpm

curl -fsSL https://github.com/pnpm/pnpm/releases/latest/download/pnpm-linux-x64 -o /usr/local/bin/pnpm
chmod +x /usr/local/bin/pnpm

Install PM2

npm install -g pm2@latest

Install PostgreSQL 16

curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc \
  | gpg --dearmor -o /usr/share/keyrings/postgresql.gpg
echo "deb [signed-by=/usr/share/keyrings/postgresql.gpg] https://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" \
  > /etc/apt/sources.list.d/pgdg.list
sudo apt update && sudo apt install -y postgresql-16
sudo systemctl enable --now postgresql
sudo -u postgres psql -c "CREATE USER xyrapanel WITH PASSWORD 'your_strong_password';"
sudo -u postgres psql -c "CREATE DATABASE xyrapanel OWNER xyrapanel;"

Install Redis

sudo apt install -y redis-server
sudo systemctl enable --now redis-server

Install Nginx and Certbot

sudo apt install -y nginx certbot python3-certbot-nginx
sudo systemctl enable nginx

Clone and configure

git clone --depth 1 https://github.com/XyraPanel/panel.git /opt/xyrapanel
cp /opt/xyrapanel/.env.example /opt/xyrapanel/.env

Edit /opt/xyrapanel/.env with your values:

.env
APP_NAME="XyraPanel"
NODE_ENV="production"
DATABASE_URL="postgresql://xyrapanel:your_strong_password@127.0.0.1:5432/xyrapanel"
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
BETTER_AUTH_URL=https://panel.yourdomain.com
BETTER_AUTH_TRUSTED_ORIGINS=https://panel.yourdomain.com
NUXT_PUBLIC_APP_URL=https://panel.yourdomain.com
XYRA_PASTE_URL=https://paste.xyrapanel.com
BETTER_AUTH_SECRET=        # openssl rand -base64 32
SEED_SECRET=               # openssl rand -base64 32
PANEL_PUBLIC_URL=https://panel.yourdomain.com
PANEL_INTERNAL_URL=http://127.0.0.1:3000
NUXT_SECURITY_CORS_ORIGIN=https://panel.yourdomain.com
NUXT_SECURITY_CONNECT_SRC=https://panel.yourdomain.com
NUXT_SECURITY_RATE_LIMIT_DRIVER=redis
NUXT_SECURITY_RATE_LIMIT_TOKENS=1000
NUXT_SECURITY_RATE_LIMIT_INTERVAL_MS=60000

Build the panel

cd /opt/xyrapanel
pnpm install --frozen-lockfile
pnpm run generate-pwa-assets
NODE_OPTIONS="--max-old-space-size=4096" pnpm build

Start with PM2

Create a launcher that loads .env before starting the server:

cat > /opt/xyrapanel/start.mjs << 'EOF'
import { readFileSync } from 'fs'
const lines = readFileSync('/opt/xyrapanel/.env', 'utf8').split('\n')
for (const line of lines) {
  const trimmed = line.trim()
  if (!trimmed || trimmed.startsWith('#')) continue
  const idx = trimmed.indexOf('=')
  if (idx === -1) continue
  const key = trimmed.slice(0, idx).trim()
  let val = trimmed.slice(idx + 1).trim()
  if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) val = val.slice(1, -1)
  if (!(key in process.env)) process.env[key] = val
}
await import('/opt/xyrapanel/.output/server/index.mjs')
EOF

pm2 start /opt/xyrapanel/start.mjs --name xyrapanel -i max
pm2 save
env PATH="$PATH:/usr/bin:/usr/local/bin" pm2 startup systemd -u root --hp /root | grep -E '^sudo|^env ' | bash

Configure Nginx

sudo mkdir -p /var/www/certbot
/etc/nginx/sites-available/xyrapanel
server {
    listen 80;
    server_name panel.yourdomain.com;
    location /.well-known/acme-challenge/ { root /var/www/certbot; }
    location / { return 301 https://$host$request_uri; }
}

server {
    listen 443 ssl;
    server_name panel.yourdomain.com;

    ssl_certificate     /etc/letsencrypt/live/panel.yourdomain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/panel.yourdomain.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;
    ssl_session_cache   shared:SSL:10m;
    ssl_session_timeout 10m;
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
    client_max_body_size 25M;
    proxy_read_timeout   300s;

    location / {
        proxy_pass         http://127.0.0.1:3000;
        proxy_http_version 1.1;
        proxy_set_header   Upgrade $http_upgrade;
        proxy_set_header   Connection 'upgrade';
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}
sudo ln -sf /etc/nginx/sites-available/xyrapanel /etc/nginx/sites-enabled/xyrapanel
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t && sudo systemctl reload nginx

Enable SSL

Follow the SSL Setup guide to issue a certificate and enable HTTPS.