# Deployment & Operations Comprehensive Guide ## Overview This guide consolidates all deployment, operations, and maintenance documentation for the Sojorn platform, covering infrastructure setup, deployment procedures, monitoring, and operational best practices. All code updates need to be made locally, synced to git, pulled to the server and deployed. Do not directly edit files on the server. --- ## Infrastructure Setup ### Server Requirements #### Minimum Specifications - **CPU**: 2 vCPU - **Memory**: 4GB RAM - **Storage**: 50GB SSD - **OS**: Ubuntu 22.04 LTS - **Network**: Public IP with domain pointing #### Recommended Specifications - **CPU**: 4 vCPU - **Memory**: 8GB RAM - **Storage**: 100GB SSD - **OS**: Ubuntu 22.04 LTS - **Network**: Public IP with SSL certificate ### System Dependencies #### Core Packages ```bash sudo apt update && sudo apt install -y \ postgresql \ postgresql-contrib \ postgis \ nginx \ certbot \ python3-certbot-nginx \ git \ curl \ wget \ htop ``` #### Go Environment ```bash # Install Go 1.21+ wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc source ~/.bashrc ``` ### Database Setup #### PostgreSQL Configuration ```bash # Create database sudo -u postgres createdb sojorn # Create user sudo -u postgres createuser --interactive sojorn_user # Grant privileges sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE sojorn TO sojorn_user;" # Enable extensions sudo -u postgres psql sojorn -c " CREATE EXTENSION IF NOT EXISTS uuid-ossp; CREATE EXTENSION IF NOT EXISTS pg_trgm; CREATE EXTENSION IF NOT EXISTS postgis; " ``` #### PostgreSQL Performance Tuning ```bash # Edit postgresql.conf sudo nano /etc/postgresql/15/main/postgresql.conf ``` Key settings: ```ini # Memory settings shared_buffers = 256MB effective_cache_size = 1GB work_mem = 4MB maintenance_work_mem = 64MB # Connection settings max_connections = 100 listen_addresses = 'localhost' # Logging settings log_statement = 'all' log_duration = on log_min_duration_statement = 1000 ``` --- ## Application Deployment ### Deployment Structure ``` /opt/sojorn/ ├── .env # Environment variables ├── firebase-service-account.json # FCM credentials ├── uploads/ # User uploaded files ├── logs/ # Application logs └── go-backend/ ├── bin/ │ └── api # Compiled binary ├── migrations/ # Database migrations └── scripts/ # Deployment scripts ``` ### Environment Configuration #### .env File Template ```bash # Database DATABASE_URL=postgres://sojorn_user:password@localhost:5432/sojorn?sslmode=disable # Server PORT=8080 HOST=localhost ENVIRONMENT=production # JWT JWT_SECRET=your-super-secret-jwt-key-here JWT_EXPIRY=24h REFRESH_TOKEN_EXPIRY=168h # Firebase/FCM FIREBASE_CREDENTIALS_FILE=/opt/sojorn/firebase-service-account.json FIREBASE_WEB_VAPID_KEY=BNxS7_your_vapid_key_here # Storage SENDER_API_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJhdWQiOiIxIiwianRpIjoiNT> R2_ACCOUNT_ID=7041ca6e0f40307190dc2e65e2fb5e0f R2_PUBLIC_BASE_URL=http://api.sojorn.net:8080/uploads R2_IMG_DOMAIN=img.sojorn.net R2_VID_DOMAIN=quips.sojorn.net R2_API_TOKEN=oR7Vk0Realtx0D6SAGMuYA8pXopSoCYKv8t3JEuk # Email (Optional) # Email / SendPulse SMTP_HOST=smtp-pulse.com SMTP_PORT=587 SMTP_USER=patrickbritton3@gmail.com SMTP_PASS=8s4jQBnAFTCXPNM # Logging LOG_LEVEL=info LOG_FORMAT=json ``` ### Build Process #### Compile Go Backend ```bash cd /opt/sojorn/go-backend # Build for production go build -ldflags="-s -w" -o bin/api ./cmd/api/main.go # Make executable chmod +x bin/api ``` #### Database Migrations ```bash cd /opt/sojorn/go-backend # Run migrations make migrate-up # Or manually go run ./cmd/migrate/main.go up ``` ### Systemd Service #### Service Configuration Create `/etc/systemd/system/sojorn-api.service`: ```ini [Unit] Description=Sojorn API Server After=network.target postgresql.service Requires=postgresql.service [Service] Type=simple User=patrick Group=patrick WorkingDirectory=/opt/sojorn/go-backend Environment=GO_ENV=production EnvironmentFile=/opt/sojorn/.env ExecStart=/opt/sojorn/go-backend/bin/api Restart=always RestartSec=5 StandardOutput=journal StandardError=journal SyslogIdentifier=sojorn-api # Security NoNewPrivileges=true PrivateTmp=true ProtectSystem=strict ProtectHome=true ReadWritePaths=/opt/sojorn/uploads /opt/sojorn/logs [Install] WantedBy=multi-user.target ``` #### Service Management ```bash # Reload systemd sudo systemctl daemon-reload # Enable service sudo systemctl enable sojorn-api # Start service sudo systemctl start sojorn-api # Check status sudo systemctl status sojorn-api # View logs sudo journalctl -u sojorn-api -f ``` --- ## Web Server Configuration ### Nginx Setup #### Main Configuration Create `/etc/nginx/sites-available/sojorn-api`: ```nginx server { listen 80; server_name api.sojorn.net; # Redirect to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name api.sojorn.net; # SSL Configuration ssl_certificate /etc/letsencrypt/live/api.sojorn.net/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/api.sojorn.net/privkey.pem; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # Security Headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Logging access_log /var/log/nginx/sojorn-api.access.log; error_log /var/log/nginx/sojorn-api.error.log; # Rate Limiting limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req zone=api burst=20 nodelay; # API Proxy location / { proxy_pass http://localhost: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; proxy_read_timeout 86400; } # File Uploads location /uploads/ { alias /opt/sojorn/uploads/; expires 1y; add_header Cache-Control "public, immutable"; # Security for uploads location ~* \.(php|jsp|asp|sh|py)$ { deny all; } } # Health Check location /health { access_log off; proxy_pass http://localhost:8080/health; } # WebSocket Support (for chat) location /ws { proxy_pass http://localhost: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_read_timeout 86400; proxy_send_timeout 86400; } } ``` #### Enable Site ```bash # Enable site sudo ln -s /etc/nginx/sites-available/sojorn-api /etc/nginx/sites-enabled/ # Test configuration sudo nginx -t # Reload Nginx sudo systemctl reload nginx ``` ### SSL Certificate Setup #### Let's Encrypt Certificate ```bash # Install Certbot sudo apt install certbot python3-certbot-nginx # Obtain certificate sudo certbot --nginx -d api.sojorn.net # Test renewal sudo certbot renew --dry-run # Set up auto-renewal sudo crontab -e # Add: 0 12 * * * /usr/bin/certbot renew --quiet ``` --- ## Monitoring & Logging ### Application Monitoring #### Health Check Endpoint ```go // health_check.go func (h *HealthHandler) Health(c *gin.Context) { status := map[string]interface{}{ "status": "healthy", "timestamp": time.Now(), "version": os.Getenv("APP_VERSION"), "environment": os.Getenv("ENVIRONMENT"), } // Check database if err := h.db.Ping(); err != nil { status["status"] = "unhealthy" status["database"] = "disconnected" c.JSON(http.StatusServiceUnavailable, status) return } status["database"] = "connected" c.JSON(http.StatusOK, status) } ``` #### Metrics Collection ```go // metrics.go var ( httpRequestsTotal = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total number of HTTP requests", }, []string{"method", "endpoint", "status"}, ) httpRequestDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request duration in seconds", }, []string{"method", "endpoint"}, ) ) func MetricsMiddleware() gin.HandlerFunc { return func(c *gin.Context) { start := time.Now() c.Next() duration := time.Since(start).Seconds() status := fmt.Sprintf("%d", c.Writer.Status()) httpRequestsTotal.WithLabelValues(c.Request.Method, c.FullPath(), status).Inc() httpRequestDuration.WithLabelValues(c.Request.Method, c.FullPath()).Observe(duration) } } ``` ### System Monitoring #### Setup Prometheus & Grafana (Optional) ```bash # Install Prometheus wget https://github.com/prometheus/prometheus/releases/download/v2.40.0/prometheus-2.40.0.linux-amd64.tar.gz tar xvfz prometheus-2.40.0.linux-amd64.tar.gz sudo mv prometheus-2.40.0.linux-amd64/prometheus /usr/local/bin/ sudo mv prometheus-2.40.0.linux-amd64/prometheus.yml /etc/prometheus/ # Install Grafana sudo apt-get install -y adduser libfontconfig1 wget https://dl.grafana.com/oss/release/grafana_9.3.0_amd64.deb sudo dpkg -i grafana_9.3.0_amd64.deb ``` #### Log Management ```bash # Configure log rotation sudo nano /etc/logrotate.d/sojorn-api ``` ``` /opt/sojorn/logs/*.log { daily missingok rotate 30 compress delaycompress notifempty create 644 patrick patrick postrotate systemctl reload sojorn-api endscript } ``` ### Alerting #### System Alerts Script ```bash #!/bin/bash # monitor.sh - System health monitoring # Check service status if ! systemctl is-active --quiet sojorn-api; then echo "ALERT: sojorn-api service is down" | mail -s "Sojorn Service Alert" admin@sojorn.com fi # Check disk space DISK_USAGE=$(df /opt/sojorn | awk 'NR==2 {print $5}' | sed 's/%//') if [ $DISK_USAGE -gt 80 ]; then echo "ALERT: Disk usage is ${DISK_USAGE}%" | mail -s "Sojorn Disk Alert" admin@sojorn.com fi # Check memory usage MEM_USAGE=$(free | awk 'NR==2{printf "%.0f", $3*100/$2}') if [ $MEM_USAGE -gt 90 ]; then echo "ALERT: Memory usage is ${MEM_USAGE}%" | mail -s "Sojorn Memory Alert" admin@sojorn.com fi ``` --- ## Backup & Recovery ### Database Backups #### Automated Backup Script ```bash #!/bin/bash # backup.sh - Database backup script BACKUP_DIR="/opt/sojorn/backups" DATE=$(date +%Y%m%d_%H%M%S) BACKUP_FILE="$BACKUP_DIR/sojorn_backup_$DATE.sql" # Create backup directory mkdir -p $BACKUP_DIR # Create backup pg_dump -h localhost -U sojorn_user sojorn > $BACKUP_FILE # Compress backup gzip $BACKUP_FILE # Remove old backups (keep 7 days) find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete echo "Backup completed: $BACKUP_FILE.gz" ``` #### Cron Job for Daily Backups ```bash # Add to crontab sudo crontab -e # Daily backup at 2 AM 0 2 * * * /opt/sojorn/scripts/backup.sh ``` ### File Backups #### Upload Directory Backup ```bash #!/bin/bash # backup_uploads.sh BACKUP_DIR="/opt/sojorn/backups" DATE=$(date +%Y%m%d_%H%M%S) UPLOAD_DIR="/opt/sojorn/uploads" # Create compressed backup tar -czf "$BACKUP_DIR/uploads_backup_$DATE.tar.gz" -C "$UPLOAD_DIR" . # Remove old backups (keep 7 days) find $BACKUP_DIR -name "uploads_backup_*.tar.gz" -mtime +7 -delete ``` ### Recovery Procedures #### Database Recovery ```bash # Stop application sudo systemctl stop sojorn-api # Restore database gunzip -c /opt/sojorn/backups/sojorn_backup_20260130_020000.sql.gz | \ psql -h localhost -U sojorn_user -d sojorn # Start application sudo systemctl start sojorn-api # Verify recovery curl -f https://api.sojorn.net/health ``` #### File Recovery ```bash # Restore uploads tar -xzf /opt/sojorn/backups/uploads_backup_20260130_020000.tar.gz \ -C /opt/sojorn/uploads # Fix permissions sudo chown -R patrick:patrick /opt/sojorn/uploads sudo chmod -R 755 /opt/sojorn/uploads ``` --- ## Security Hardening ### System Security #### Firewall Configuration ```bash # Install UFW sudo apt install ufw # Default policies sudo ufw default deny incoming sudo ufw default allow outgoing # Allow SSH sudo ufw allow ssh # Allow HTTP/HTTPS sudo ufw allow 80 sudo ufw allow 443 # Enable firewall sudo ufw enable ``` #### SSH Security ```bash # Edit SSH config sudo nano /etc/ssh/sshd_config ``` Key settings: ``` # Disable root login PermitRootLogin no # Use key-based authentication PasswordAuthentication no PubkeyAuthentication yes # Change default port (optional) Port 2222 # Limit login attempts MaxAuthTries 3 ``` #### Fail2Ban Setup ```bash # Install Fail2Ban sudo apt install fail2ban # Configure for SSH sudo nano /etc/fail2ban/jail.local ``` ``` [DEFAULT] bantime = 3600 findtime = 600 maxretry = 3 [sshd] enabled = true port = ssh filter = sshd logpath = /var/log/auth.log ``` ### Application Security #### Environment Variable Protection ```bash # Secure .env file sudo chmod 600 /opt/sojorn/.env sudo chown patrick:patrick /opt/sojorn/.env # Secure service account file sudo chmod 600 /opt/sojorn/firebase-service-account.json sudo chown patrick:patrick /opt/sojorn/firebase-service-account.json ``` #### SSL/TLS Hardening ```nginx # Add to Nginx config ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; ssl_session_cache shared:SSL:10m; ssl_session_timeout 10m; # HSTS add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Other security headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Referrer-Policy "strict-origin-when-cross-origin"; ``` --- ## Performance Optimization ### Database Optimization #### Query Optimization ```sql -- Add indexes for common queries CREATE INDEX CONCURRENTLY idx_posts_author_created ON posts(author_id, created_at DESC); CREATE INDEX CONCURRENTLY idx_posts_category_created ON posts(category_id, created_at DESC) WHERE category_id IS NOT NULL; CREATE INDEX CONCURRENTLY idx_follows_follower ON follows(follower_id); CREATE INDEX CONCURRENTLY idx_follows_following ON follows(following_id); -- Analyze table statistics ANALYZE posts; ANALYZE follows; ANALYZE users; ``` #### Connection Pool Tuning ```go // In database setup config, err := pgxpool.ParseConfig(databaseURL) if err != nil { return nil, err } config.MaxConns = 25 config.MinConns = 5 config.MaxConnLifetime = time.Hour config.HealthCheckPeriod = time.Minute * 5 config.MaxConnIdleTime = time.Minute * 30 ``` ### Caching Strategy #### Redis Integration (Optional) ```bash # Install Redis sudo apt install redis-server # Configure Redis sudo nano /etc/redis/redis.conf ``` Key settings: ``` # Memory management maxmemory 256mb maxmemory-policy allkeys-lru # Persistence save 900 1 save 300 10 save 60 10000 ``` #### Application Caching ```go // Cache implementation type CacheService struct { redis *redis.Client } func (c *CacheService) Set(key string, value interface{}, expiration time.Duration) error { json, err := json.Marshal(value) if err != nil { return err } return c.redis.Set(context.Background(), key, json, expiration).Err() } func (c *CacheService) Get(key string, dest interface{}) error { val, err := c.redis.Get(context.Background(), key).Result() if err != nil { return err } return json.Unmarshal([]byte(val), dest) } ``` --- ## Deployment Automation ### Deployment Script #### Automated Deploy Script ```bash #!/bin/bash # deploy.sh - Automated deployment script set -e # Exit on any error # Configuration REPO_URL="https://github.com/your-org/sojorn.git" DEPLOY_DIR="/opt/sojorn" BACKUP_DIR="/opt/sojorn/backups" SERVICE_NAME="sojorn-api" echo "Starting deployment..." # Create backup echo "Creating backup..." $DEPLOY_DIR/scripts/backup.sh # Pull latest code echo "Pulling latest code..." cd $DEPLOY_DIR git pull origin main # Build application echo "Building application..." cd go-backend go build -ldflags="-s -w" -o bin/api ./cmd/api/main.go # Run migrations echo "Running database migrations..." make migrate-up # Restart service echo "Restarting service..." sudo systemctl restart $SERVICE_NAME # Wait for service to start sleep 5 # Health check echo "Performing health check..." if curl -f http://localhost:8080/health; then echo "✅ Deployment successful!" else echo "❌ Health check failed!" echo "Rolling back..." sudo systemctl restart $SERVICE_NAME exit 1 fi echo "Deployment completed successfully!" ``` ### CI/CD Pipeline (Optional) #### GitHub Actions Example ```yaml name: Deploy to Production on: push: branches: [main] jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - name: Setup Go uses: actions/setup-go@v3 with: go-version: '1.21' - name: Build application run: | cd go-backend go build -ldflags="-s -w" -o bin/api ./cmd/api/main.go - name: Deploy to server uses: appleboy/ssh-action@v0.1.5 with: host: ${{ secrets.HOST }} username: ${{ secrets.USERNAME }} key: ${{ secrets.SSH_KEY }} script: | cd /opt/sojorn ./scripts/deploy.sh ``` --- ## Troubleshooting Common Issues ### Service Startup Issues #### Problem: Service fails to start ```bash # Check service status sudo systemctl status sojorn-api # Check logs sudo journalctl -u sojorn-api -n 50 # Common fixes # 1. Check .env file permissions sudo chmod 600 /opt/sojorn/.env # 2. Check database connection sudo -u postgres psql -c "SELECT 1;" # 3. Check port availability sudo netstat -tlnp | grep :8080 ``` #### Problem: Database connection errors ```bash # Check PostgreSQL status sudo systemctl status postgresql # Check connection string sudo cat /opt/sojorn/.env | grep DATABASE_URL # Test connection manually psql "postgres://sojorn_user:password@localhost:5432/sojorn" ``` ### Performance Issues #### Problem: Slow API responses ```bash # Check system resources top htop iostat -x 1 # Check database queries sudo -u postgres psql -c " SELECT query, mean_time, calls FROM pg_stat_statements ORDER BY mean_time DESC LIMIT 10;" # Check Nginx logs sudo tail -f /var/log/nginx/sojorn-api.access.log ``` #### Problem: High memory usage ```bash # Check memory usage free -h ps aux --sort=-%mem | head # Restart service if needed sudo systemctl restart sojorn-api # Consider tuning Go memory settings # In .env: GOMEMLIMIT=256MiB ``` ### SSL/TLS Issues #### Problem: Certificate expired ```bash # Check certificate expiry sudo certbot certificates # Renew certificate sudo certbot renew # Reload Nginx sudo systemctl reload nginx ``` #### Problem: SSL handshake failed ```bash # Test SSL configuration openssl s_client -connect api.sojorn.net:443 # Check Nginx configuration sudo nginx -t # Reload Nginx sudo systemctl reload nginx ``` --- ## Maintenance Schedule ### Daily Tasks - [ ] Check service status and logs - [ ] Monitor system resources - [ ] Verify backup completion - [ ] Review error rates ### Weekly Tasks - [ ] Update security patches - [ ] Review performance metrics - [ ] Check disk space usage - [ ] Test backup restoration ### Monthly Tasks - [ ] Update application dependencies - [ ] Review and rotate secrets - [ ] Audit user access and permissions - [ ] Performance optimization review ### Quarterly Tasks - [ ] Security audit and penetration testing - [ ] Disaster recovery testing - [ ] Capacity planning review - [ ] Documentation updates --- ## Emergency Procedures ### Service Outage Response 1. **Immediate Assessment** ```bash # Check service status sudo systemctl status sojorn-api # Check system resources top df -h # Check network connectivity ping 8.8.8.8 ``` 2. **Service Recovery** ```bash # Restart services sudo systemctl restart sojorn-api sudo systemctl restart nginx sudo systemctl restart postgresql # Verify recovery curl -f https://api.sojorn.net/health ``` 3. **Communication** - Notify stakeholders of outage - Provide ETA for resolution - Document incident and resolution ### Security Incident Response 1. **Containment** - Isolate affected systems - Preserve evidence - Change credentials 2. **Investigation** - Review logs and audit trails - Identify breach scope - Document findings 3. **Recovery** - Patch vulnerabilities - Restore from clean backups - Monitor for suspicious activity --- **Last Updated**: January 30, 2026 **Version**: 1.0 **Next Review**: February 15, 2026