Some checks failed
Sync all deployment docs with actual docker-compose.yml values: - celery-worker: 512→768MB, concurrency 4→2 - db: 512→256MB, celery-beat: 256→128MB, flower: 256→192MB - Redis maxmemory: 256mb→100mb (matches container mem_limit 128m) - Add redis-exporter to scaling guide memory budget Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
490 lines
10 KiB
Markdown
490 lines
10 KiB
Markdown
# Traditional VPS Deployment
|
|
|
|
This guide covers deploying Orion to a traditional VPS (Ubuntu 22.04+) without containers.
|
|
|
|
**Best for:** Teams who want direct server access and familiar Linux administration.
|
|
|
|
---
|
|
|
|
## Prerequisites
|
|
|
|
- Ubuntu 22.04 LTS or newer
|
|
- 4GB+ RAM recommended
|
|
- Root or sudo access
|
|
- Domain name with DNS configured
|
|
|
|
---
|
|
|
|
## Quick Start
|
|
|
|
```bash
|
|
# 1. Install system packages
|
|
sudo apt update && sudo apt upgrade -y
|
|
sudo apt install -y nginx postgresql-15 redis-server python3.11 python3.11-venv git curl
|
|
|
|
# 2. Create application user
|
|
sudo useradd -m -s /bin/bash orion
|
|
|
|
# 3. Setup PostgreSQL
|
|
sudo -u postgres createuser orion_user
|
|
sudo -u postgres createdb orion_db -O orion_user
|
|
sudo -u postgres psql -c "ALTER USER orion_user WITH PASSWORD 'your-secure-password';"
|
|
|
|
# 4. Clone and setup application
|
|
sudo su - orion
|
|
git clone <repository-url> ~/app
|
|
cd ~/app
|
|
python3.11 -m venv .venv
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
|
|
# 5. Configure environment
|
|
cp .env.example .env
|
|
nano .env # Edit with production values
|
|
|
|
# 6. Initialize database
|
|
alembic upgrade head
|
|
python scripts/seed/init_production.py
|
|
|
|
# 7. Exit orion user
|
|
exit
|
|
```
|
|
|
|
---
|
|
|
|
## Systemd Services
|
|
|
|
### Main Application
|
|
|
|
```bash
|
|
sudo nano /etc/systemd/system/orion.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Orion API Server
|
|
After=network.target postgresql.service redis.service
|
|
|
|
[Service]
|
|
User=orion
|
|
Group=orion
|
|
WorkingDirectory=/home/orion/app
|
|
Environment="PATH=/home/orion/app/.venv/bin"
|
|
EnvironmentFile=/home/orion/app/.env
|
|
ExecStart=/home/orion/app/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 4
|
|
Restart=always
|
|
RestartSec=3
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### Celery Worker
|
|
|
|
```bash
|
|
sudo nano /etc/systemd/system/orion-celery.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Orion Celery Worker
|
|
After=network.target redis.service postgresql.service
|
|
|
|
[Service]
|
|
User=orion
|
|
Group=orion
|
|
WorkingDirectory=/home/orion/app
|
|
Environment="PATH=/home/orion/app/.venv/bin"
|
|
EnvironmentFile=/home/orion/app/.env
|
|
ExecStart=/home/orion/app/.venv/bin/celery -A app.core.celery_config worker --loglevel=info -Q default,long_running,scheduled --concurrency=2
|
|
Restart=always
|
|
RestartSec=3
|
|
StandardOutput=journal
|
|
StandardError=journal
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### Celery Beat (Scheduler)
|
|
|
|
```bash
|
|
sudo nano /etc/systemd/system/orion-celery-beat.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Orion Celery Beat Scheduler
|
|
After=network.target redis.service
|
|
|
|
[Service]
|
|
User=orion
|
|
Group=orion
|
|
WorkingDirectory=/home/orion/app
|
|
Environment="PATH=/home/orion/app/.venv/bin"
|
|
EnvironmentFile=/home/orion/app/.env
|
|
ExecStart=/home/orion/app/.venv/bin/celery -A app.core.celery_config beat --loglevel=info
|
|
Restart=always
|
|
RestartSec=3
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### Flower (Task Monitoring)
|
|
|
|
```bash
|
|
sudo nano /etc/systemd/system/orion-flower.service
|
|
```
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Orion Flower Task Monitor
|
|
After=network.target redis.service
|
|
|
|
[Service]
|
|
User=orion
|
|
Group=orion
|
|
WorkingDirectory=/home/orion/app
|
|
Environment="PATH=/home/orion/app/.venv/bin"
|
|
EnvironmentFile=/home/orion/app/.env
|
|
ExecStart=/home/orion/app/.venv/bin/celery -A app.core.celery_config flower --port=5555
|
|
Restart=always
|
|
RestartSec=3
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
### Enable Services
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable orion orion-celery orion-celery-beat orion-flower
|
|
sudo systemctl start orion orion-celery orion-celery-beat orion-flower
|
|
```
|
|
|
|
---
|
|
|
|
## Nginx Configuration
|
|
|
|
```bash
|
|
sudo nano /etc/nginx/sites-available/orion
|
|
```
|
|
|
|
```nginx
|
|
# Redirect HTTP to HTTPS
|
|
server {
|
|
listen 80;
|
|
server_name yourdomain.com www.yourdomain.com;
|
|
return 301 https://$server_name$request_uri;
|
|
}
|
|
|
|
# Main HTTPS server
|
|
server {
|
|
listen 443 ssl http2;
|
|
server_name yourdomain.com www.yourdomain.com;
|
|
|
|
# SSL (managed by Certbot)
|
|
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
|
|
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
|
|
include /etc/letsencrypt/options-ssl-nginx.conf;
|
|
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
|
|
|
# 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;
|
|
|
|
# Logging
|
|
access_log /var/log/nginx/orion.access.log;
|
|
error_log /var/log/nginx/orion.error.log;
|
|
|
|
# Static files (served directly)
|
|
location /static {
|
|
alias /home/orion/app/static;
|
|
expires 30d;
|
|
add_header Cache-Control "public, immutable";
|
|
access_log off;
|
|
}
|
|
|
|
# Uploaded files
|
|
location /uploads {
|
|
alias /home/orion/app/uploads;
|
|
expires 7d;
|
|
add_header Cache-Control "public";
|
|
}
|
|
|
|
# Application
|
|
location / {
|
|
proxy_pass http://127.0.0.1:8000;
|
|
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;
|
|
|
|
# WebSocket support
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade $http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
|
|
# Timeouts
|
|
proxy_connect_timeout 60s;
|
|
proxy_send_timeout 60s;
|
|
proxy_read_timeout 60s;
|
|
}
|
|
|
|
# Block sensitive files
|
|
location ~ /\. {
|
|
deny all;
|
|
}
|
|
location ~ \.env$ {
|
|
deny all;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Enable Site
|
|
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/orion /etc/nginx/sites-enabled/
|
|
sudo nginx -t
|
|
sudo systemctl reload nginx
|
|
```
|
|
|
|
### SSL with Certbot
|
|
|
|
```bash
|
|
sudo apt install certbot python3-certbot-nginx
|
|
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
|
|
```
|
|
|
|
---
|
|
|
|
## Firewall
|
|
|
|
```bash
|
|
sudo ufw allow OpenSSH
|
|
sudo ufw allow 'Nginx Full'
|
|
sudo ufw enable
|
|
```
|
|
|
|
---
|
|
|
|
## Daily Operations
|
|
|
|
### View Logs
|
|
|
|
```bash
|
|
# Application logs
|
|
sudo journalctl -u orion -f
|
|
|
|
# Celery logs
|
|
sudo journalctl -u orion-celery -f
|
|
|
|
# Nginx logs
|
|
sudo tail -f /var/log/nginx/orion.access.log
|
|
sudo tail -f /var/log/nginx/orion.error.log
|
|
|
|
# PostgreSQL logs
|
|
sudo tail -f /var/log/postgresql/postgresql-15-main.log
|
|
```
|
|
|
|
### Restart Services
|
|
|
|
```bash
|
|
sudo systemctl restart orion
|
|
sudo systemctl restart orion-celery
|
|
sudo systemctl restart nginx
|
|
```
|
|
|
|
### Database Access
|
|
|
|
```bash
|
|
# Connect as orion user
|
|
sudo -u postgres psql orion_db
|
|
|
|
# Or with password
|
|
psql -h localhost -U orion_user -d orion_db
|
|
```
|
|
|
|
### Deploy Updates
|
|
|
|
```bash
|
|
sudo su - orion
|
|
cd ~/app
|
|
git pull origin main
|
|
source .venv/bin/activate
|
|
pip install -r requirements.txt
|
|
alembic upgrade head
|
|
exit
|
|
sudo systemctl restart orion orion-celery
|
|
```
|
|
|
|
---
|
|
|
|
## Backups
|
|
|
|
!!! tip "Docker deployment"
|
|
For Docker-based deployments, use the automated backup scripts (`scripts/backup.sh` and `scripts/restore.sh`) with systemd timer. See [Hetzner Server Setup — Step 17](hetzner-server-setup.md#step-17-backups).
|
|
|
|
### Database Backup Script (VPS without Docker)
|
|
|
|
```bash
|
|
sudo nano /home/orion/backup.sh
|
|
```
|
|
|
|
```bash
|
|
#!/bin/bash
|
|
BACKUP_DIR=/home/orion/backups
|
|
DATE=$(date +%Y%m%d_%H%M%S)
|
|
|
|
# Create backup directory
|
|
mkdir -p $BACKUP_DIR
|
|
|
|
# Backup database
|
|
pg_dump -U orion_user orion_db | gzip > $BACKUP_DIR/db_$DATE.sql.gz
|
|
|
|
# Backup uploads
|
|
tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz -C /home/orion/app uploads/
|
|
|
|
# Keep last 7 days
|
|
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
|
|
|
|
echo "Backup completed: $DATE"
|
|
```
|
|
|
|
```bash
|
|
chmod +x /home/orion/backup.sh
|
|
```
|
|
|
|
### Cron Job
|
|
|
|
```bash
|
|
sudo -u orion crontab -e
|
|
```
|
|
|
|
```cron
|
|
# Daily backup at 2 AM
|
|
0 2 * * * /home/orion/backup.sh >> /home/orion/backup.log 2>&1
|
|
```
|
|
|
|
---
|
|
|
|
## Monitoring
|
|
|
|
!!! tip "Docker deployment"
|
|
For Docker-based deployments, a full Prometheus + Grafana + node-exporter + cAdvisor stack is included in `docker-compose.yml`. See [Hetzner Server Setup — Step 18](hetzner-server-setup.md#step-18-monitoring-observability) and [Observability Framework](../architecture/observability.md).
|
|
|
|
### Basic Health Check
|
|
|
|
```bash
|
|
curl -s http://localhost:8000/health | jq
|
|
```
|
|
|
|
### Process Monitoring
|
|
|
|
```bash
|
|
# Check all services
|
|
systemctl status orion orion-celery postgresql redis nginx
|
|
|
|
# Resource usage
|
|
htop
|
|
df -h
|
|
free -h
|
|
```
|
|
|
|
### Set Up Sentry (Error Tracking)
|
|
|
|
Sentry provides real-time error tracking and performance monitoring.
|
|
|
|
1. **Create a Sentry account** at [sentry.io](https://sentry.io) (free tier available)
|
|
2. **Create a new project** (Python/FastAPI)
|
|
3. **Add to `.env`**:
|
|
```env
|
|
SENTRY_DSN=https://your-key@sentry.io/project-id
|
|
SENTRY_ENVIRONMENT=production
|
|
SENTRY_TRACES_SAMPLE_RATE=0.1
|
|
```
|
|
4. **Restart services**:
|
|
```bash
|
|
sudo systemctl restart orion orion-celery
|
|
```
|
|
|
|
Sentry will now capture:
|
|
- Unhandled exceptions
|
|
- API errors with request context
|
|
- Celery task failures
|
|
- Performance traces (10% sample rate)
|
|
|
|
---
|
|
|
|
## Cloudflare R2 Storage
|
|
|
|
For production, use Cloudflare R2 instead of local storage for scalability and CDN integration.
|
|
|
|
### Setup
|
|
|
|
1. **Create R2 bucket** in CloudFlare dashboard
|
|
2. **Create API token** with Object Read/Write permissions
|
|
3. **Add to `.env`**:
|
|
```env
|
|
STORAGE_BACKEND=r2
|
|
R2_ACCOUNT_ID=your_account_id
|
|
R2_ACCESS_KEY_ID=your_access_key
|
|
R2_SECRET_ACCESS_KEY=your_secret_key
|
|
R2_BUCKET_NAME=orion-media
|
|
R2_PUBLIC_URL=https://media.yourdomain.com
|
|
```
|
|
|
|
See [CloudFlare Setup Guide](cloudflare.md) for detailed instructions.
|
|
|
|
---
|
|
|
|
## CloudFlare CDN & Proxy
|
|
|
|
For production, proxy your domain through CloudFlare for:
|
|
- Global CDN caching
|
|
- DDoS protection
|
|
- Free SSL certificates
|
|
- WAF (Web Application Firewall)
|
|
|
|
### Enable CloudFlare Headers
|
|
|
|
Add to `.env`:
|
|
```env
|
|
CLOUDFLARE_ENABLED=true
|
|
```
|
|
|
|
This enables proper handling of `CF-Connecting-IP` for real client IPs.
|
|
|
|
See [CloudFlare Setup Guide](cloudflare.md) for complete configuration.
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
See [Infrastructure Guide - Troubleshooting](infrastructure.md#troubleshooting-guide) for detailed diagnostics.
|
|
|
|
### Quick Checks
|
|
|
|
```bash
|
|
# Is the app running?
|
|
systemctl status orion
|
|
|
|
# Can we connect to the database?
|
|
pg_isready -h localhost -U orion_user
|
|
|
|
# Is Redis running?
|
|
redis-cli ping
|
|
|
|
# Check open ports
|
|
ss -tlnp | grep -E '(8000|5432|6379|80|443)'
|
|
|
|
# View recent errors
|
|
journalctl -u orion --since "1 hour ago" | grep -i error
|
|
```
|