Add comprehensive deployment documentation for bare-metal deployment using GitLab CI/CD to DigitalOcean, including: - Server setup with deploy user - PostgreSQL database configuration - systemd service and Nginx reverse proxy - HTTPS with Let's Encrypt - Complete CI/CD pipeline with test, build, and deploy stages - Environment variables and security recommendations - Troubleshooting guide Also remove .env from git tracking (was accidentally committed before being added to .gitignore). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
8.3 KiB
8.3 KiB
GitLab CI/CD Deployment Guide
This document describes how to deploy the Wizamart 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/wizamart/
├── app/ # FastAPI application
├── static/
│ ├── admin/
│ ├── vendor/
│ ├── shop/
│ └── shared/
├── templates/
├── alembic/ # Database migrations
├── .venv/ # Python virtual environment
├── .env # Environment variables (created manually)
└── pyproject.toml
2. Server Setup
Create Deploy User (Recommended)
sudo adduser deploy --disabled-password
sudo usermod -aG sudo deploy
sudo mkdir -p /var/www/wizamart
sudo chown -R deploy:deploy /var/www/wizamart
Install System Dependencies
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)
curl -LsSf https://astral.sh/uv/install.sh | sh
source ~/.bashrc
3. Database Setup (PostgreSQL)
# Create database and user
sudo -u postgres psql << EOF
CREATE USER wizamart WITH PASSWORD 'your_secure_password';
CREATE DATABASE wizamart OWNER wizamart;
GRANT ALL PRIVILEGES ON DATABASE wizamart TO wizamart;
EOF
4. systemd Service
Create /etc/systemd/system/wizamart.service:
[Unit]
Description=Wizamart FastAPI Application
After=network.target postgresql.service
[Service]
User=deploy
Group=deploy
WorkingDirectory=/var/www/wizamart
Environment="PATH=/var/www/wizamart/.venv/bin"
EnvironmentFile=/var/www/wizamart/.env
ExecStart=/var/www/wizamart/.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:
sudo systemctl daemon-reload
sudo systemctl enable wizamart
5. Nginx Configuration
Create /etc/nginx/sites-available/wizamart:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
# Static files
location /static/ {
alias /var/www/wizamart/static/;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Media/uploads
location /uploads/ {
alias /var/www/wizamart/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:
sudo ln -s /etc/nginx/sites-available/wizamart /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
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
Certbot will automatically configure HTTPS and set up auto-renewal.
7. Firewall Configuration
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:
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 wizamart"
# Verify deployment
- ssh $SERVER_USER@$SERVER_HOST "sudo systemctl status wizamart --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/wizamart |
Mark SSH_PRIVATE_KEY as Protected and Masked.
10. Environment Variables
Create /var/www/wizamart/.env on the server:
# Application
APP_ENV=production
DEBUG=false
SECRET_KEY=your-super-secret-key-change-this
# Database
DATABASE_URL=postgresql://wizamart:password@localhost:5432/wizamart
# 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:
chmod 600 /var/www/wizamart/.env
11. Deployment Flow
- Developer pushes to
mainbranch - GitLab runs tests
- GitLab builds Tailwind CSS
- GitLab syncs files to server via rsync
- Server installs/updates Python dependencies
- Alembic runs database migrations
- systemd restarts the FastAPI service
- Nginx serves the application over HTTPS
12. Maintenance Commands
# View application logs
sudo journalctl -u wizamart -f
# Restart application
sudo systemctl restart wizamart
# Check application status
sudo systemctl status wizamart
# Run migrations manually
cd /var/www/wizamart
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
# Check logs
sudo journalctl -u wizamart -n 100
# Verify environment file
cat /var/www/wizamart/.env
# Test manually
cd /var/www/wizamart
source .venv/bin/activate
uvicorn app.main:app --host 127.0.0.1 --port 8000
Database connection issues
# Test PostgreSQL connection
psql -U wizamart -h localhost -d wizamart
# Check PostgreSQL status
sudo systemctl status postgresql
Nginx errors
# Test configuration
sudo nginx -t
# Check error logs
sudo tail -f /var/log/nginx/error.log