Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 1m10s
638 lines
15 KiB
Markdown
638 lines
15 KiB
Markdown
# Bare-Metal Server Setup Guide
|
|
|
|
This guide covers the manual installation of Flyer Crawler and its dependencies on a bare-metal Ubuntu server (e.g., a colocation server). This is the definitive reference for setting up a production environment without containers.
|
|
|
|
**Target Environment**: Ubuntu 22.04 LTS (or newer)
|
|
|
|
---
|
|
|
|
## Table of Contents
|
|
|
|
1. [System Prerequisites](#system-prerequisites)
|
|
2. [PostgreSQL Setup](#postgresql-setup)
|
|
3. [Redis Setup](#redis-setup)
|
|
4. [Node.js and Application Setup](#nodejs-and-application-setup)
|
|
5. [PM2 Process Manager](#pm2-process-manager)
|
|
6. [NGINX Reverse Proxy](#nginx-reverse-proxy)
|
|
7. [Bugsink Error Tracking](#bugsink-error-tracking)
|
|
8. [Logstash Log Aggregation](#logstash-log-aggregation)
|
|
9. [SSL/TLS with Let's Encrypt](#ssltls-with-lets-encrypt)
|
|
10. [Firewall Configuration](#firewall-configuration)
|
|
11. [Maintenance Commands](#maintenance-commands)
|
|
|
|
---
|
|
|
|
## System Prerequisites
|
|
|
|
Update the system and install essential packages:
|
|
|
|
```bash
|
|
sudo apt update && sudo apt upgrade -y
|
|
sudo apt install -y curl git build-essential python3 python3-pip python3-venv
|
|
```
|
|
|
|
---
|
|
|
|
## PostgreSQL Setup
|
|
|
|
### Install PostgreSQL 14+ with PostGIS
|
|
|
|
```bash
|
|
# Add PostgreSQL APT repository
|
|
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
|
wget --quiet -O - https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo apt-key add -
|
|
sudo apt update
|
|
|
|
# Install PostgreSQL and PostGIS
|
|
sudo apt install -y postgresql-14 postgresql-14-postgis-3
|
|
```
|
|
|
|
### Create Application Database and User
|
|
|
|
```bash
|
|
sudo -u postgres psql
|
|
```
|
|
|
|
```sql
|
|
-- Create application user and database
|
|
CREATE USER flyer_crawler WITH PASSWORD 'YOUR_SECURE_PASSWORD';
|
|
CREATE DATABASE flyer_crawler OWNER flyer_crawler;
|
|
|
|
-- Connect to the database and enable extensions
|
|
\c flyer_crawler
|
|
|
|
CREATE EXTENSION IF NOT EXISTS postgis;
|
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
|
|
|
-- Grant privileges
|
|
GRANT ALL PRIVILEGES ON DATABASE flyer_crawler TO flyer_crawler;
|
|
|
|
\q
|
|
```
|
|
|
|
### Create Bugsink Database (for error tracking)
|
|
|
|
```bash
|
|
sudo -u postgres psql
|
|
```
|
|
|
|
```sql
|
|
-- Create dedicated Bugsink user and database
|
|
CREATE USER bugsink WITH PASSWORD 'BUGSINK_SECURE_PASSWORD';
|
|
CREATE DATABASE bugsink OWNER bugsink;
|
|
GRANT ALL PRIVILEGES ON DATABASE bugsink TO bugsink;
|
|
|
|
\q
|
|
```
|
|
|
|
### Configure PostgreSQL for Remote Access (if needed)
|
|
|
|
Edit `/etc/postgresql/14/main/postgresql.conf`:
|
|
|
|
```conf
|
|
listen_addresses = 'localhost' # Change to '*' for remote access
|
|
```
|
|
|
|
Edit `/etc/postgresql/14/main/pg_hba.conf` to add allowed hosts:
|
|
|
|
```conf
|
|
# Local connections
|
|
local all all peer
|
|
host all all 127.0.0.1/32 scram-sha-256
|
|
```
|
|
|
|
Restart PostgreSQL:
|
|
|
|
```bash
|
|
sudo systemctl restart postgresql
|
|
```
|
|
|
|
---
|
|
|
|
## Redis Setup
|
|
|
|
### Install Redis
|
|
|
|
```bash
|
|
sudo apt install -y redis-server
|
|
```
|
|
|
|
### Configure Redis Password
|
|
|
|
Edit `/etc/redis/redis.conf`:
|
|
|
|
```conf
|
|
requirepass YOUR_REDIS_PASSWORD
|
|
```
|
|
|
|
Restart Redis:
|
|
|
|
```bash
|
|
sudo systemctl restart redis-server
|
|
sudo systemctl enable redis-server
|
|
```
|
|
|
|
### Test Redis Connection
|
|
|
|
```bash
|
|
redis-cli -a YOUR_REDIS_PASSWORD ping
|
|
# Should output: PONG
|
|
```
|
|
|
|
---
|
|
|
|
## Node.js and Application Setup
|
|
|
|
### Install Node.js 20.x
|
|
|
|
```bash
|
|
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
|
|
sudo apt install -y nodejs
|
|
```
|
|
|
|
Verify installation:
|
|
|
|
```bash
|
|
node --version # Should output v20.x.x
|
|
npm --version
|
|
```
|
|
|
|
### Install System Dependencies for PDF Processing
|
|
|
|
```bash
|
|
sudo apt install -y poppler-utils # For pdftocairo
|
|
```
|
|
|
|
### Clone and Install Application
|
|
|
|
```bash
|
|
# Create application directory
|
|
sudo mkdir -p /opt/flyer-crawler
|
|
sudo chown $USER:$USER /opt/flyer-crawler
|
|
|
|
# Clone repository
|
|
cd /opt/flyer-crawler
|
|
git clone https://gitea.projectium.com/flyer-crawler/flyer-crawler.projectium.com.git .
|
|
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Build for production
|
|
npm run build
|
|
```
|
|
|
|
### Configure Environment Variables
|
|
|
|
Create a systemd environment file at `/etc/flyer-crawler/environment`:
|
|
|
|
```bash
|
|
sudo mkdir -p /etc/flyer-crawler
|
|
sudo nano /etc/flyer-crawler/environment
|
|
```
|
|
|
|
Add the following (replace with actual values):
|
|
|
|
```bash
|
|
# Database
|
|
DB_HOST=localhost
|
|
DB_USER=flyer_crawler
|
|
DB_PASSWORD=YOUR_SECURE_PASSWORD
|
|
DB_DATABASE_PROD=flyer_crawler
|
|
|
|
# Redis
|
|
REDIS_HOST=localhost
|
|
REDIS_PORT=6379
|
|
REDIS_PASSWORD_PROD=YOUR_REDIS_PASSWORD
|
|
|
|
# Authentication
|
|
JWT_SECRET=YOUR_LONG_RANDOM_JWT_SECRET
|
|
|
|
# Google APIs
|
|
VITE_GOOGLE_GENAI_API_KEY=YOUR_GEMINI_API_KEY
|
|
GOOGLE_MAPS_API_KEY=YOUR_MAPS_API_KEY
|
|
|
|
# Sentry/Bugsink Error Tracking (ADR-015)
|
|
SENTRY_DSN=http://BACKEND_KEY@localhost:8000/1
|
|
VITE_SENTRY_DSN=http://FRONTEND_KEY@localhost:8000/2
|
|
SENTRY_ENVIRONMENT=production
|
|
VITE_SENTRY_ENVIRONMENT=production
|
|
SENTRY_ENABLED=true
|
|
VITE_SENTRY_ENABLED=true
|
|
SENTRY_DEBUG=false
|
|
VITE_SENTRY_DEBUG=false
|
|
|
|
# Application
|
|
NODE_ENV=production
|
|
PORT=3001
|
|
```
|
|
|
|
Secure the file:
|
|
|
|
```bash
|
|
sudo chmod 600 /etc/flyer-crawler/environment
|
|
```
|
|
|
|
---
|
|
|
|
## PM2 Process Manager
|
|
|
|
### Install PM2 Globally
|
|
|
|
```bash
|
|
sudo npm install -g pm2
|
|
```
|
|
|
|
### Start Application with PM2
|
|
|
|
```bash
|
|
cd /opt/flyer-crawler
|
|
npm run start:prod
|
|
```
|
|
|
|
This starts three processes:
|
|
|
|
- `flyer-crawler-api` - Main API server (port 3001)
|
|
- `flyer-crawler-worker` - Background job worker
|
|
- `flyer-crawler-analytics-worker` - Analytics processing worker
|
|
|
|
### Configure PM2 Startup
|
|
|
|
```bash
|
|
pm2 startup systemd
|
|
# Follow the command output to enable PM2 on boot
|
|
|
|
pm2 save
|
|
```
|
|
|
|
### PM2 Log Rotation
|
|
|
|
```bash
|
|
pm2 install pm2-logrotate
|
|
pm2 set pm2-logrotate:max_size 10M
|
|
pm2 set pm2-logrotate:retain 14
|
|
pm2 set pm2-logrotate:compress true
|
|
```
|
|
|
|
---
|
|
|
|
## NGINX Reverse Proxy
|
|
|
|
### Install NGINX
|
|
|
|
```bash
|
|
sudo apt install -y nginx
|
|
```
|
|
|
|
### Create Site Configuration
|
|
|
|
Create `/etc/nginx/sites-available/flyer-crawler.projectium.com`:
|
|
|
|
```nginx
|
|
server {
|
|
listen 80;
|
|
server_name flyer-crawler.projectium.com;
|
|
|
|
# Redirect HTTP to HTTPS (uncomment after SSL setup)
|
|
# return 301 https://$server_name$request_uri;
|
|
|
|
location / {
|
|
proxy_pass http://localhost:5173;
|
|
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;
|
|
}
|
|
|
|
location /api {
|
|
proxy_pass http://localhost:3001;
|
|
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;
|
|
|
|
# File upload size limit
|
|
client_max_body_size 50M;
|
|
}
|
|
|
|
# MIME type fix for .mjs files
|
|
types {
|
|
application/javascript js mjs;
|
|
}
|
|
}
|
|
```
|
|
|
|
### Enable the Site
|
|
|
|
```bash
|
|
sudo ln -s /etc/nginx/sites-available/flyer-crawler.projectium.com /etc/nginx/sites-enabled/
|
|
sudo nginx -t
|
|
sudo systemctl reload nginx
|
|
sudo systemctl enable nginx
|
|
```
|
|
|
|
---
|
|
|
|
## Bugsink Error Tracking
|
|
|
|
Bugsink is a lightweight, self-hosted Sentry-compatible error tracking system. See [ADR-015](adr/0015-application-performance-monitoring-and-error-tracking.md) for architecture details.
|
|
|
|
### Install Bugsink
|
|
|
|
```bash
|
|
# Create virtual environment
|
|
sudo mkdir -p /opt/bugsink
|
|
sudo python3 -m venv /opt/bugsink/venv
|
|
|
|
# Activate and install
|
|
source /opt/bugsink/venv/bin/activate
|
|
pip install bugsink
|
|
|
|
# Create wrapper scripts
|
|
sudo tee /opt/bugsink/bin/bugsink-manage << 'EOF'
|
|
#!/bin/bash
|
|
source /opt/bugsink/venv/bin/activate
|
|
exec python -m bugsink.manage "$@"
|
|
EOF
|
|
|
|
sudo tee /opt/bugsink/bin/bugsink-runserver << 'EOF'
|
|
#!/bin/bash
|
|
source /opt/bugsink/venv/bin/activate
|
|
exec python -m bugsink.runserver "$@"
|
|
EOF
|
|
|
|
sudo chmod +x /opt/bugsink/bin/bugsink-manage /opt/bugsink/bin/bugsink-runserver
|
|
```
|
|
|
|
### Configure Bugsink
|
|
|
|
Create `/etc/bugsink/environment`:
|
|
|
|
```bash
|
|
sudo mkdir -p /etc/bugsink
|
|
sudo nano /etc/bugsink/environment
|
|
```
|
|
|
|
```bash
|
|
SECRET_KEY=YOUR_RANDOM_50_CHAR_SECRET_KEY
|
|
DATABASE_URL=postgresql://bugsink:BUGSINK_SECURE_PASSWORD@localhost:5432/bugsink
|
|
BASE_URL=http://localhost:8000
|
|
PORT=8000
|
|
```
|
|
|
|
```bash
|
|
sudo chmod 600 /etc/bugsink/environment
|
|
```
|
|
|
|
### Initialize Bugsink Database
|
|
|
|
```bash
|
|
source /etc/bugsink/environment
|
|
/opt/bugsink/bin/bugsink-manage migrate
|
|
/opt/bugsink/bin/bugsink-manage migrate --database=snappea
|
|
```
|
|
|
|
### Create Bugsink Admin User
|
|
|
|
```bash
|
|
/opt/bugsink/bin/bugsink-manage createsuperuser
|
|
```
|
|
|
|
### Create Systemd Service
|
|
|
|
Create `/etc/systemd/system/bugsink.service`:
|
|
|
|
```ini
|
|
[Unit]
|
|
Description=Bugsink Error Tracking
|
|
After=network.target postgresql.service
|
|
|
|
[Service]
|
|
Type=simple
|
|
User=www-data
|
|
Group=www-data
|
|
EnvironmentFile=/etc/bugsink/environment
|
|
ExecStart=/opt/bugsink/bin/bugsink-runserver 0.0.0.0:8000
|
|
Restart=always
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
```
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable bugsink
|
|
sudo systemctl start bugsink
|
|
```
|
|
|
|
### Create Bugsink Projects and Get DSNs
|
|
|
|
1. Access Bugsink UI at `http://localhost:8000`
|
|
2. Log in with admin credentials
|
|
3. Create projects:
|
|
- **flyer-crawler-backend** (Platform: Node.js)
|
|
- **flyer-crawler-frontend** (Platform: React)
|
|
4. Copy the DSNs from each project's settings
|
|
5. Update `/etc/flyer-crawler/environment` with the DSNs
|
|
|
|
### Test Error Tracking
|
|
|
|
```bash
|
|
cd /opt/flyer-crawler
|
|
npx tsx scripts/test-bugsink.ts
|
|
```
|
|
|
|
Check Bugsink UI for test events.
|
|
|
|
---
|
|
|
|
## Logstash Log Aggregation
|
|
|
|
Logstash aggregates logs from the application and infrastructure, forwarding errors to Bugsink.
|
|
|
|
### Install Logstash
|
|
|
|
```bash
|
|
# Add Elastic APT repository
|
|
wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo gpg --dearmor -o /usr/share/keyrings/elastic-keyring.gpg
|
|
echo "deb [signed-by=/usr/share/keyrings/elastic-keyring.gpg] https://artifacts.elastic.co/packages/8.x/apt stable main" | sudo tee /etc/apt/sources.list.d/elastic-8.x.list
|
|
|
|
sudo apt update
|
|
sudo apt install -y logstash
|
|
```
|
|
|
|
### Configure Logstash Pipeline
|
|
|
|
Create `/etc/logstash/conf.d/bugsink.conf`:
|
|
|
|
```conf
|
|
input {
|
|
# Pino application logs
|
|
file {
|
|
path => "/opt/flyer-crawler/logs/*.log"
|
|
codec => json
|
|
type => "pino"
|
|
tags => ["app"]
|
|
}
|
|
|
|
# Redis logs
|
|
file {
|
|
path => "/var/log/redis/*.log"
|
|
type => "redis"
|
|
tags => ["redis"]
|
|
}
|
|
}
|
|
|
|
filter {
|
|
# Pino error detection (level 50 = error, 60 = fatal)
|
|
if [type] == "pino" and [level] >= 50 {
|
|
mutate { add_tag => ["error"] }
|
|
}
|
|
|
|
# Redis error detection
|
|
if [type] == "redis" {
|
|
grok {
|
|
match => { "message" => "%{POSINT:pid}:%{WORD:role} %{MONTHDAY} %{MONTH} %{TIME} %{WORD:loglevel} %{GREEDYDATA:redis_message}" }
|
|
}
|
|
if [loglevel] in ["WARNING", "ERROR"] {
|
|
mutate { add_tag => ["error"] }
|
|
}
|
|
}
|
|
}
|
|
|
|
output {
|
|
if "error" in [tags] {
|
|
http {
|
|
url => "http://localhost:8000/api/1/store/"
|
|
http_method => "post"
|
|
format => "json"
|
|
headers => {
|
|
"X-Sentry-Auth" => "Sentry sentry_version=7, sentry_client=logstash/1.0, sentry_key=YOUR_BACKEND_DSN_KEY"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
Replace `YOUR_BACKEND_DSN_KEY` with the key from your backend project DSN.
|
|
|
|
### Start Logstash
|
|
|
|
```bash
|
|
sudo systemctl enable logstash
|
|
sudo systemctl start logstash
|
|
```
|
|
|
|
---
|
|
|
|
## SSL/TLS with Let's Encrypt
|
|
|
|
### Install Certbot
|
|
|
|
```bash
|
|
sudo apt install -y certbot python3-certbot-nginx
|
|
```
|
|
|
|
### Obtain Certificate
|
|
|
|
```bash
|
|
sudo certbot --nginx -d flyer-crawler.projectium.com
|
|
```
|
|
|
|
Certbot will automatically configure NGINX for HTTPS.
|
|
|
|
### Auto-Renewal
|
|
|
|
Certbot installs a systemd timer for automatic renewal. Verify:
|
|
|
|
```bash
|
|
sudo systemctl status certbot.timer
|
|
```
|
|
|
|
---
|
|
|
|
## Firewall Configuration
|
|
|
|
### Configure UFW
|
|
|
|
```bash
|
|
sudo ufw default deny incoming
|
|
sudo ufw default allow outgoing
|
|
|
|
# Allow SSH
|
|
sudo ufw allow ssh
|
|
|
|
# Allow HTTP and HTTPS
|
|
sudo ufw allow 80/tcp
|
|
sudo ufw allow 443/tcp
|
|
|
|
# Enable firewall
|
|
sudo ufw enable
|
|
```
|
|
|
|
**Important**: Bugsink (port 8000) should NOT be exposed externally. It listens on localhost only.
|
|
|
|
---
|
|
|
|
## Maintenance Commands
|
|
|
|
### Application Management
|
|
|
|
| Task | Command |
|
|
| --------------------- | -------------------------------------------------------------------------------------- |
|
|
| View PM2 status | `pm2 status` |
|
|
| View application logs | `pm2 logs` |
|
|
| Restart all processes | `pm2 restart all` |
|
|
| Restart specific app | `pm2 restart flyer-crawler-api` |
|
|
| Update application | `cd /opt/flyer-crawler && git pull && npm install && npm run build && pm2 restart all` |
|
|
|
|
### Service Management
|
|
|
|
| Service | Start | Stop | Status |
|
|
| ---------- | ----------------------------------- | ---------------------------------- | ------------------------------------ |
|
|
| PostgreSQL | `sudo systemctl start postgresql` | `sudo systemctl stop postgresql` | `sudo systemctl status postgresql` |
|
|
| Redis | `sudo systemctl start redis-server` | `sudo systemctl stop redis-server` | `sudo systemctl status redis-server` |
|
|
| NGINX | `sudo systemctl start nginx` | `sudo systemctl stop nginx` | `sudo systemctl status nginx` |
|
|
| Bugsink | `sudo systemctl start bugsink` | `sudo systemctl stop bugsink` | `sudo systemctl status bugsink` |
|
|
| Logstash | `sudo systemctl start logstash` | `sudo systemctl stop logstash` | `sudo systemctl status logstash` |
|
|
|
|
### Database Backup
|
|
|
|
```bash
|
|
# Backup application database
|
|
pg_dump -U flyer_crawler -h localhost flyer_crawler > backup_$(date +%Y%m%d).sql
|
|
|
|
# Backup Bugsink database
|
|
pg_dump -U bugsink -h localhost bugsink > bugsink_backup_$(date +%Y%m%d).sql
|
|
```
|
|
|
|
### Log Locations
|
|
|
|
| Log | Location |
|
|
| ----------------- | --------------------------- |
|
|
| Application (PM2) | `~/.pm2/logs/` |
|
|
| NGINX access | `/var/log/nginx/access.log` |
|
|
| NGINX error | `/var/log/nginx/error.log` |
|
|
| PostgreSQL | `/var/log/postgresql/` |
|
|
| Redis | `/var/log/redis/` |
|
|
| Bugsink | `journalctl -u bugsink` |
|
|
| Logstash | `/var/log/logstash/` |
|
|
|
|
---
|
|
|
|
## Related Documentation
|
|
|
|
- [DEPLOYMENT.md](../DEPLOYMENT.md) - Container-based deployment
|
|
- [DATABASE.md](../DATABASE.md) - Database schema and extensions
|
|
- [AUTHENTICATION.md](../AUTHENTICATION.md) - OAuth provider setup
|
|
- [ADR-015](adr/0015-application-performance-monitoring-and-error-tracking.md) - Error tracking architecture
|