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>
401 lines
8.3 KiB
Markdown
401 lines
8.3 KiB
Markdown
# 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)
|
|
|
|
```bash
|
|
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
|
|
|
|
```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 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`:
|
|
|
|
```ini
|
|
[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:
|
|
|
|
```bash
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable wizamart
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Nginx Configuration
|
|
|
|
Create `/etc/nginx/sites-available/wizamart`:
|
|
|
|
```nginx
|
|
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:
|
|
|
|
```bash
|
|
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
|
|
|
|
```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 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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
chmod 600 /var/www/wizamart/.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 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
|
|
|
|
```bash
|
|
# 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
|
|
|
|
```bash
|
|
# Test PostgreSQL connection
|
|
psql -U wizamart -h localhost -d wizamart
|
|
|
|
# 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
|
|
```
|