Skip to content

Latest commit

 

History

History
1117 lines (839 loc) · 26.5 KB

File metadata and controls

1117 lines (839 loc) · 26.5 KB

Aladdin Video Editor — VPS Deployment Guide

Live on blacklyrics.tech


Table of Contents

  1. Prerequisites
  2. VPS Setup (Hetzner)
  3. Domain & DNS (Cloudflare)
  4. Server Initial Setup
  5. Install Dependencies
  6. Clone & Build Project
  7. Environment Variables
  8. Nginx Reverse Proxy
  9. SSL Certificate (Let's Encrypt)
  10. PM2 Process Manager
  11. Firewall & Security
  12. Verify Deployment
  13. Maintenance & Updates
  14. Troubleshooting

Architecture on blacklyrics.tech

Internet → Cloudflare DNS → blacklyrics.tech
                                   │
                            ┌──────▼──────┐
                            │    Nginx     │ ← SSL termination
                            │   :80/:443  │
                            └──────┬──────┘
                    ┌──────────────┼──────────────┐
                    │              │              │
             ┌──────▼──┐   ┌──────▼──┐   ┌──────▼──┐
             │Frontend  │   │ AI      │   │ Render  │
             │React     │   │ Backend │   │ Server  │
             │Router SSR│   │ FastAPI │   │Remotion │
             │ :5173    │   │ :3000   │   │ :8000   │
             └──────────┘   └─────────┘   └─────────┘

Routing:

URL Proxied To Service
blacklyrics.tech/* localhost:5173 Frontend (React Router SSR)
blacklyrics.tech/ai/* localhost:3000 AI Backend (FastAPI)
blacklyrics.tech/render/* localhost:8000 Render Server (Remotion + media)
blacklyrics.tech/media/* localhost:8000/media/ Media file serving

Prerequisites

Before starting, you need:

  • Domain: blacklyrics.tech purchased (Namecheap, Cloudflare Registrar, etc.)
  • VPS: Hetzner CCX33 or better (8+ dedicated vCPU, 16+ GB RAM)
  • Cloudflare account (free tier is fine)
  • GitHub repo access to your project
  • Groq API key for AI backend
  • Google OAuth credentials (for login)
  • PostgreSQL database (Supabase or self-hosted)

Step 1 — VPS Setup (Hetzner)

Order a VPS

  1. Go to Hetzner Cloud Console
  2. Create a new project → "Aladdin"
  3. Add a new server:
Setting Value
Location Falkenstein (EU) or Ashburn (US)
Image Ubuntu 24.04 LTS
Type CCX33 (8 dedicated vCPU, 32 GB RAM, 240 GB NVMe)
SSH Key Add your public key
Name aladdin-prod
  1. Click Create & Buy (~€51/month)

Note the Server IP

After creation, note your server's IP address (e.g., 65.21.xxx.xxx). You'll need this for DNS.


Step 2 — Domain & DNS (Cloudflare)

Add Domain to Cloudflare

  1. Go to Cloudflare Dashboard
  2. Add a site → Enter blacklyrics.tech
  3. Choose Free plan
  4. Cloudflare will show you nameservers (e.g., aria.ns.cloudflare.com, leo.ns.cloudflare.com)
  5. Go to your domain registrar → Update nameservers to Cloudflare's

Configure DNS Records

In Cloudflare DNS settings, add:

Type Name Content Proxy TTL
A @ 65.21.xxx.xxx (your VPS IP) ☁️ Proxied Auto
A www 65.21.xxx.xxx (your VPS IP) ☁️ Proxied Auto

Cloudflare SSL Settings

  1. Go to SSL/TLS → Set mode to Full (Strict)
  2. Go to SSL/TLS → Edge Certificates → Enable Always Use HTTPS
  3. Go to Speed → Optimization → Enable Auto Minify (JS, CSS, HTML)

Cloudflare Page Rules (Optional)

Rule Setting
blacklyrics.tech/media/* Cache Level: Standard, Edge TTL: 7 days
blacklyrics.tech/api/* Cache Level: Bypass

Step 3 — Server Initial Setup

SSH into your VPS

ssh root@65.21.xxx.xxx

Create a non-root user

adduser aladdin
usermod -aG sudo aladdin
# Copy SSH keys
rsync --archive --chown=aladdin:aladdin ~/.ssh /home/aladdin

Switch to new user

su - aladdin

Update system

sudo apt update && sudo apt upgrade -y
sudo apt install -y build-essential curl wget git unzip software-properties-common

Set timezone

sudo timedatectl set-timezone UTC

Set hostname

sudo hostnamectl set-hostname aladdin-prod

Step 4 — Install Dependencies

Node.js 20 LTS (via nvm)

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
source ~/.bashrc
nvm install 20
nvm use 20
nvm alias default 20
node -v  # Should show v20.x.x

pnpm

npm install -g pnpm
pnpm -v  # Should show 9.x.x

Python 3.12 + uv

sudo apt install -y python3.12 python3.12-venv python3-pip
# Install uv (fast Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc
uv --version

FFmpeg (required by Remotion)

sudo apt install -y ffmpeg
ffmpeg -version

Chromium (required by Remotion for headless rendering)

# Install Chromium dependencies (Remotion will download its own, but system deps are needed)
sudo apt install -y \
  libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 \
  libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
  libpango-1.0-0 libcairo2 libasound2t64 libxshmfence1 \
  fonts-liberation fonts-noto-color-emoji

# Verify
npx remotion browser ensure

PM2 (process manager)

npm install -g pm2
pm2 --version

Nginx

sudo apt install -y nginx
sudo systemctl enable nginx
sudo systemctl start nginx

Step 5 — Clone & Build Project

Clone repository

cd /home/aladdin
git clone https://github.com/YOUR_USERNAME/videoeditor.git aladdin
cd aladdin

Install frontend dependencies

pnpm install

Build frontend (production)

pnpm build

This outputs to build/ directory (React Router production build).

Install backend dependencies

cd backend
uv sync
cd ..

Create output directory for media

mkdir -p out
chmod 755 out

Copy fonts

# Ensure public/fonts directory has all required fonts
ls public/fonts/
# Should see: PakNastaleeq.ttf, NotoNastaliqUrdu.ttf, etc.

Step 6 — Environment Variables

Create .env file in project root

nano /home/aladdin/aladdin/.env

Paste:

# ==========================================
# Aladdin Production Environment Variables
# ==========================================

# Node
NODE_ENV=production

# Database (PostgreSQL — Supabase or self-hosted)
DATABASE_URL=postgresql://postgres:[PASSWORD]@db.[PROJECT_REF].supabase.co:6543/postgres?pgbouncer=true

# Google OAuth (for login)
GOOGLE_CLIENT_ID=your-google-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-google-client-secret

# Auth Configuration
AUTH_BASE_URL=https://blacklyrics.tech
AUTH_TRUSTED_ORIGINS=https://blacklyrics.tech,https://www.blacklyrics.tech
AUTH_COOKIE_DOMAIN=blacklyrics.tech

# Groq AI (backend)
GROQ_API_KEY=gsk_your_groq_api_key_here

# Render Server (internal communication)
UPLOAD_SERVICE_URL=http://localhost:8000/upload
RENDER_SERVICE_URL=http://localhost:8000

# Supabase (if using for waitlist on landing page)
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=your-supabase-anon-key

Create .env for backend

nano /home/aladdin/aladdin/backend/.env

Paste:

GROQ_API_KEY=gsk_your_groq_api_key_here

Set correct permissions

chmod 600 /home/aladdin/aladdin/.env
chmod 600 /home/aladdin/aladdin/backend/.env

Step 7 — Nginx Reverse Proxy

Create Nginx config

sudo nano /etc/nginx/sites-available/blacklyrics.tech

Paste the full configuration:

# Redirect www to non-www
server {
    listen 80;
    listen [::]:80;
    server_name www.blacklyrics.tech;
    return 301 https://blacklyrics.tech$request_uri;
}

# Main server block
server {
    listen 80;
    listen [::]:80;
    server_name blacklyrics.tech;

    # Max upload size (500MB for video uploads)
    client_max_body_size 500M;

    # Increase timeouts for video rendering (can take 15+ minutes)
    proxy_connect_timeout 60s;
    proxy_send_timeout 900s;
    proxy_read_timeout 900s;
    send_timeout 900s;

    # Security headers
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header Referrer-Policy "strict-origin-when-cross-origin" always;

    # ─── AI Backend (FastAPI) ───
    # /ai/* → localhost:3000
    location /ai/ {
        proxy_pass http://127.0.0.1:3000/;
        proxy_http_version 1.1;
        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;
        
        # CORS headers for AI endpoint
        add_header Access-Control-Allow-Origin "*" always;
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type, Authorization" always;
        
        if ($request_method = 'OPTIONS') {
            return 204;
        }
    }

    # ─── Render Server & Media ───
    # /render/* → localhost:8000
    location /render/ {
        proxy_pass http://127.0.0.1:8000/;
        proxy_http_version 1.1;
        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;
        
        # Long timeouts for rendering
        proxy_read_timeout 900s;
        proxy_send_timeout 900s;
        
        # Allow large video uploads
        client_max_body_size 500M;
    }

    # ─── Media Files (with caching) ───
    # /media/* → localhost:8000/media/
    location /media/ {
        proxy_pass http://127.0.0.1:8000/media/;
        proxy_http_version 1.1;
        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;

        # Cache media files aggressively
        proxy_cache_valid 200 7d;
        add_header Cache-Control "public, max-age=604800, immutable";
        add_header Access-Control-Allow-Origin "*" always;
    }

    # ─── Frontend (React Router SSR) ───
    # Everything else → localhost:5173 (production: 3000 via react-router-serve)
    location / {
        proxy_pass http://127.0.0.1:4173;
        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;
    }

    # ─── Static Assets ───
    location /assets/ {
        proxy_pass http://127.0.0.1:4173/assets/;
        proxy_http_version 1.1;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # ─── Fonts ───
    location /fonts/ {
        proxy_pass http://127.0.0.1:4173/fonts/;
        proxy_http_version 1.1;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    # Health check
    location /health {
        proxy_pass http://127.0.0.1:8000/health;
        proxy_http_version 1.1;
    }
}

Enable the site

sudo ln -s /etc/nginx/sites-available/blacklyrics.tech /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default  # Remove default site

Test & reload Nginx

sudo nginx -t
# Should say: syntax is ok, test is successful
sudo systemctl reload nginx

Step 8 — SSL Certificate (Let's Encrypt)

If using Cloudflare Proxy (☁️ orange cloud): Cloudflare handles SSL automatically. Skip to Option A. If not using Cloudflare Proxy: Use Let's Encrypt. Use Option B.

Option A: Cloudflare Origin Certificate (Recommended)

  1. In Cloudflare Dashboard → SSL/TLS → Origin Server
  2. Click Create Certificate
  3. Keep defaults (15 years, covers blacklyrics.tech and *.blacklyrics.tech)
  4. Copy the certificate and private key
sudo mkdir -p /etc/ssl/cloudflare
sudo nano /etc/ssl/cloudflare/blacklyrics.tech.pem
# Paste the certificate

sudo nano /etc/ssl/cloudflare/blacklyrics.tech.key
# Paste the private key

sudo chmod 600 /etc/ssl/cloudflare/blacklyrics.tech.key

Update Nginx to listen on 443:

sudo nano /etc/nginx/sites-available/blacklyrics.tech

Change the main server block:

server {
    listen 80;
    listen [::]:80;
    server_name blacklyrics.tech;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name blacklyrics.tech;

    ssl_certificate /etc/ssl/cloudflare/blacklyrics.tech.pem;
    ssl_certificate_key /etc/ssl/cloudflare/blacklyrics.tech.key;

    # ... rest of config (client_max_body_size, locations, etc.) ...
}
sudo nginx -t && sudo systemctl reload nginx

Option B: Let's Encrypt (Certbot)

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d blacklyrics.tech -d www.blacklyrics.tech
# Follow prompts, enter email, agree to ToS

# Auto-renewal (already set up by certbot, verify):
sudo certbot renew --dry-run

Step 9 — PM2 Process Manager

Create PM2 ecosystem config

nano /home/aladdin/aladdin/ecosystem.config.cjs

Paste:

module.exports = {
  apps: [
    // ─── Frontend (React Router SSR production server) ───
    {
      name: "frontend",
      script: "node_modules/.bin/react-router-serve",
      args: "./build/server/index.js",
      cwd: "/home/aladdin/aladdin",
      env: {
        NODE_ENV: "production",
        PORT: 4173,
      },
      instances: 2,           // 2 instances for load balancing
      exec_mode: "cluster",   // Cluster mode for multi-core
      max_memory_restart: "1G",
      watch: false,
      autorestart: true,
      max_restarts: 10,
      restart_delay: 5000,
    },

    // ─── Render Server (Remotion + Express) ───
    {
      name: "render-server",
      script: "node_modules/.bin/tsx",
      args: "app/videorender/videorender.ts",
      cwd: "/home/aladdin/aladdin",
      env: {
        NODE_ENV: "production",
        PORT: 8000,
      },
      instances: 1,            // Single instance (Remotion manages its own concurrency)
      exec_mode: "fork",
      max_memory_restart: "8G", // Render server can use lots of RAM
      watch: false,
      autorestart: true,
      max_restarts: 5,
      restart_delay: 10000,    // Wait 10s before restarting (rebundling takes time)
      kill_timeout: 30000,     // Give 30s to finish current render before kill
    },

    // ─── AI Backend (FastAPI via uvicorn) ───
    {
      name: "ai-backend",
      script: "uv",
      args: "run uvicorn main:app --host 127.0.0.1 --port 3000 --workers 2",
      cwd: "/home/aladdin/aladdin/backend",
      interpreter: "none",     // uv is the command, not a script
      env: {
        GROQ_API_KEY: "gsk_your_groq_api_key_here",
      },
      instances: 1,
      exec_mode: "fork",
      max_memory_restart: "2G",
      watch: false,
      autorestart: true,
      max_restarts: 10,
      restart_delay: 3000,
    },
  ],
};

Start all services

cd /home/aladdin/aladdin
pm2 start ecosystem.config.cjs

Verify all services are running

pm2 status

Expected output:

┌────┬──────────────────┬──────┬─────┬───────────┬──────────┬────────┐
│ id │ name             │ mode │ ↺   │ status    │ cpu      │ memory │
├────┼──────────────────┼──────┼─────┼───────────┼──────────┼────────┤
│ 0  │ frontend         │ cluster │ 0 │ online │ 0%       │ 80MB   │
│ 1  │ frontend         │ cluster │ 0 │ online │ 0%       │ 80MB   │
│ 2  │ render-server    │ fork │ 0   │ online    │ 0%       │ 300MB  │
│ 3  │ ai-backend       │ fork │ 0   │ online    │ 0%       │ 120MB  │
└────┴──────────────────┴──────┴─────┴───────────┴──────────┴────────┘

Save PM2 process list & auto-start on boot

pm2 save
pm2 startup
# Run the command it outputs (sudo env PATH=... pm2 startup systemd ...)

Useful PM2 Commands

pm2 logs                    # View all logs
pm2 logs frontend           # View frontend logs only
pm2 logs render-server      # View render server logs
pm2 logs ai-backend         # View AI backend logs
pm2 monit                   # Real-time monitoring dashboard
pm2 restart all             # Restart all services
pm2 restart render-server   # Restart only render server
pm2 reload frontend         # Zero-downtime reload frontend
pm2 stop all                # Stop all services

Step 10 — Firewall & Security

Configure UFW

sudo ufw default deny incoming
sudo ufw default allow outgoing

# Allow SSH
sudo ufw allow 22/tcp

# Allow HTTP and HTTPS
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

# Enable firewall
sudo ufw enable
sudo ufw status

Do NOT expose ports 3000, 4173, 8000 externally. Nginx proxies all traffic.

Secure SSH

sudo nano /etc/ssh/sshd_config

Set:

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
MaxAuthTries 3
sudo systemctl restart sshd

Install Fail2Ban

sudo apt install -y fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban

Set up automatic security updates

sudo apt install -y unattended-upgrades
sudo dpkg-reconfigure -plow unattended-upgrades
# Select "Yes"

Step 11 — Verify Deployment

Check all services are running

# PM2 status
pm2 status

# Test frontend
curl -I http://localhost:4173

# Test render server
curl http://localhost:8000/health

# Test AI backend
curl http://localhost:3000/docs

Check nginx is proxying correctly

# Test from outside via domain
curl -I https://blacklyrics.tech
# Should return 200 OK

curl -I https://blacklyrics.tech/health
# Should return render server health JSON

curl https://blacklyrics.tech/ai/docs
# Should return FastAPI OpenAPI docs

Test video upload

curl -X POST https://blacklyrics.tech/render/upload \
  -F "media=@test-video.mp4"
# Should return JSON with filename and URL

Check SSL

curl -vI https://blacklyrics.tech 2>&1 | grep -E "SSL|certificate|subject"
# Should show valid SSL certificate

Browser Test Checklist

Test URL Expected
Landing page https://blacklyrics.tech Landing page loads
Login https://blacklyrics.tech/login Google OAuth works
Editor https://blacklyrics.tech/home Editor loads with timeline
AI Chat Send message in chat AI responds with tool call
Upload video Drop video in media bin Upload succeeds
Render video Click render button Render starts and completes
Media playback Play video in timeline Video plays smoothly

Update the API URLs

Before deploying, update the production URLs in your code:

app/utils/api.ts

The URLs are already configured with a production/dev toggle. Update the production URLs:

// Current (needs update):
return isProduction ? "https://tryAladdin.com/ai/api" : "http://127.0.0.1:3000";
return isProduction ? "https://tryAladdin.com/render" : "http://localhost:8000";

// Change to:
return isProduction ? "https://blacklyrics.tech/ai" : "http://127.0.0.1:3000";
return isProduction ? "https://blacklyrics.tech/render" : "http://localhost:8000";

app/lib/auth.server.ts

Update trusted origins:

// Add to defaultTrustedOrigins:
"https://blacklyrics.tech",
"https://www.blacklyrics.tech",

app/routes/api.assets.$.tsx

Update the production upload URL:

// Change from:
? process.env.UPLOAD_SERVICE_URL || "https://localhost:8000/upload"
// Change to:
? process.env.UPLOAD_SERVICE_URL || "http://localhost:8000/upload"  // internal, no HTTPS needed

Google OAuth Console

Update in Google Cloud Console:

Setting Value
Authorized JavaScript origins https://blacklyrics.tech
Authorized redirect URIs https://blacklyrics.tech/api/auth/callback/google

Maintenance & Updates

Deploy new code

# SSH into server
ssh aladdin@65.21.xxx.xxx

cd /home/aladdin/aladdin

# Pull latest code
git pull origin main

# Install any new dependencies
pnpm install

# Rebuild frontend
pnpm build

# Install backend deps if changed
cd backend && uv sync && cd ..

# Restart services
pm2 restart frontend --update-env
pm2 restart render-server --update-env
pm2 restart ai-backend --update-env

Zero-downtime frontend deploy

# Uses PM2 cluster mode — reloads one instance at a time
pm2 reload frontend

View logs

# All logs
pm2 logs

# Follow specific service
pm2 logs render-server --lines 50

# Nginx access logs
sudo tail -f /var/log/nginx/access.log

# Nginx error logs
sudo tail -f /var/log/nginx/error.log

Disk cleanup (run weekly)

# Delete temp render files older than 24 hours
find /home/aladdin/aladdin/out -name "*.jpeg" -mtime +1 -delete
find /tmp/remotion-* -mtime +1 -exec rm -rf {} +
find /tmp/react-motion-* -mtime +1 -exec rm -rf {} +

# Check disk usage
df -h
du -sh /home/aladdin/aladdin/out/

Automate cleanup with cron

crontab -e

Add:

# Clean temp render files every 6 hours
0 */6 * * * find /tmp/remotion-* -mmin +360 -exec rm -rf {} + 2>/dev/null
0 */6 * * * find /tmp/react-motion-* -mmin +360 -exec rm -rf {} + 2>/dev/null

# Clean old media files older than 30 days
0 3 * * * find /home/aladdin/aladdin/out -name "TimelineComposition.mp4" -mtime +30 -delete 2>/dev/null

# PM2 log rotation (keep last 10MB per service)
0 0 * * * pm2 flush

Monitor server health

# Real-time dashboard
pm2 monit

# Memory usage
free -h

# CPU usage
htop

# Disk usage
df -h

Troubleshooting

Frontend not loading

# Check if frontend is running
pm2 logs frontend --lines 20

# Check if port 4173 is listening
ss -tlnp | grep 4173

# Rebuild if needed
pnpm build
pm2 restart frontend

Render server failing

# Check logs
pm2 logs render-server --lines 50

# Common issue: Chromium deps missing
sudo apt install -y libnss3 libatk1.0-0 libatk-bridge2.0-0 libcups2 \
  libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxrandr2 libgbm1 \
  libpango-1.0-0 libcairo2 libasound2t64

# Common issue: Out of memory
free -h
# If RAM is low, reduce Remotion concurrency in videorender.ts

# Restart
pm2 restart render-server

AI Backend not responding

# Check logs
pm2 logs ai-backend --lines 20

# Test directly
curl http://localhost:3000/docs

# Check Python env
cd /home/aladdin/aladdin/backend
uv run python -c "import fastapi; print(fastapi.__version__)"

502 Bad Gateway

# Nginx can't reach backend — check if services are running
pm2 status

# Check nginx error log
sudo tail -20 /var/log/nginx/error.log

# Restart everything
pm2 restart all
sudo systemctl reload nginx

SSL issues

# Check Cloudflare SSL mode is "Full (Strict)"
# Check origin certificate is installed correctly
sudo nginx -t
openssl x509 -in /etc/ssl/cloudflare/blacklyrics.tech.pem -text -noout | head -20

Slow renders on VPS

# Check CPU usage during render
htop  # Look for Chromium processes

# Check if swap is needed
free -h
# If no swap and RAM is maxing out:
sudo fallocate -l 4G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab

Quick Deploy Script

Save this as deploy.sh on the server for easy updates:

#!/bin/bash
set -e

echo "🚀 Deploying Aladdin to blacklyrics.tech..."

cd /home/aladdin/aladdin

echo "📥 Pulling latest code..."
git pull origin main

echo "📦 Installing frontend dependencies..."
pnpm install --frozen-lockfile

echo "🔨 Building frontend..."
pnpm build

echo "📦 Installing backend dependencies..."
cd backend && uv sync --frozen && cd ..

echo "♻️ Restarting services..."
pm2 reload frontend --update-env
pm2 restart render-server --update-env
pm2 restart ai-backend --update-env

echo "✅ Deployment complete!"
echo "🌐 https://blacklyrics.tech"
pm2 status
chmod +x deploy.sh
./deploy.sh

Summary Checklist

Step Status
VPS ordered (Hetzner CCX33+)
Domain DNS pointed to VPS IP
Cloudflare SSL configured
Node.js 20 + pnpm installed
Python 3.12 + uv installed
FFmpeg + Chromium deps installed
Project cloned and built
.env files created
Nginx configured and running
SSL certificate installed
PM2 running all 3 services
PM2 startup on boot configured
Firewall (UFW) enabled
SSH hardened (no root, no password)
API URLs updated for production
Google OAuth redirect updated
Frontend loads at https://blacklyrics.tech
AI chat works
Video upload works
Video render completes
Cron cleanup jobs configured

Document generated: February 8, 2026 Aladdin Video Editor — blacklyrics.tech Deployment Guide