mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-03 18:22:57 +00:00
13 KiB
13 KiB
Production Deployment Guide
Quick Production Deployment (15 Minutes)
Prerequisites
- Docker & Docker Compose installed
- Domain name configured
- SSL certificate ready (Let's Encrypt recommended)
- PostgreSQL 14+ database
Step 1: Clone & Configure (5 min)
# Clone repository
git clone <your-repo-url> fotbal-club-production
cd fotbal-club-production
# Copy environment template
cp .env.example .env
# Generate JWT secret (64 characters)
openssl rand -hex 32 > jwt_secret.txt
Edit .env file:
nano .env
Critical settings to change:
# Application
APP_ENV=production
DEBUG=false
PORT=8080
# JWT - CHANGE THIS!
JWT_SECRET=<paste-from-jwt_secret.txt>
# Database
DATABASE_URL=postgres://dbuser:dbpassword@localhost:5432/fotbal_club?sslmode=require
# SMTP - Real email service
SMTP_HOST=smtp.sendgrid.net
SMTP_PORT=587
SMTP_USER=apikey
SMTP_PASSWORD=<your-sendgrid-api-key>
SMTP_FROM=noreply@your-domain.cz
SMTP_FROM_NAME="Your Club Name"
# Migrations
RUN_MIGRATIONS=true
SEED_DATABASE=false
# CORS
ALLOWED_ORIGINS=https://your-domain.cz,https://www.your-domain.cz
Step 2: Database Setup (3 min)
# Start PostgreSQL (if using Docker)
docker-compose up -d db
# Wait for database to be ready
docker-compose exec db pg_isready
# Run migrations
docker-compose run --rm backend ./fotbal-club migrate
# Verify migrations
docker-compose exec db psql -U postgres -d fotbal_club -c "\dt"
Step 3: Build & Deploy (5 min)
# Build frontend
cd frontend
npm install --production
npm run build
cd ..
# Build backend
docker-compose build backend
# Start all services
docker-compose up -d
# Verify services are running
docker-compose ps
# Check logs
docker-compose logs -f backend | head -50
Step 4: Verify Deployment (2 min)
# Health check
curl http://localhost:8080/api/v1/health
# Expected response:
# {"status":"ok","database":"connected"}
# Check metrics
curl http://localhost:8080/metrics | grep "http_requests_total"
# Test authentication
curl -X POST http://localhost:8080/api/v1/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"admin123"}'
Nginx Reverse Proxy Configuration
Install Nginx
sudo apt update
sudo apt install nginx certbot python3-certbot-nginx
Configure Site
sudo nano /etc/nginx/sites-available/fotbal-club
# Backend API
server {
listen 80;
server_name api.your-domain.cz;
# Redirect to HTTPS
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.your-domain.cz;
# SSL certificates (Let's Encrypt)
ssl_certificate /etc/letsencrypt/live/api.your-domain.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.your-domain.cz/privkey.pem;
# SSL configuration
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
# Security headers (backend already sets these, but good to enforce)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
# Rate limiting
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
limit_req zone=api_limit burst=200 nodelay;
# Proxy settings
location / {
proxy_pass http://127.0.0.1:8080;
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;
# Timeouts
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s;
}
# Uploads - longer timeout
location ~ ^/(api/v1/upload|api/v1/admin/.*/(upload|image)) {
client_max_body_size 10M;
proxy_pass http://127.0.0.1:8080;
proxy_request_buffering off;
proxy_read_timeout 300s;
}
# Static files - long cache
location ~ ^/(dist|uploads|cache)/ {
proxy_pass http://127.0.0.1:8080;
proxy_cache_valid 200 7d;
add_header Cache-Control "public, max-age=604800, immutable";
}
# Metrics endpoint - restrict access
location /metrics {
allow 127.0.0.1;
allow <your-monitoring-server-ip>;
deny all;
proxy_pass http://127.0.0.1:8080;
}
# Access/error logs
access_log /var/log/nginx/fotbal-club-access.log combined;
error_log /var/log/nginx/fotbal-club-error.log warn;
}
# Frontend (static files)
server {
listen 80;
server_name your-domain.cz www.your-domain.cz;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name your-domain.cz www.your-domain.cz;
ssl_certificate /etc/letsencrypt/live/your-domain.cz/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.cz/privkey.pem;
root /var/www/fotbal-club/frontend/build;
index index.html;
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css text/xml text/javascript application/javascript application/xml+rss application/json;
# Security headers
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# React Router (SPA)
location / {
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-cache";
}
# Static assets - long cache
location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}
# Proxy API requests to backend
location /api {
proxy_pass http://127.0.0.1:8080;
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;
}
access_log /var/log/nginx/fotbal-club-frontend-access.log combined;
error_log /var/log/nginx/fotbal-club-frontend-error.log warn;
}
Enable Site & Get SSL
# Enable site
sudo ln -s /etc/nginx/sites-available/fotbal-club /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Get SSL certificate
sudo certbot --nginx -d your-domain.cz -d www.your-domain.cz -d api.your-domain.cz
# Reload Nginx
sudo systemctl reload nginx
# Auto-renewal
sudo certbot renew --dry-run
Database Backup Setup
Automated Daily Backups
# Create backup script
sudo nano /usr/local/bin/backup-fotbal-db.sh
#!/bin/bash
set -e
# Configuration
DB_NAME="fotbal_club"
DB_USER="postgres"
BACKUP_DIR="/var/backups/fotbal-club"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="$BACKUP_DIR/fotbal_club_$DATE.dump"
# Create backup directory
mkdir -p $BACKUP_DIR
# Backup database
pg_dump -U $DB_USER -Fc $DB_NAME > $BACKUP_FILE
# Compress
gzip $BACKUP_FILE
# Delete old backups
find $BACKUP_DIR -name "*.dump.gz" -mtime +$RETENTION_DAYS -delete
# Upload to S3 (optional)
# aws s3 cp $BACKUP_FILE.gz s3://your-bucket/backups/
echo "Backup completed: $BACKUP_FILE.gz"
# Make executable
sudo chmod +x /usr/local/bin/backup-fotbal-db.sh
# Add to crontab (daily at 2 AM)
sudo crontab -e
Add line:
0 2 * * * /usr/local/bin/backup-fotbal-db.sh >> /var/log/fotbal-backup.log 2>&1
Monitoring Setup
Prometheus Configuration
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'fotbal-club'
static_configs:
- targets: ['localhost:8080']
metrics_path: '/metrics'
basic_auth:
username: 'admin'
password: '<secure-password>'
Grafana Dashboard Import
Use dashboard ID: 6417 (Gin metrics)
Modify for custom metrics
Security Hardening Checklist
Server Level
# Update system
sudo apt update && sudo apt upgrade -y
# Enable firewall
sudo ufw allow 22/tcp
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
# Fail2ban for SSH
sudo apt install fail2ban
sudo systemctl enable fail2ban
sudo systemctl start fail2ban
# Disable root SSH login
sudo nano /etc/ssh/sshd_config
# Set: PermitRootLogin no
sudo systemctl restart sshd
Application Level
# Set file permissions
sudo chown -R app:app /app/uploads
sudo chmod 755 /app/uploads
sudo chmod 644 /app/uploads/*
# Secure environment files
chmod 600 .env
chown root:root .env
# Rotate logs
sudo nano /etc/logrotate.d/fotbal-club
/var/log/nginx/fotbal-club-*.log {
daily
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 `cat /var/run/nginx.pid`
endscript
}
Performance Tuning
PostgreSQL Optimization
# Edit postgresql.conf
sudo nano /etc/postgresql/14/main/postgresql.conf
# Memory settings (for 4GB RAM server)
shared_buffers = 1GB
effective_cache_size = 3GB
maintenance_work_mem = 256MB
work_mem = 32MB
# Connections
max_connections = 200
# Checkpoints
checkpoint_completion_target = 0.9
wal_buffers = 16MB
# Query planner
random_page_cost = 1.1 # For SSD
effective_io_concurrency = 200
# Logging
log_min_duration_statement = 1000 # Log slow queries (1s+)
Docker Resource Limits
# docker-compose.yml
services:
backend:
deploy:
resources:
limits:
cpus: '2'
memory: 1G
reservations:
cpus: '0.5'
memory: 512M
restart: unless-stopped
db:
deploy:
resources:
limits:
cpus: '2'
memory: 2G
reservations:
cpus: '1'
memory: 1G
restart: unless-stopped
Maintenance Scripts
Health Check Script
#!/bin/bash
# /usr/local/bin/health-check.sh
URL="https://your-domain.cz/api/v1/health"
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" $URL)
if [ $RESPONSE -ne 200 ]; then
echo "Health check failed! HTTP $RESPONSE"
# Send alert
curl -X POST "https://api.telegram.org/bot<TOKEN>/sendMessage" \
-d "chat_id=<CHAT_ID>" \
-d "text=⚠️ Fotbal Club Health Check Failed!"
exit 1
fi
echo "Health check OK"
Database Maintenance
#!/bin/bash
# Weekly database maintenance
# Vacuum and analyze
psql -U postgres -d fotbal_club -c "VACUUM ANALYZE;"
# Reindex
psql -U postgres -d fotbal_club -c "REINDEX DATABASE fotbal_club;"
# Check table sizes
psql -U postgres -d fotbal_club -c "
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size
FROM pg_tables
WHERE schemaname = 'public'
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC
LIMIT 10;
"
Troubleshooting
Service Won't Start
# Check logs
docker-compose logs backend --tail=100
# Common issues:
# 1. Port already in use
sudo lsof -i :8080
# Kill process if needed
# 2. Database connection failed
docker-compose exec db pg_isready
# 3. Permission denied
sudo chown -R app:app /app
High Memory Usage
# Check container stats
docker stats
# Restart services if needed
docker-compose restart backend
# Check for memory leaks
docker-compose exec backend ps aux --sort=-%mem | head
Slow Queries
# Enable query logging
psql -U postgres -d fotbal_club -c "
ALTER DATABASE fotbal_club SET log_min_duration_statement = 100;
"
# View slow queries
sudo tail -f /var/log/postgresql/postgresql-14-main.log | grep "duration:"
Rollback Procedure
Quick Rollback
# Stop current version
docker-compose down
# Checkout previous version
git checkout <previous-commit-hash>
# Rollback database migrations (if needed)
docker-compose run backend ./fotbal-club migrate down
# Restart with old version
docker-compose up -d
# Verify
curl http://localhost:8080/api/v1/health
Support & Contact
Log Locations
- Backend:
docker-compose logs backend - Database:
/var/log/postgresql/ - Nginx:
/var/log/nginx/fotbal-club-*.log - System:
/var/log/syslog
Useful Commands
# View real-time logs
docker-compose logs -f backend
# Check resource usage
docker stats
# Database console
docker-compose exec db psql -U postgres fotbal_club
# Restart specific service
docker-compose restart backend
# Clean up old images
docker system prune -a
Success Criteria
After deployment, verify:
- Health endpoint returns 200
- Homepage loads in < 2 seconds
- Login works
- Articles display correctly
- File uploads work
- Email sends successfully
- SSL certificate valid
- Metrics endpoint accessible
- Database backups running
- Logs are being written
Status: READY FOR PRODUCTION ✅