Files
yellow-bank-soal/AAPANEL_DEPLOYMENT.md
Dwindi Ramadhana cf193d7ea0 first commit
2026-03-21 23:32:59 +07:00

953 lines
22 KiB
Markdown

# IRT Bank Soal - AaPanel Deployment Guide
**Document Version:** 1.1
**Date:** March 21, 2026
**Project:** IRT-Powered Adaptive Question Bank System v1.2.0
**Updated:** Clarified PostgreSQL setup using Databases > PgSQL menu
---
## Table of Contents
1. [Prerequisites](#1-prerequisites)
2. [AaPanel Installation](#2-aapanel-installation)
3. [Install Required Software via AaPanel](#3-install-required-software-via-aapanel)
4. [PostgreSQL Setup](#4-postgresql-setup)
5. [Python Manager Setup](#5-python-manager-setup)
6. [Project Deployment](#6-project-deployment)
7. [Environment Configuration](#7-environment-configuration)
8. [Database Migration](#8-database-migration)
9. [Running the Application](#9-running-the-application)
10. [Nginx Reverse Proxy Configuration](#10-nginx-reverse-proxy-configuration)
11. [SSL Configuration](#11-ssl-configuration)
12. [Post-Deployment Verification](#12-post-deployment-verification)
13. [Troubleshooting](#13-troubleshooting)
---
## 1. Prerequisites
### Server Requirements
| Requirement | Minimum | Recommended |
|-------------|---------|-------------|
| OS | Ubuntu 20.04 / CentOS 7+ | Ubuntu 22.04 LTS |
| RAM | 2 GB | 4 GB+ |
| Storage | 20 GB | 50 GB+ |
| CPU | 1 vCPU | 2+ vCPU |
### Domain Requirements
- A domain name pointed to your server IP
- Subdomain recommended (e.g., `api.yourdomain.com`)
---
## 2. AaPanel Installation
### Step 2.1: Install AaPanel
**For Ubuntu/Debian:**
```bash
# Login to your server via SSH
ssh root@your-server-ip
# Install AaPanel
wget -O install.sh http://www.aapanel.com/script/install-ubuntu_6.0_en.sh && bash install.sh
```
**For CentOS:**
```bash
# Install AaPanel
yum install -y wget && wget -O install.sh http://www.aapanel.com/script/install_6.0_en.sh && sh install.sh
```
### Step 2.2: Access AaPanel
1. After installation completes, note the panel URL and credentials
2. Access AaPanel via browser: `http://your-server-ip:8888`
3. Login with provided credentials
4. **Important:** Change default port and password after first login
---
## 3. Install Required Software via AaPanel
### Step 3.1: Install Nginx
1. In AaPanel, go to **App Store**
2. Find **Nginx** and click **Install**
3. Select version (recommended: 1.24+)
4. Click **Submit** and wait for installation
### Step 3.2: Install Python Manager
1. Go to **App Store**
2. Search for **Python Manager** (or **PM2 Manager**)
3. Click **Install**
### Step 3.3: Install Redis (Optional, for Celery)
1. Go to **App Store**
2. Find **Redis** and click **Install**
3. Click **Submit**
---
## 4. PostgreSQL Setup
> **IMPORTANT:** Use **Databases > PgSQL** menu from AaPanel sidebar.
>
> This menu supports both:
> - **Local server** - PostgreSQL installed on your AaPanel server
> - **Remote server** - External PostgreSQL (Supabase, Neon, AWS RDS, etc.)
### Step 4.1: Choose Your Database Type
You have two options:
| Option | Description | Best For |
|--------|-------------|----------|
| **Remote Database** | External PostgreSQL service (Supabase, Neon, etc.) | Easy setup, managed, free tier available |
| **Local Database** | PostgreSQL on your AaPanel server | Full control, no external dependency |
---
### Option A: Remote PostgreSQL Database (RECOMMENDED)
Use an external PostgreSQL service:
- **Supabase** - https://supabase.com (free tier: 500MB)
- **Neon** - https://neon.tech (free tier: 3GB)
- **AWS RDS** - https://aws.amazon.com/rds/postgresql/
- **DigitalOcean** - https://www.digitalocean.com/products/managed-databases-postgresql
- **Railway** - https://railway.app
#### Step 4.A.1: Create Database on Provider
1. Sign up on your chosen provider
2. Create a new PostgreSQL project/database
3. Note down the connection details from dashboard:
- **Host** (e.g., `db.xxxxx.supabase.co` or `ep-xxx.us-east-2.aws.neon.tech`)
- **Port** (usually `5432`, Supabase uses `6543` for pooler)
- **Database name** (e.g., `postgres` or `neondb`)
- **Username** (e.g., `postgres.xxxxx`)
- **Password**
#### Step 4.A.2: Add Remote Server to AaPanel PgSQL
1. In AaPanel, go to **Databases** > **PgSQL**
2. Click **Remote DB** button
3. Fill in the form:
- **Server Name:** `my-remote-db` (any name you like)
- **Server Address:** `db.xxxxx.supabase.co` (your host)
- **Port:** `5432` or `6543` (check your provider)
- **Root User:** `postgres` or your username
- **Root Password:** your password
4. Click **Submit**
#### Step 4.A.3: Sync Databases from Remote Server
1. After adding remote server, click **Get DB from server**
2. Select your remote server from dropdown
3. Click **Submit**
4. Your remote databases will appear in the list
#### Step 4.A.4: Note Your Connection String
Your connection string format:
```
postgresql+asyncpg://username:password@host:port/database_name
```
**Example (Supabase):**
```
postgresql+asyncpg://postgres.xxxxx:YourPassword@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres
```
**Example (Neon):**
```
postgresql+asyncpg://neondb_owner:YourPassword@ep-xxxx.us-east-2.aws.neon.tech/neondb?sslmode=require
```
---
### Option B: Local PostgreSQL Database
Install PostgreSQL directly on your AaPanel server.
#### Step 4.B.1: Install PostgreSQL via Terminal
```bash
# SSH into your server
ssh root@your-server-ip
# Ubuntu/Debian
apt update
apt install -y postgresql postgresql-contrib
# Start and enable PostgreSQL
systemctl start postgresql
systemctl enable postgresql
# Check status
systemctl status postgresql
```
#### Step 4.B.2: Create Database and User via Terminal
```bash
# Switch to postgres user
su - postgres
# Enter PostgreSQL CLI
psql
# Run SQL commands:
CREATE DATABASE irt_bank_soal;
CREATE USER irt_user WITH ENCRYPTED PASSWORD 'your_secure_password_here';
GRANT ALL PRIVILEGES ON DATABASE irt_bank_soal TO irt_user;
# Connect to database and grant schema
\c irt_bank_soal
GRANT ALL ON SCHEMA public TO irt_user;
# Exit
\q
exit
```
#### Step 4.B.3: Add Local Server to AaPanel PgSQL
1. In AaPanel, go to **Databases** > **PgSQL**
2. Click **Root Password** to view/change postgres password
3. If your local PostgreSQL is not showing, click **Get DB from server**
4. Select **Local server**
5. Click **Submit**
#### Step 4.B.4: Create Additional Database via AaPanel (Optional)
1. In **Databases** > **PgSQL**
2. Click **Add DB**
3. Fill in:
- **Database name:** `irt_bank_soal`
- **Username:** `irt_user` (or same as DB name)
- **Password:** (click generate or enter custom)
- **Add to:** `Local server`
4. Click **Submit**
#### Step 4.B.5: Note Your Connection String
```
postgresql+asyncpg://irt_user:your_password@127.0.0.1:5432/irt_bank_soal
```
---
## 4.1 Test Database Connection
Before proceeding, verify your database connection works.
### For Remote Database:
```bash
# Install psql client if needed
apt install -y postgresql-client
# Test connection (replace with your details)
psql "postgresql://username:password@host:port/database_name" -c "SELECT version();"
```
### For Local Database:
```bash
# Test connection
psql -U irt_user -d irt_bank_soal -h 127.0.0.1 -c "SELECT version();"
# If prompted for password, enter it
```
---
## 4.2 Connection String Quick Reference
| Database Type | Connection String Format |
|---------------|-------------------------|
| **Remote (Supabase)** | `postgresql+asyncpg://postgres.xxxx:password@aws-0-region.pooler.supabase.com:6543/postgres` |
| **Remote (Neon)** | `postgresql+asyncpg://user:password@ep-xxxx.region.aws.neon.tech/neondb?sslmode=require` |
| **Local** | `postgresql+asyncpg://irt_user:password@127.0.0.1:5432/irt_bank_soal` |
> **Note:** We use `postgresql+asyncpg://` because our app uses async SQLAlchemy with `asyncpg` driver.
---
## 5. Python Manager Setup
### Step 5.1: Open Python Manager
1. In AaPanel, go to **App Store**
2. Find **Python Manager** and click **Settings**
### Step 5.2: Install Python Version
1. Click **Version Management**
2. Select **Python 3.11** (or latest stable)
3. Click **Install**
4. Wait for installation to complete
---
## 6. Project Deployment
### Step 6.1: Create Project Directory
```bash
# Create project directory
mkdir -p /www/wwwroot/irt-bank-soal
# Navigate to directory
cd /www/wwwroot/irt-bank-soal
```
### Step 6.2: Upload Project Files
**Option A: Upload via File Manager**
1. In AaPanel, go to **Files**
2. Navigate to `/www/wwwroot/irt-bank-soal`
3. Upload your project ZIP file
4. Extract the archive
**Option B: Clone from Git (if applicable)**
```bash
cd /www/wwwroot/irt-bank-soal
# If using Git
git clone https://github.com/your-repo/irt-bank-soal.git .
# Or copy from local
# scp -r /Users/dwindown/Applications/tryout-system/* root@your-server-ip:/www/wwwroot/irt-bank-soal/
```
### Step 6.3: Verify Project Structure
```bash
# Expected structure:
ls -la /www/wwwroot/irt-bank-soal/
# app/
# app/models/
# app/routers/
# app/services/
# app/core/
# tests/
# requirements.txt
# .env.example
# alembic/
```
---
## 7. Environment Configuration
### Step 7.1: Create Virtual Environment via Python Manager
1. In AaPanel **Python Manager**, click **Add Project**
2. Configure:
- **Project Name:** `irt-bank-soal`
- **Project Path:** `/www/wwwroot/irt-bank-soal`
- **Python Version:** `Python 3.11`
- **Framework:** `FastAPI`
- **Startup Method:** `uvicorn`
3. Click **Submit**
### Step 7.2: Create Environment File
```bash
# Copy example file
cp /www/wwwroot/irt-bank-soal/.env.example /www/wwwroot/irt-bank-soal/.env
# Edit .env file
nano /www/wwwroot/irt-bank-soal/.env
```
### Step 7.3: Configure .env File
```env
# Database Configuration
# For Remote Database (Supabase example):
# DATABASE_URL=postgresql+asyncpg://postgres.xxxx:password@aws-0-ap-southeast-1.pooler.supabase.com:6543/postgres
# For Remote Database (Neon example):
# DATABASE_URL=postgresql+asyncpg://neondb_owner:password@ep-xxxx.us-east-2.aws.neon.tech/neondb?sslmode=require
# For Local Database:
DATABASE_URL=postgresql+asyncpg://irt_user:your_secure_password_here@127.0.0.1:5432/irt_bank_soal
# Security
SECRET_KEY=your-production-secret-key-min-32-characters-random-string
# Environment
ENVIRONMENT=production
DEBUG=false
# API Configuration
API_V1_STR=/api/v1
PROJECT_NAME=IRT Bank Soal
PROJECT_VERSION=1.2.0
# CORS - Add your WordPress domains
ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
# OpenRouter API (for AI Generation)
OPENROUTER_API_KEY=your-openrouter-api-key-here
OPENROUTER_API_URL=https://openrouter.ai/api/v1
OPENROUTER_MODEL_QWEN=qwen/qwen-2.5-coder-32b-instruct
OPENROUTER_MODEL_LLAMA=meta-llama/llama-3.3-70b-instruct
OPENROUTER_TIMEOUT=60
# WordPress Integration
WORDPRESS_API_URL=https://yourdomain.com/wp-json
WORDPRESS_AUTH_TOKEN=your-wordpress-jwt-token
# Redis (for Celery task queue)
REDIS_URL=redis://127.0.0.1:6379/0
# Admin Panel
ADMIN_USER=admin
ADMIN_PASSWORD=your-secure-admin-password
# Normalization Defaults
DEFAULT_RATAAN=500
DEFAULT_SB=100
MIN_SAMPLE_FOR_DYNAMIC=100
```
### Step 7.4: Generate Secret Key
```bash
# Generate a secure secret key
python3 -c "import secrets; print(secrets.token_urlsafe(32))"
# Copy the output and paste into SECRET_KEY in .env
```
---
## 8. Database Migration
### Step 8.1: Activate Virtual Environment
```bash
# Via Python Manager, the venv is usually at:
source /www/wwwroot/irt-bank-soal/venv/bin/activate
# Or check Python Manager for exact venv path
```
### Step 8.2: Install Dependencies
```bash
# Ensure you're in project directory
cd /www/wwwroot/irt-bank-soal
# Install dependencies
pip install -r requirements.txt
# Verify installation
pip list | grep -E "fastapi|sqlalchemy|numpy|scipy|httpx|openpyxl"
```
### Step 8.3: Initialize Alembic (First Time Setup)
```bash
# Initialize Alembic if not already done
alembic init alembic
# Generate initial migration
alembic revision --autogenerate -m "Initial migration"
# Apply migration
alembic upgrade head
```
### Step 8.4: Verify Database Tables
```bash
# Check tables were created
psql -U irt_user -d irt_bank_soal -h 127.0.0.1 -c "\dt"
# Expected output: websites, users, tryouts, items, sessions, user_answers, tryout_stats
```
---
## 9. Running the Application
### Step 9.1: Configure Python Project in AaPanel
1. In **Python Manager**, find your project `irt-bank-soal`
2. Click **Settings**
3. Configure startup:
- **Startup File:** `app/main.py`
- **Startup Method:** `uvicorn`
- **Port:** `8000`
- **Modules:** `uvicorn[standard]`
### Step 9.2: Set Startup Command
In Python Manager settings, set the startup command:
```bash
# Startup command
uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 4
# Or for development:
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```
### Step 9.3: Start the Application
1. In Python Manager, click **Start** on your project
2. Check logs for any errors
3. Verify the application is running:
```bash
# Test health endpoint
curl http://127.0.0.1:8000/
# Expected response:
# {"status": "healthy", "project_name": "IRT Bank Soal", "version": "1.2.0"}
```
### Step 9.4: Configure Auto-Start on Boot
1. In Python Manager, enable **Auto-start on boot**
2. Or manually via terminal:
```bash
# Using systemd (create service file)
nano /etc/systemd/system/irt-bank-soal.service
```
```ini
[Unit]
Description=IRT Bank Soal FastAPI Application
After=network.target
# Uncomment below if using LOCAL PostgreSQL:
# After=network.target postgresql.service
[Service]
Type=simple
User=www
Group=www
WorkingDirectory=/www/wwwroot/irt-bank-soal
Environment="PATH=/www/wwwroot/irt-bank-soal/venv/bin"
ExecStart=/www/wwwroot/irt-bank-soal/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 4
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
```
```bash
# Enable and start service
systemctl daemon-reload
systemctl enable irt-bank-soal
systemctl start irt-bank-soal
systemctl status irt-bank-soal
```
---
## 10. Nginx Reverse Proxy Configuration
### Step 10.1: Create Website in AaPanel
1. In AaPanel, go to **Website**
2. Click **Add Site**
3. Configure:
- **Domain:** `api.yourdomain.com` (or your subdomain)
- **PHP Version:** Pure Static (not needed)
- **Database:** None (already created)
4. Click **Submit**
### Step 10.2: Configure Reverse Proxy
1. Click **Settings** on the newly created website
2. Go to **Reverse Proxy**
3. Click **Add Reverse Proxy**
4. Configure:
- **Proxy Name:** `irt-api`
- **Target URL:** `http://127.0.0.1:8000`
5. Click **Submit**
### Step 10.3: Manual Nginx Configuration (Alternative)
```bash
# Edit Nginx config
nano /www/server/panel/vhost/nginx/api.yourdomain.com.conf
```
```nginx
server {
listen 80;
server_name api.yourdomain.com;
# Access and error logs
access_log /www/wwwlogs/api.yourdomain.com.log;
error_log /www/wwwlogs/api.yourdomain.com.error.log;
# Client body size (for Excel uploads)
client_max_body_size 50M;
# Proxy to FastAPI
location / {
proxy_pass http://127.0.0.1:8000;
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_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Static files (if any)
location /static/ {
alias /www/wwwroot/irt-bank-soal/static/;
expires 30d;
}
}
```
### Step 10.4: Test and Reload Nginx
```bash
# Test Nginx configuration
nginx -t
# Reload Nginx
nginx -s reload
# Or via AaPanel: Website > Settings > Config > Save
```
---
## 11. SSL Configuration
### Step 11.1: Install SSL Certificate
1. In AaPanel, go to **Website**
2. Click **Settings** on your site
3. Go to **SSL**
4. Choose method:
- **Let's Encrypt:** Free, auto-renewal
- **Own Certificate:** Upload your own
- **Buy:** Purchase through AaPanel
### Step 11.2: Configure Let's Encrypt
1. Click **Let's Encrypt**
2. Enter your email
3. Select domain `api.yourdomain.com`
4. Click **Apply**
5. Enable **Force HTTPS**
### Step 11.3: Update .env for HTTPS
```bash
# Edit .env
nano /www/wwwroot/irt-bank-soal/.env
# Update CORS to use HTTPS
ALLOWED_ORIGINS=https://yourdomain.com,https://www.yourdomain.com
```
---
## 12. Post-Deployment Verification
### Step 12.1: Test API Endpoints
```bash
# Test health endpoint
curl https://api.yourdomain.com/
# Test detailed health
curl https://api.yourdomain.com/health
# Test API documentation
# Open in browser: https://api.yourdomain.com/docs
```
### Step 12.2: Test Database Connection
```bash
# Via API
curl https://api.yourdomain.com/health
# Expected response includes database status:
# {"status": "healthy", "database": "connected", "api_version": "v1"}
```
### Step 12.3: Test Admin Panel
```bash
# Access admin panel
# Open in browser: https://api.yourdomain.com/admin
# Login with credentials from .env
```
### Step 12.4: Load Test Data (Optional)
```bash
# SSH into server
ssh root@your-server-ip
# Navigate to project
cd /www/wwwroot/irt-bank-soal
# Activate venv
source venv/bin/activate
# Run test data script
python3 -c "
import asyncio
from app.database import init_db
asyncio.run(init_db())
print('Database initialized successfully')
"
```
---
## 13. Troubleshooting
### Issue: Python Manager Not Starting Application
**Solution:**
```bash
# Check logs
tail -f /www/wwwroot/irt-bank-soal/logs/error.log
# Check if port is in use
lsof -i :8000
# Manually test startup
cd /www/wwwroot/irt-bank-soal
source venv/bin/activate
uvicorn app.main:app --host 127.0.0.1 --port 8000
```
### Issue: Database Connection Failed
**For Remote Database:**
```bash
# Test connection from server
apt install -y postgresql-client
psql "postgresql://username:password@remote-host:port/database" -c "SELECT 1;"
# Check if firewall allows outbound connection
# Most remote DBs use port 5432 or 6543
# Verify DATABASE_URL in .env
cat /www/wwwroot/irt-bank-soal/.env | grep DATABASE_URL
# Common issues:
# - Wrong port (Supabase pooler uses 6543, direct uses 5432)
# - Missing sslmode=require (Neon requires this)
# - IP not whitelisted (check provider dashboard)
```
**For Local Database:**
```bash
# Check PostgreSQL status
systemctl status postgresql
# Test connection manually
psql -U irt_user -d irt_bank_soal -h 127.0.0.1 -W
# Check pg_hba.conf allows connections
cat /etc/postgresql/*/main/pg_hba.conf | grep -v "^#" | grep -v "^$"
# Verify DATABASE_URL in .env
cat /www/wwwroot/irt-bank-soal/.env | grep DATABASE_URL
```
### Issue: 502 Bad Gateway
**Solution:**
```bash
# Check if FastAPI is running
ps aux | grep uvicorn
# Check Nginx error logs
tail -f /www/wwwlogs/api.yourdomain.com.error.log
# Verify proxy configuration
cat /www/server/panel/vhost/nginx/api.yourdomain.com.conf | grep proxy_pass
```
### Issue: CORS Errors
**Solution:**
```bash
# Check ALLOWED_ORIGINS in .env
cat /www/wwwroot/irt-bank-soal/.env | grep ALLOWED_ORIGINS
# Ensure WordPress domain is included
# Example: ALLOWED_ORIGINS=https://site1.com,https://site2.com
# Restart application after changes
# Via Python Manager: Stop > Start
```
### Issue: SSL Certificate Not Working
**Solution:**
```bash
# Check certificate
openssl s_client -connect api.yourdomain.com:443
# Force HTTPS in Nginx config
# Add to server block:
# return 301 https://$host$request_uri;
# Reload Nginx
nginx -s reload
```
### Issue: Large File Upload Failed
**Solution:**
```bash
# Increase Nginx client body size
nano /www/server/panel/vhost/nginx/api.yourdomain.com.conf
# Add/modify:
# client_max_body_size 100M;
# Also check PHP settings if using PHP
# In AaPanel: PHP > Settings > Upload Max Filesize
```
---
## Quick Reference Commands
```bash
# Application Management
systemctl start irt-bank-soal
systemctl stop irt-bank-soal
systemctl restart irt-bank-soal
systemctl status irt-bank-soal
# Local Database Management (if using local PostgreSQL)
systemctl start postgresql
systemctl stop postgresql
systemctl restart postgresql
systemctl status postgresql
# Nginx Management
nginx -t # Test config
nginx -s reload # Reload config
systemctl restart nginx # Restart Nginx
# View Logs
tail -f /www/wwwlogs/api.yourdomain.com.log
tail -f /www/wwwlogs/api.yourdomain.com.error.log
# Application Logs (if configured)
tail -f /www/wwwroot/irt-bank-soal/logs/app.log
# Test Database Connection
# Local:
psql -U irt_user -d irt_bank_soal -h 127.0.0.1 -c "SELECT version();"
# Remote:
psql "postgresql://user:pass@host:port/db" -c "SELECT version();"
```
---
## Security Checklist
- [ ] Changed AaPanel default port and password
- [ ] Database user has strong password
- [ ] SECRET_KEY is unique and 32+ characters
- [ ] SSL certificate installed and forced HTTPS
- [ ] CORS restricted to production domains only
- [ ] Firewall configured (only 80, 443, 22, 8888 open)
- [ ] Admin password is strong
- [ ] For local DB: PostgreSQL not exposed to internet
- [ ] For remote DB: IP whitelist configured (if supported)
- [ ] Regular backups configured
---
## Backup Configuration
### Database Backup
**For Local Database:**
```bash
# Create backup directory
mkdir -p /www/backup
# Manual backup
pg_dump -U irt_user -h 127.0.0.1 irt_bank_soal > /www/backup/irt_bank_soal_$(date +%Y%m%d).sql
# Automated backup (cron)
crontab -e
# Add: 0 2 * * * pg_dump -U irt_user -h 127.0.0.1 irt_bank_soal > /www/backup/irt_bank_soal_$(date +\%Y\%m\%d).sql
```
**For Remote Database:**
Most managed PostgreSQL providers have built-in backup features:
- **Supabase:** Dashboard > Database > Backups (daily automatic)
- **Neon:** Automatic point-in-time recovery
- **AWS RDS:** Automated backups with retention period
You can also backup manually:
```bash
# Manual backup from remote (requires postgresql-client)
pg_dump "postgresql://username:password@host:port/database" > /www/backup/irt_bank_soal_$(date +%Y%m%d).sql
# Or with SSL for providers like Neon
pg_dump "postgresql://username:password@host:port/database?sslmode=require" > /www/backup/irt_bank_soal_$(date +%Y%m%d).sql
```
### Project Backup
```bash
# Backup project files
tar -czvf /www/backup/irt_project_$(date +%Y%m%d).tar.gz /www/wwwroot/irt-bank-soal
# Exclude venv to save space
tar -czvf /www/backup/irt_project_$(date +%Y%m%d).tar.gz --exclude='venv' /www/wwwroot/irt-bank-soal
```
---
**Document End**
**Status:** Ready for Deployment
**Support:** Refer to TEST.md for testing procedures and PRD.md for requirements.