# GitLab CI/CD Deployment Guide This document describes how to deploy the Orion platform to a **DigitalOcean Droplet** using **bare-metal systemd + Nginx**, with automated deployments from **GitLab CI/CD**. --- ## Prerequisites - DigitalOcean Droplet (Ubuntu 22.04+ recommended) - Domain name pointing to your server - GitLab repository with CI/CD enabled - SSH key pair for deployment --- ## 1. Server Folder Structure The application will be deployed to: ``` /var/www/orion/ ├── app/ # FastAPI application ├── static/ │ ├── admin/ │ ├── store/ │ ├── shop/ │ └── shared/ ├── templates/ ├── alembic/ # Database migrations ├── .venv/ # Python virtual environment ├── .env # Environment variables (created manually) └── pyproject.toml ``` --- ## 2. Server Setup ### Create Deploy User (Recommended) ```bash sudo adduser deploy --disabled-password sudo usermod -aG sudo deploy sudo mkdir -p /var/www/orion sudo chown -R deploy:deploy /var/www/orion ``` ### Install System Dependencies ```bash sudo apt update sudo apt install -y python3.11 python3.11-venv python3-pip \ build-essential libpq-dev nginx postgresql postgresql-contrib \ nodejs npm certbot python3-certbot-nginx ``` ### Install uv (Python Package Manager) ```bash curl -LsSf https://astral.sh/uv/install.sh | sh source ~/.bashrc ``` --- ## 3. Database Setup (PostgreSQL) ```bash # Create database and user sudo -u postgres psql << EOF CREATE USER orion WITH PASSWORD 'your_secure_password'; CREATE DATABASE orion OWNER orion; GRANT ALL PRIVILEGES ON DATABASE orion TO orion; EOF ``` --- ## 4. systemd Service Create `/etc/systemd/system/orion.service`: ```ini [Unit] Description=Orion FastAPI Application After=network.target postgresql.service [Service] User=deploy Group=deploy WorkingDirectory=/var/www/orion Environment="PATH=/var/www/orion/.venv/bin" EnvironmentFile=/var/www/orion/.env ExecStart=/var/www/orion/.venv/bin/uvicorn app.main:app --host 127.0.0.1 --port 8000 --workers 4 Restart=always RestartSec=5 [Install] WantedBy=multi-user.target ``` Enable the service: ```bash sudo systemctl daemon-reload sudo systemctl enable orion ``` --- ## 5. Nginx Configuration Create `/etc/nginx/sites-available/orion`: ```nginx server { listen 80; server_name yourdomain.com www.yourdomain.com; # Static files location /static/ { alias /var/www/orion/static/; expires 30d; add_header Cache-Control "public, immutable"; } # Media/uploads location /uploads/ { alias /var/www/orion/uploads/; expires 7d; } # Proxy to FastAPI 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; proxy_read_timeout 300; proxy_connect_timeout 300; } } ``` Enable the site: ```bash sudo ln -s /etc/nginx/sites-available/orion /etc/nginx/sites-enabled/ sudo rm /etc/nginx/sites-enabled/default # Remove default site sudo nginx -t sudo systemctl restart nginx ``` --- ## 6. HTTPS with Let's Encrypt ```bash sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com ``` Certbot will automatically configure HTTPS and set up auto-renewal. --- ## 7. Firewall Configuration ```bash sudo ufw allow OpenSSH sudo ufw allow 'Nginx Full' sudo ufw enable ``` --- ## 8. GitLab CI/CD Pipeline Create `.gitlab-ci.yml` in your project root: ```yaml stages: - test - build - deploy variables: PIP_CACHE_DIR: "$CI_PROJECT_DIR/.cache/pip" UV_CACHE_DIR: "$CI_PROJECT_DIR/.cache/uv" # Test stage test: stage: test image: python:3.11 before_script: - pip install uv - uv venv - source .venv/bin/activate - uv pip install -r requirements.txt script: - python -m pytest tests/ -v --tb=short only: - merge_requests - main # Build Tailwind CSS build: stage: build image: node:20 script: - npm install - npx tailwindcss -i ./static/src/input.css -o ./static/dist/output.css --minify artifacts: paths: - static/dist/ expire_in: 1 week only: - main # Deploy to production deploy: stage: deploy image: alpine:latest before_script: - apk add --no-cache openssh-client rsync - mkdir -p ~/.ssh - echo "$SSH_PRIVATE_KEY" | tr -d '\r' > ~/.ssh/id_ed25519 - chmod 600 ~/.ssh/id_ed25519 - ssh-keyscan -H $SERVER_HOST >> ~/.ssh/known_hosts script: # Sync files to server (excluding sensitive files) - rsync -avz --delete --exclude='.git' --exclude='.env' --exclude='*.pyc' --exclude='__pycache__' --exclude='.pytest_cache' --exclude='htmlcov' --exclude='*.db' ./ $SERVER_USER@$SERVER_HOST:$SERVER_PATH/ # Install dependencies and run migrations - ssh $SERVER_USER@$SERVER_HOST " cd $SERVER_PATH && ~/.cargo/bin/uv venv --python 3.11 && source .venv/bin/activate && ~/.cargo/bin/uv pip install -r requirements.txt && python -m alembic upgrade head " # Restart the service - ssh $SERVER_USER@$SERVER_HOST "sudo systemctl restart orion" # Verify deployment - ssh $SERVER_USER@$SERVER_HOST "sudo systemctl status orion --no-pager" only: - main environment: name: production url: https://yourdomain.com ``` --- ## 9. GitLab CI/CD Variables Configure these in **Settings > CI/CD > Variables**: | Variable | Description | Example | |----------|-------------|---------| | `SSH_PRIVATE_KEY` | Private key for server access | `-----BEGIN OPENSSH PRIVATE KEY-----...` | | `SERVER_USER` | SSH user on server | `deploy` | | `SERVER_HOST` | Server IP or hostname | `203.0.113.50` | | `SERVER_PATH` | Application directory | `/var/www/orion` | Mark `SSH_PRIVATE_KEY` as **Protected** and **Masked**. --- ## 10. Environment Variables Create `/var/www/orion/.env` on the server: ```bash # Application APP_ENV=production DEBUG=false SECRET_KEY=your-super-secret-key-change-this # Database DATABASE_URL=postgresql://orion:password@localhost:5432/orion # Stripe (if using billing) STRIPE_SECRET_KEY=sk_live_... STRIPE_WEBHOOK_SECRET=whsec_... # Email SMTP_HOST=smtp.your-provider.com SMTP_PORT=587 SMTP_USER=your-email@domain.com SMTP_PASSWORD=your-smtp-password EMAILS_FROM=noreply@yourdomain.com # Letzshop Integration (if applicable) LETZSHOP_API_URL=https://api.letzshop.lu ``` Secure the file: ```bash chmod 600 /var/www/orion/.env ``` --- ## 11. Deployment Flow 1. Developer pushes to `main` branch 2. GitLab runs tests 3. GitLab builds Tailwind CSS 4. GitLab syncs files to server via rsync 5. Server installs/updates Python dependencies 6. Alembic runs database migrations 7. systemd restarts the FastAPI service 8. Nginx serves the application over HTTPS --- ## 12. Maintenance Commands ```bash # View application logs sudo journalctl -u orion -f # Restart application sudo systemctl restart orion # Check application status sudo systemctl status orion # Run migrations manually cd /var/www/orion source .venv/bin/activate python -m alembic upgrade head # Rollback migration python -m alembic downgrade -1 ``` --- ## 13. Security Recommendations - [ ] Use a non-root deploy user (as shown above) - [ ] Enable fail2ban for SSH protection - [ ] Configure PostgreSQL to only allow local connections - [ ] Set up automated backups for the database - [ ] Enable log rotation - [ ] Consider using Docker for isolation - [ ] Set up monitoring (e.g., Prometheus + Grafana) - [ ] Configure rate limiting in Nginx --- ## 14. Troubleshooting ### Application won't start ```bash # Check logs sudo journalctl -u orion -n 100 # Verify environment file cat /var/www/orion/.env # Test manually cd /var/www/orion source .venv/bin/activate uvicorn app.main:app --host 127.0.0.1 --port 8000 ``` ### Database connection issues ```bash # Test PostgreSQL connection psql -U orion -h localhost -d orion # Check PostgreSQL status sudo systemctl status postgresql ``` ### Nginx errors ```bash # Test configuration sudo nginx -t # Check error logs sudo tail -f /var/log/nginx/error.log ```