Production Server: 5.182.17.148 (Ubuntu 24.04) Live URL: https://ho.nm-forum.de Last Updated: 2025-12-21
- Server Overview
- Initial Setup
- Deployment Process
- PM2 Management
- Nginx Configuration
- SSL Certificate
- Database Management
- Troubleshooting
- Maintenance Tasks
- Monitoring & Logs
OS: Ubuntu 24.04 LTS
Node.js: 20.19.6
npm: 10.8.2
PostgreSQL: 16.11
Nginx: 1.24.0
PM2: Latest (Global)
Host: 5.182.17.148
User: root
SSH: ssh root@5.182.17.148IMPORTANT: Password stored securely (not documented here for security)
Application Root: /var/www/humansonly/
Nginx Config: /etc/nginx/sites-available/humansonly
Nginx Enabled: /etc/nginx/sites-enabled/humansonly
SSL Certificates: /etc/letsencrypt/live/ho.nm-forum.de/
PM2 Logs: /root/.pm2/logs/
App Logs: /var/log/humansonly/
Environment File: /var/www/humansonly/.env
PM2 Config: /var/www/humansonly/ecosystem.config.js
apt update && apt upgrade -ycurl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
apt install -y nodejs
node --version # Verify: v20.19.6
npm --version # Verify: 10.8.2apt install -y build-essential gcc g++ makeapt install -y postgresql-16 postgresql-contrib
systemctl enable postgresql
systemctl start postgresqlapt install -y nginx
systemctl enable nginx
systemctl start nginxnpm install -g pm2apt install -y certbot python3-certbot-nginxufw allow 22 # SSH
ufw allow 80 # HTTP
ufw allow 443 # HTTPS
ufw enable
ufw statussudo -u postgres psqlCREATE DATABASE humansonly_prod;
CREATE USER humansonly_user WITH ENCRYPTED PASSWORD '<strong-password>';
GRANT ALL PRIVILEGES ON DATABASE humansonly_prod TO humansonly_user;
\qPGPASSWORD='<strong-password>' psql -h localhost -U humansonly_user -d humansonly_prod -c "SELECT 1"mkdir -p /var/www/humansonly
cd /var/www/humansonlyFrom Local Machine:
cd /Users/denniswestermann/Desktop/Coding\ Projekte/HumansOnly/app
# Sync files to server (excluding build artifacts)
rsync -avz \
--exclude 'node_modules' \
--exclude '.next' \
--exclude '.git' \
--exclude '.env' \
./ root@5.182.17.148:/var/www/humansonly/On Server:
cd /var/www/humansonly
nano .envEnvironment Configuration:
# DATABASE
DATABASE_URL="postgresql://humansonly_user:<strong-password>@localhost:5432/humansonly_prod?schema=public"
DIRECT_DATABASE_URL="postgresql://humansonly_user:<strong-password>@localhost:5432/humansonly_prod?schema=public"
# AUTHENTICATION
JWT_SECRET_KEY="<generate-with-openssl-rand-hex-32>"
CREATION_SECRET_KEY="<generate-with-openssl-rand-hex-32>"
BLUE_SECRET_KEY="<set-a-private-verification-code>"
# APPLICATION
NEXT_PUBLIC_HOST_URL="https://ho.nm-forum.de"
NODE_ENV="production"
PORT=3001
# STORAGE (Placeholder - needs real Supabase credentials)
NEXT_PUBLIC_SUPABASE_URL="https://placeholder.supabase.co"
NEXT_PUBLIC_SUPABASE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
NEXT_PUBLIC_STORAGE_URL="https://placeholder.supabase.co/storage/v1/object/public/"Security Note: Generate secure secrets with:
openssl rand -hex 32Important: Production must point to humansonly_prod (not humansonly_dev).
Validate before restart:
cd /var/www/humansonly
grep -E 'DATABASE_URL|DIRECT_DATABASE_URL' .envcd /var/www/humansonly
npm ci # Clean install for productioncd /var/www/humansonly/src
npx prisma migrate deploy
npx prisma generate
cd ..npm run buildnano /var/www/humansonly/ecosystem.config.jsContent:
module.exports = {
apps: [{
name: 'humansonly',
script: 'npm',
args: 'start',
cwd: '/var/www/humansonly',
env: {
NODE_ENV: 'production',
PORT: 3001
},
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
error_file: '/var/log/humansonly/error.log',
out_file: '/var/log/humansonly/out.log',
log_file: '/var/log/humansonly/combined.log',
time: true
}]
}mkdir -p /var/log/humansonlycd /var/www/humansonly
pm2 start ecosystem.config.js
pm2 save
pm2 startup systemd -u root --hp /rootVerify:
pm2 status
pm2 logs humansonly --lines 50nano /etc/nginx/sites-available/humansonlyContent:
server {
listen 80;
server_name ho.nm-forum.de;
client_max_body_size 20M;
location / {
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_cache_bypass $http_upgrade;
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_read_timeout 300s;
proxy_connect_timeout 300s;
}
location /storage/ {
alias /var/www/humansonly/public/uploads/;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /_next/static/ {
alias /var/www/humansonly/.next/static/;
expires 1y;
add_header Cache-Control "public, immutable";
}
}ln -s /etc/nginx/sites-available/humansonly /etc/nginx/sites-enabled/
nginx -t # Test configuration
systemctl reload nginxcertbot --nginx -d ho.nm-forum.deFollow prompts:
- Enter email for renewal notifications
- Agree to Terms of Service
- Choose to redirect HTTP to HTTPS (recommended)
Verify:
certbot certificatesAuto-renewal is enabled by default via systemd timer.
Repository includes workflow:
.github/workflows/deploy.yml
This workflow runs on every push to main (for selected paths) and performs:
npm ci,npm run lint,npm run buildinapp/rsyncupload ofapp/to/var/www/humansonly- remote
npm ci,prisma migrate deploy,npm run build pm2 restart humansonly --update-env+ health probe
Required GitHub repository secrets:
DEPLOY_SSH_KEY(private key, e.g. matching server authorized key)DEPLOY_HOST(e.g.5.182.17.148)DEPLOY_PORT(e.g.2222)DEPLOY_USER(e.g.root)DEPLOY_PATH(e.g./var/www/humansonly)
Use local script:
./scripts/deploy-server.shDry-run (no server changes):
./scripts/deploy-server.sh --dry-run# On local machine
cd /Users/denniswestermann/Desktop/Coding\ Projekte/HumansOnly
# Make changes, test locally
./scripts/baseline-check.sh
# Commit changes
git add .
git commit -m "feat: Your changes"
git push origin main# Preferred: push to main triggers GitHub auto-deploy workflow
# Fallback: run local deployment script
./scripts/deploy-server.sh# Check PM2 status
pm2 status
# Check logs
pm2 logs humansonly --lines 50
# Test application
curl -I http://localhost:3001
curl -I https://ho.nm-forum.deCreate deployment script for faster updates:
nano /root/deploy-humansonly.shContent:
#!/bin/bash
set -e
echo "Starting Humans Only deployment..."
cd /var/www/humansonly
echo "Pulling latest changes..."
git pull origin main
echo "Installing dependencies..."
npm ci
echo "Running database migrations..."
cd src
npx prisma migrate deploy
npx prisma generate
cd ..
echo "Building application..."
npm run build
echo "Restarting PM2..."
pm2 restart humansonly --update-env
pm2 save
echo "Deployment complete!"
pm2 statusMake executable:
chmod +x /root/deploy-humansonly.shUsage:
/root/deploy-humansonly.shpm2 status # Show all processes
pm2 info humansonly # Detailed info
pm2 monit # Real-time monitoring
pm2 logs humansonly # Live logs
pm2 logs humansonly --lines 100 # Last 100 lines
pm2 logs humansonly --err # Error logs onlypm2 start ecosystem.config.js # Start application
pm2 restart humansonly # Restart application
pm2 restart humansonly --update-env # Restart with new .env
pm2 stop humansonly # Stop application
pm2 delete humansonly # Remove from PM2
pm2 save # Save current process list
pm2 resurrect # Restore saved processespm2 startup # Generate startup script
pm2 startup systemd -u root --hp /root # Systemd startup
systemctl status pm2-root # Check systemd servicepm2 flush # Clear all logs
pm2 reloadLogs # Reload log configuration
pm2 install pm2-logrotate # Install log rotation
pm2 set pm2-logrotate:max_size 10M
pm2 set pm2-logrotate:retain 7# Check logs for errors
pm2 logs humansonly --lines 200 --err
# Common causes:
# - Port conflict (check with: lsof -i :3001)
# - Database connection error
# - Missing environment variables
# - Build errors# Check memory usage
pm2 monit
# Restart with updated memory limit
pm2 delete humansonly
# Edit ecosystem.config.js: max_memory_restart: '2G'
pm2 start ecosystem.config.js
pm2 save# Kill zombie processes
pkill -f "next-server"
pkill -f "next-router"
# Clean PM2 state
pm2 delete all
pm2 flush
# Restart with ecosystem config
cd /var/www/humansonly
pm2 start ecosystem.config.js
pm2 savenginx -t # Test syntax
nginx -T # Show full configurationsystemctl reload nginx # Reload config (no downtime)
systemctl restart nginx # Full restart
systemctl status nginx # Check status# Check if PM2 is running
pm2 status
# Check if port is open
curl http://localhost:3001
# Check Nginx error logs
tail -f /var/log/nginx/error.log# Increase client_max_body_size in nginx config
nano /etc/nginx/sites-available/humansonly
# Add: client_max_body_size 50M;
nginx -t && systemctl reload nginxDomain: ho.nm-forum.de
Issuer: Let's Encrypt
Valid Until: 2026-03-21 (auto-renews)
Certificate Path: /etc/letsencrypt/live/ho.nm-forum.de/fullchain.pem
Private Key Path: /etc/letsencrypt/live/ho.nm-forum.de/privkey.pem
certbot certificates # List all certificates
certbot renew --dry-run # Test renewal
certbot renew # Manual renewal
certbot renew --force-renewal # Force renewalCertbot creates a systemd timer for automatic renewal:
systemctl status certbot.timer
systemctl list-timers certbot.timercertbot renew
systemctl reload nginx# Connect to database
PGPASSWORD='<strong-password>' psql -h localhost -U humansonly_user -d humansonly_prod
# As postgres superuser
sudo -u postgres psql-- List databases
\l
-- Connect to database
\c humansonly_prod
-- List tables
\dt
-- List users
\du
-- View table structure
\d+ "User"
-- Check migrations
SELECT * FROM "_prisma_migrations" ORDER BY finished_at DESC;
-- Quit
\q# Create backup
pg_dump -U humansonly_user -h localhost humansonly_prod > backup_$(date +%F).sql
# With password
PGPASSWORD='<strong-password>' pg_dump -h localhost -U humansonly_user humansonly_prod > backup_$(date +%F).sqlnano /root/backup-humansonly-db.shContent:
#!/bin/bash
BACKUP_DIR="/var/backups/humansonly"
DATE=$(date +%F)
KEEP_DAYS=7
mkdir -p $BACKUP_DIR
# Create backup
PGPASSWORD='<strong-password>' pg_dump -h localhost -U humansonly_user humansonly_prod > $BACKUP_DIR/humansonly_$DATE.sql
# Compress
gzip $BACKUP_DIR/humansonly_$DATE.sql
# Delete old backups
find $BACKUP_DIR -name "humansonly_*.sql.gz" -mtime +$KEEP_DAYS -delete
echo "Backup completed: humansonly_$DATE.sql.gz"Make executable:
chmod +x /root/backup-humansonly-db.shAdd to crontab (daily at 2 AM):
crontab -e
# Add:
0 2 * * * /root/backup-humansonly-db.sh >> /var/log/humansonly/backup.log 2>&1# Uncompress backup
gunzip backup_2025-12-21.sql.gz
# Restore database (WARNING: This will overwrite existing data)
PGPASSWORD='<strong-password>' psql -h localhost -U humansonly_user -d humansonly_prod < backup_2025-12-21.sqlpm2 status
pm2 logs humansonly --lines 100 --errPort Conflict:
# Check what's using the port
lsof -i :3001
# Kill process if needed
kill -9 $(lsof -ti:3001)
# Restart PM2
pm2 restart humansonlyDatabase Connection Error:
# Test database connection
PGPASSWORD='<strong-password>' psql -h localhost -U humansonly_user -d humansonly_prod -c "SELECT 1"
# Check .env file
cat /var/www/humansonly/.env | grep DATABASE_URL
# Restart PostgreSQL
systemctl restart postgresqlBuild Errors:
cd /var/www/humansonly
rm -rf .next node_modules package-lock.json
npm install
npm run build
pm2 restart humansonlysystemctl status nginx
nginx -t
tail -f /var/log/nginx/error.logcertbot certificates
curl -I https://ho.nm-forum.deufw status
# Ensure ports 80 and 443 are open# Memory usage
free -h
# Disk space
df -h
# CPU usage
top
# PM2 monitoring
pm2 monit# Check database size
sudo -u postgres psql -c "\l+ humansonly_prod"
# Check active connections
sudo -u postgres psql -c "SELECT count(*) FROM pg_stat_activity WHERE datname='humansonly_prod';"apt update
apt upgrade -ypm2 status
pm2 logs humansonly --lines 20ls -lh /var/backups/humansonly/# PM2 logs
pm2 logs humansonly --lines 500
# Nginx logs
tail -500 /var/log/nginx/access.log
tail -500 /var/log/nginx/error.log
# PostgreSQL logs
tail -500 /var/log/postgresql/postgresql-16-main.logcd /var/www/humansonly
npm outdated
# Review and update as needed
npm update
npm audit fix
npm run build
pm2 restart humansonlypm2 flush
find /var/log/humansonly -name "*.log" -mtime +30 -delete# Check current version
node --version
# Update to latest LTS
curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo -E bash -
apt install -y nodejs
pm2 restart humansonlyapt update
apt upgrade postgresql-16
systemctl restart postgresql# Real-time logs
pm2 logs humansonly
# Last N lines
pm2 logs humansonly --lines 200
# Only errors
pm2 logs humansonly --err
# Only output
pm2 logs humansonly --out
# Log files location
ls -lh /var/log/humansonly/
ls -lh /root/.pm2/logs/# Access log (real-time)
tail -f /var/log/nginx/access.log
# Error log (real-time)
tail -f /var/log/nginx/error.log
# Last 100 lines
tail -100 /var/log/nginx/access.log
tail -100 /var/log/nginx/error.log
# Search for specific IP
grep "123.456.789.0" /var/log/nginx/access.log# PostgreSQL log location
tail -f /var/log/postgresql/postgresql-16-main.log
# Slow queries (if logging enabled)
grep "duration:" /var/log/postgresql/postgresql-16-main.log# Memory
free -h
# Disk
df -h
# CPU & Processes
htop # or: top
# Network
netstat -tuln# Real-time dashboard
pm2 monit
# Process info
pm2 info humansonly# Stop application
pm2 stop humansonly
# Restart PostgreSQL
systemctl restart postgresql
# Restart Nginx
systemctl restart nginx
# Start application
pm2 start humansonly
pm2 save
# Verify
pm2 status
curl -I https://ho.nm-forum.decd /var/www/humansonly
# Find previous commit
git log --oneline -10
# Rollback to previous version
git reset --hard <commit-hash>
# Rebuild
npm ci
npm run build
# Restart
pm2 restart humansonly --update-env# Stop application
pm2 stop humansonly
# Restore latest backup
LATEST_BACKUP=$(ls -t /var/backups/humansonly/*.sql.gz | head -1)
gunzip -c $LATEST_BACKUP | PGPASSWORD='<strong-password>' psql -h localhost -U humansonly_user -d humansonly_prod
# Restart application
pm2 start humansonly# One-line health check
systemctl status nginx && systemctl status postgresql && pm2 status && curl -I https://ho.nm-forum.de#!/bin/bash
echo "=== System Status ==="
echo "Uptime: $(uptime)"
echo "Memory: $(free -h | grep Mem:)"
echo "Disk: $(df -h / | tail -1)"
echo ""
echo "=== Services ==="
systemctl status nginx --no-pager
systemctl status postgresql --no-pager
echo ""
echo "=== PM2 ==="
pm2 status
echo ""
echo "=== SSL Certificate ==="
certbot certificates
echo ""
echo "=== Website Check ==="
curl -I https://ho.nm-forum.deServer Administrator: d.westermann@ol-mg.de Documentation: /docs/DEPLOYMENT.md Architecture Docs: /docs/ARCHITECTURE.md API Docs: /docs/API_CONSUMERS.md
Last Updated: 2025-12-21 Version: 1.0 Status: Production Ready