chore: PostgreSQL migration compatibility and infrastructure improvements
Database & Migrations: - Update all Alembic migrations for PostgreSQL compatibility - Remove SQLite-specific syntax (AUTOINCREMENT, etc.) - Add database utility helpers for PostgreSQL operations - Fix services to use PostgreSQL-compatible queries Documentation: - Add comprehensive Docker deployment guide - Add production deployment documentation - Add infrastructure architecture documentation - Update database setup guide for PostgreSQL-only - Expand troubleshooting guide Architecture & Validation: - Add migration.yaml rules for SQL compatibility checking - Enhance validate_architecture.py with migration validation - Update architecture rules to validate Alembic migrations Development: - Fix duplicate install-all target in Makefile - Add Celery/Redis validation to install.py script - Add docker-compose.test.yml for CI testing - Add squash_migrations.py utility script - Update tests for PostgreSQL compatibility - Improve test fixtures in conftest.py Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,524 @@
|
||||
# Docker Deployment
|
||||
|
||||
This guide covers deploying Wizamart using Docker and Docker Compose.
|
||||
|
||||
**Best for:** Teams who want consistent environments and easy rollbacks.
|
||||
|
||||
---
|
||||
|
||||
## Development vs Production
|
||||
|
||||
| Aspect | Development | Production |
|
||||
|--------|-------------|------------|
|
||||
| Compose file | `docker-compose.yml` | `docker-compose.prod.yml` |
|
||||
| App server | Hot reload enabled | Multiple workers |
|
||||
| Database | Local volume | Persistent volume with backups |
|
||||
| SSL | Not needed | Required (via Nginx) |
|
||||
| Logging | Console | File + centralized |
|
||||
|
||||
---
|
||||
|
||||
## Development Setup
|
||||
|
||||
```bash
|
||||
# Start all services
|
||||
make docker-up
|
||||
|
||||
# Or manually
|
||||
docker compose up -d
|
||||
|
||||
# View logs
|
||||
docker compose logs -f
|
||||
|
||||
# Stop services
|
||||
make docker-down
|
||||
```
|
||||
|
||||
### Current Services
|
||||
|
||||
| Service | Port | Purpose |
|
||||
|---------|------|---------|
|
||||
| db | 5432 | PostgreSQL database |
|
||||
| redis | 6379 | Cache and queue broker |
|
||||
| api | 8000 | FastAPI application |
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### 1. Create Production Compose File
|
||||
|
||||
```yaml
|
||||
# docker-compose.prod.yml
|
||||
services:
|
||||
api:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: Dockerfile
|
||||
restart: always
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000"
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- uploads:/app/uploads
|
||||
- logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 1G
|
||||
|
||||
celery:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery worker --loglevel=info --concurrency=4
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
volumes:
|
||||
- logs:/app/logs
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
|
||||
celery-beat:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery beat --loglevel=info
|
||||
environment:
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
env_file:
|
||||
- .env
|
||||
depends_on:
|
||||
- redis
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 256M
|
||||
|
||||
db:
|
||||
image: postgres:15-alpine
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: wizamart_db
|
||||
POSTGRES_USER: wizamart_user
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U wizamart_user -d wizamart_db"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 512M
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: always
|
||||
command: redis-server --appendonly yes --maxmemory 256mb --maxmemory-policy allkeys-lru
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 300M
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./nginx/conf.d:/etc/nginx/conf.d:ro
|
||||
- ./static:/app/static:ro
|
||||
- uploads:/app/uploads:ro
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
depends_on:
|
||||
- api
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 128M
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
uploads:
|
||||
logs:
|
||||
```
|
||||
|
||||
### 2. Create Dockerfile
|
||||
|
||||
```dockerfile
|
||||
# Dockerfile
|
||||
FROM python:3.11-slim
|
||||
|
||||
# Install system dependencies
|
||||
RUN apt-get update && apt-get install -y \
|
||||
curl \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install Tailwind CLI
|
||||
RUN curl -sLO https://github.com/tailwindlabs/tailwindcss/releases/latest/download/tailwindcss-linux-x64 \
|
||||
&& chmod +x tailwindcss-linux-x64 \
|
||||
&& mv tailwindcss-linux-x64 /usr/local/bin/tailwindcss
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install Python dependencies
|
||||
COPY requirements.txt .
|
||||
RUN pip install --no-cache-dir -r requirements.txt
|
||||
|
||||
# Copy application
|
||||
COPY . .
|
||||
|
||||
# Build Tailwind CSS
|
||||
RUN tailwindcss -i ./static/admin/css/tailwind.css -o ./static/admin/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/vendor/css/tailwind.css -o ./static/vendor/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/shop/css/tailwind.css -o ./static/shop/css/tailwind.output.css --minify \
|
||||
&& tailwindcss -i ./static/platform/css/tailwind.css -o ./static/platform/css/tailwind.output.css --minify
|
||||
|
||||
# Create non-root user
|
||||
RUN useradd -m -u 1000 wizamart && chown -R wizamart:wizamart /app
|
||||
USER wizamart
|
||||
|
||||
EXPOSE 8000
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
|
||||
```
|
||||
|
||||
### 3. Nginx Configuration
|
||||
|
||||
```bash
|
||||
mkdir -p nginx/conf.d
|
||||
```
|
||||
|
||||
```nginx
|
||||
# nginx/conf.d/wizamart.conf
|
||||
upstream api {
|
||||
server api:8000;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
|
||||
# Static files
|
||||
location /static {
|
||||
alias /app/static;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
location /uploads {
|
||||
alias /app/uploads;
|
||||
expires 7d;
|
||||
}
|
||||
|
||||
location / {
|
||||
proxy_pass http://api;
|
||||
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_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 4. Deploy
|
||||
|
||||
```bash
|
||||
# Create .env file with production values
|
||||
cp .env.example .env
|
||||
nano .env
|
||||
|
||||
# Set database password
|
||||
export DB_PASSWORD=$(openssl rand -hex 16)
|
||||
echo "DB_PASSWORD=$DB_PASSWORD" >> .env
|
||||
|
||||
# Build and start
|
||||
docker compose -f docker-compose.prod.yml build
|
||||
docker compose -f docker-compose.prod.yml up -d
|
||||
|
||||
# Run migrations
|
||||
docker compose -f docker-compose.prod.yml exec api alembic upgrade head
|
||||
|
||||
# Initialize data
|
||||
docker compose -f docker-compose.prod.yml exec api python scripts/init_production.py
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daily Operations
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# All services
|
||||
docker compose -f docker-compose.prod.yml logs -f
|
||||
|
||||
# Specific service
|
||||
docker compose -f docker-compose.prod.yml logs -f api
|
||||
docker compose -f docker-compose.prod.yml logs -f celery
|
||||
|
||||
# Last 100 lines
|
||||
docker compose -f docker-compose.prod.yml logs --tail 100 api
|
||||
```
|
||||
|
||||
### Access Container Shell
|
||||
|
||||
```bash
|
||||
# API container
|
||||
docker compose -f docker-compose.prod.yml exec api bash
|
||||
|
||||
# Database
|
||||
docker compose -f docker-compose.prod.yml exec db psql -U wizamart_user -d wizamart_db
|
||||
|
||||
# Redis
|
||||
docker compose -f docker-compose.prod.yml exec redis redis-cli
|
||||
```
|
||||
|
||||
### Restart Services
|
||||
|
||||
```bash
|
||||
# Single service
|
||||
docker compose -f docker-compose.prod.yml restart api
|
||||
|
||||
# All services
|
||||
docker compose -f docker-compose.prod.yml restart
|
||||
```
|
||||
|
||||
### Deploy Updates
|
||||
|
||||
```bash
|
||||
# Pull latest code
|
||||
git pull origin main
|
||||
|
||||
# Rebuild and restart
|
||||
docker compose -f docker-compose.prod.yml build api celery
|
||||
docker compose -f docker-compose.prod.yml up -d api celery
|
||||
|
||||
# Run migrations if needed
|
||||
docker compose -f docker-compose.prod.yml exec api alembic upgrade head
|
||||
```
|
||||
|
||||
### Rollback
|
||||
|
||||
```bash
|
||||
# View image history
|
||||
docker images wizamart-api
|
||||
|
||||
# Tag current as backup
|
||||
docker tag wizamart-api:latest wizamart-api:backup
|
||||
|
||||
# Rollback to previous
|
||||
docker compose -f docker-compose.prod.yml down api
|
||||
docker tag wizamart-api:previous wizamart-api:latest
|
||||
docker compose -f docker-compose.prod.yml up -d api
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backups
|
||||
|
||||
### Database Backup
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
docker compose -f docker-compose.prod.yml exec db pg_dump -U wizamart_user wizamart_db | gzip > backup_$(date +%Y%m%d).sql.gz
|
||||
|
||||
# Restore backup
|
||||
gunzip -c backup_20240115.sql.gz | docker compose -f docker-compose.prod.yml exec -T db psql -U wizamart_user -d wizamart_db
|
||||
```
|
||||
|
||||
### Volume Backup
|
||||
|
||||
```bash
|
||||
# Backup all volumes
|
||||
docker run --rm \
|
||||
-v wizamart_postgres_data:/data \
|
||||
-v $(pwd)/backups:/backup \
|
||||
alpine tar czf /backup/postgres_$(date +%Y%m%d).tar.gz /data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Resource Usage
|
||||
|
||||
```bash
|
||||
docker stats
|
||||
```
|
||||
|
||||
### Health Checks
|
||||
|
||||
```bash
|
||||
# Check service health
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
|
||||
# Test API health
|
||||
curl -s http://localhost:8000/health | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Container Won't Start
|
||||
|
||||
```bash
|
||||
# Check logs
|
||||
docker compose -f docker-compose.prod.yml logs api
|
||||
|
||||
# Check container status
|
||||
docker compose -f docker-compose.prod.yml ps -a
|
||||
|
||||
# Inspect container
|
||||
docker inspect <container_id>
|
||||
```
|
||||
|
||||
### Database Connection Issues
|
||||
|
||||
```bash
|
||||
# Test from API container
|
||||
docker compose -f docker-compose.prod.yml exec api python -c "
|
||||
from app.core.database import engine
|
||||
with engine.connect() as conn:
|
||||
print('Connected!')
|
||||
"
|
||||
```
|
||||
|
||||
### Out of Disk Space
|
||||
|
||||
```bash
|
||||
# Check disk usage
|
||||
docker system df
|
||||
|
||||
# Clean up
|
||||
docker system prune -a --volumes
|
||||
```
|
||||
|
||||
### Memory Issues
|
||||
|
||||
```bash
|
||||
# Check memory usage
|
||||
docker stats --no-stream
|
||||
|
||||
# Increase limits in docker-compose.prod.yml
|
||||
deploy:
|
||||
resources:
|
||||
limits:
|
||||
memory: 2G
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
### Non-Root User
|
||||
|
||||
All containers run as non-root users. The Dockerfile creates a `wizamart` user.
|
||||
|
||||
### Secret Management
|
||||
|
||||
```bash
|
||||
# Use Docker secrets (Swarm mode)
|
||||
echo "your-password" | docker secret create db_password -
|
||||
|
||||
# Or use environment files
|
||||
# Never commit .env to git
|
||||
```
|
||||
|
||||
### Network Isolation
|
||||
|
||||
```yaml
|
||||
# Add to docker-compose.prod.yml
|
||||
networks:
|
||||
frontend:
|
||||
backend:
|
||||
|
||||
services:
|
||||
nginx:
|
||||
networks:
|
||||
- frontend
|
||||
api:
|
||||
networks:
|
||||
- frontend
|
||||
- backend
|
||||
db:
|
||||
networks:
|
||||
- backend
|
||||
redis:
|
||||
networks:
|
||||
- backend
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Scaling
|
||||
|
||||
### Horizontal Scaling
|
||||
|
||||
```bash
|
||||
# Scale API containers
|
||||
docker compose -f docker-compose.prod.yml up -d --scale api=3
|
||||
|
||||
# Update nginx upstream
|
||||
upstream api {
|
||||
server api_1:8000;
|
||||
server api_2:8000;
|
||||
server api_3:8000;
|
||||
}
|
||||
```
|
||||
|
||||
### Moving to Kubernetes
|
||||
|
||||
When you outgrow Docker Compose, see our Kubernetes migration guide (coming soon).
|
||||
|
||||
@@ -2,13 +2,24 @@
|
||||
|
||||
This guide covers deploying the Wizamart platform to production environments.
|
||||
|
||||
!!! tip "New to deployment?"
|
||||
Start with the [Infrastructure Guide](infrastructure.md) for a complete overview of architecture options.
|
||||
|
||||
## Deployment Options
|
||||
|
||||
| Option | Best For | Guide |
|
||||
|--------|----------|-------|
|
||||
| **Traditional VPS** | Direct server access, debugging | [Production Guide](production.md) |
|
||||
| **Docker Compose** | Consistent environments, easy rollbacks | [Docker Guide](docker.md) |
|
||||
| **Managed Services** | Minimal ops, small teams | See [Infrastructure Guide](infrastructure.md#option-3-managed-services-minimal-ops) |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.11+
|
||||
- PostgreSQL 14+ (production) or SQLite (development)
|
||||
- Redis (optional, for caching/sessions)
|
||||
- PostgreSQL 15+ (required - SQLite not supported)
|
||||
- Redis (required for Celery background jobs)
|
||||
- Docker (for development database)
|
||||
- Tailwind CSS CLI (standalone binary)
|
||||
- uv package manager
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
|
||||
845
docs/deployment/infrastructure.md
Normal file
845
docs/deployment/infrastructure.md
Normal file
@@ -0,0 +1,845 @@
|
||||
# Infrastructure Guide
|
||||
|
||||
This guide documents the complete infrastructure for the Wizamart platform, from development to high-end production.
|
||||
|
||||
**Philosophy:** We prioritize **debuggability and operational simplicity** over complexity. Every component should be directly accessible for troubleshooting.
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Architecture Overview](#architecture-overview)
|
||||
- [Current State](#current-state)
|
||||
- [Development Environment](#development-environment)
|
||||
- [Production Options](#production-options)
|
||||
- [Future High-End Architecture](#future-high-end-architecture)
|
||||
- [Component Deep Dives](#component-deep-dives)
|
||||
- [Troubleshooting Guide](#troubleshooting-guide)
|
||||
- [Decision Matrix](#decision-matrix)
|
||||
|
||||
---
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### System Components
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ CLIENTS │
|
||||
│ (Browsers, Mobile Apps, API Consumers) │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ LOAD BALANCER / PROXY │
|
||||
│ (Nginx, Caddy, or Cloud LB) │
|
||||
│ - SSL termination │
|
||||
│ - Static file serving │
|
||||
│ - Rate limiting │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│
|
||||
┌───────────────┼───────────────┐
|
||||
▼ ▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────┐
|
||||
│ APPLICATION SERVERS │
|
||||
│ (FastAPI + Uvicorn) │
|
||||
│ - API endpoints │
|
||||
│ - HTML rendering (Jinja2) │
|
||||
│ - WebSocket connections │
|
||||
└─────────────────────────────────────────────────────────────────────────┘
|
||||
│ │ │
|
||||
▼ ▼ ▼
|
||||
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
||||
│ PostgreSQL │ │ Redis │ │ File Storage │
|
||||
│ (Primary DB) │ │ (Cache/Queue) │ │ (S3/Local) │
|
||||
└──────────────────┘ └──────────────────┘ └──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────────┐
|
||||
│ Celery Workers │
|
||||
│ (Background Jobs)│
|
||||
└──────────────────┘
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. **Request** → Nginx → Uvicorn → FastAPI → Service Layer → Database
|
||||
2. **Background Job** → API creates task → Redis Queue → Celery Worker → Database
|
||||
3. **Static Files** → Nginx serves directly (or CDN in production)
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
### What We Have Now
|
||||
|
||||
| Component | Technology | Status |
|
||||
|-----------|------------|--------|
|
||||
| Web Framework | FastAPI + Uvicorn | ✅ Production Ready |
|
||||
| Database | PostgreSQL 15 | ✅ Production Ready |
|
||||
| ORM | SQLAlchemy 2.0 | ✅ Production Ready |
|
||||
| Migrations | Alembic | ✅ Production Ready |
|
||||
| Templates | Jinja2 + Tailwind CSS | ✅ Production Ready |
|
||||
| Authentication | JWT (PyJWT) | ✅ Production Ready |
|
||||
| Email | SMTP/SendGrid/Mailgun/SES | ✅ Production Ready |
|
||||
| Payments | Stripe | ✅ Production Ready |
|
||||
| Background Jobs | - | ⏳ Planned (Celery) |
|
||||
| Caching | - | ⏳ Planned (Redis) |
|
||||
| File Storage | Local filesystem | ⏳ Needs S3 for prod |
|
||||
|
||||
### What We Need to Add
|
||||
|
||||
| Component | Priority | Reason |
|
||||
|-----------|----------|--------|
|
||||
| Redis | High | Session cache, Celery broker |
|
||||
| Celery | High | Background jobs (imports, emails, reports) |
|
||||
| S3/MinIO | Medium | Scalable file storage |
|
||||
| Sentry | Medium | Error tracking |
|
||||
| Prometheus/Grafana | Low | Metrics and dashboards |
|
||||
|
||||
---
|
||||
|
||||
## Development Environment
|
||||
|
||||
### Local Setup (Recommended)
|
||||
|
||||
```bash
|
||||
# 1. Start PostgreSQL
|
||||
make docker-up
|
||||
|
||||
# 2. Run migrations
|
||||
make migrate-up
|
||||
|
||||
# 3. Initialize data
|
||||
make init-prod
|
||||
|
||||
# 4. Start development server
|
||||
make dev
|
||||
|
||||
# 5. (Optional) Run tests
|
||||
make test
|
||||
```
|
||||
|
||||
### Services Running Locally
|
||||
|
||||
| Service | Host | Port | Purpose |
|
||||
|---------|------|------|---------|
|
||||
| FastAPI | localhost | 8000 | Main application |
|
||||
| PostgreSQL | localhost | 5432 | Development database |
|
||||
| PostgreSQL (test) | localhost | 5433 | Test database |
|
||||
| MkDocs | localhost | 8001 | Documentation |
|
||||
|
||||
### Docker Compose Services
|
||||
|
||||
```yaml
|
||||
# docker-compose.yml
|
||||
services:
|
||||
db: # PostgreSQL for development
|
||||
redis: # Redis for cache/queue (coming soon)
|
||||
api: # FastAPI application (optional)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Production Options
|
||||
|
||||
### Option 1: Traditional VPS (Recommended for Troubleshooting)
|
||||
|
||||
**Best for:** Teams who want direct server access, familiar with Linux administration.
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ VPS (4GB+ RAM) │
|
||||
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Nginx │ │ Uvicorn │ │ PostgreSQL │ │
|
||||
│ │ (reverse │ │ (4 workers)│ │ (local) │ │
|
||||
│ │ proxy) │ │ │ │ │ │
|
||||
│ └─────────────┘ └─────────────┘ └─────────────┘ │
|
||||
│ │ │ │ │
|
||||
│ └────────────────┼────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌─────────────┐ ┌─────────────┐ │
|
||||
│ │ Redis │ │ Celery │ │
|
||||
│ │ (local) │ │ (workers) │ │
|
||||
│ └─────────────┘ └─────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Setup:**
|
||||
|
||||
```bash
|
||||
# On Ubuntu 22.04+ VPS
|
||||
|
||||
# 1. Install system packages
|
||||
sudo apt update
|
||||
sudo apt install -y nginx postgresql-15 redis-server python3.11 python3.11-venv
|
||||
|
||||
# 2. Create application user
|
||||
sudo useradd -m -s /bin/bash wizamart
|
||||
sudo su - wizamart
|
||||
|
||||
# 3. Clone and setup
|
||||
git clone <repo> /home/wizamart/app
|
||||
cd /home/wizamart/app
|
||||
python3.11 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 4. Configure environment
|
||||
cp .env.example .env
|
||||
nano .env # Edit with production values
|
||||
|
||||
# 5. Setup database
|
||||
sudo -u postgres createuser wizamart_user
|
||||
sudo -u postgres createdb wizamart_db -O wizamart_user
|
||||
alembic upgrade head
|
||||
python scripts/init_production.py
|
||||
|
||||
# 6. Create systemd service
|
||||
sudo nano /etc/systemd/system/wizamart.service
|
||||
```
|
||||
|
||||
**Systemd Service:**
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/wizamart.service
|
||||
[Unit]
|
||||
Description=Wizamart API
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=wizamart
|
||||
Group=wizamart
|
||||
WorkingDirectory=/home/wizamart/app
|
||||
Environment="PATH=/home/wizamart/app/.venv/bin"
|
||||
EnvironmentFile=/home/wizamart/app/.env
|
||||
ExecStart=/home/wizamart/app/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 4
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Celery Workers:**
|
||||
|
||||
```ini
|
||||
# /etc/systemd/system/wizamart-celery.service
|
||||
[Unit]
|
||||
Description=Wizamart Celery Worker
|
||||
After=network.target redis.service
|
||||
|
||||
[Service]
|
||||
User=wizamart
|
||||
Group=wizamart
|
||||
WorkingDirectory=/home/wizamart/app
|
||||
Environment="PATH=/home/wizamart/app/.venv/bin"
|
||||
EnvironmentFile=/home/wizamart/app/.env
|
||||
ExecStart=/home/wizamart/app/.venv/bin/celery -A app.celery worker --loglevel=info --concurrency=4
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
**Nginx Configuration:**
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/sites-available/wizamart
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
|
||||
# Static files (served directly by Nginx)
|
||||
location /static {
|
||||
alias /home/wizamart/app/static;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
}
|
||||
|
||||
# Uploaded files
|
||||
location /uploads {
|
||||
alias /home/wizamart/app/uploads;
|
||||
expires 7d;
|
||||
}
|
||||
|
||||
# API and application
|
||||
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;
|
||||
|
||||
# WebSocket support (for future real-time features)
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Troubleshooting Commands:**
|
||||
|
||||
```bash
|
||||
# Check service status
|
||||
sudo systemctl status wizamart
|
||||
sudo systemctl status wizamart-celery
|
||||
sudo systemctl status postgresql
|
||||
sudo systemctl status redis
|
||||
|
||||
# View logs
|
||||
sudo journalctl -u wizamart -f
|
||||
sudo journalctl -u wizamart-celery -f
|
||||
|
||||
# Connect to database directly
|
||||
sudo -u postgres psql wizamart_db
|
||||
|
||||
# Check Redis
|
||||
redis-cli ping
|
||||
redis-cli monitor # Watch commands in real-time
|
||||
|
||||
# Restart services
|
||||
sudo systemctl restart wizamart
|
||||
sudo systemctl restart wizamart-celery
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Option 2: Docker Compose Production
|
||||
|
||||
**Best for:** Consistent environments, easy rollbacks, container familiarity.
|
||||
|
||||
```yaml
|
||||
# docker-compose.prod.yml
|
||||
services:
|
||||
api:
|
||||
build: .
|
||||
restart: always
|
||||
ports:
|
||||
- "127.0.0.1:8000:8000"
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- ./uploads:/app/uploads
|
||||
- ./logs:/app/logs
|
||||
healthcheck:
|
||||
test: ["CMD", "curl", "-f", "http://localhost:8000/health"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
|
||||
celery:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery worker --loglevel=info --concurrency=4
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
REDIS_URL: redis://redis:6379/0
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
depends_on:
|
||||
- db
|
||||
- redis
|
||||
volumes:
|
||||
- ./logs:/app/logs
|
||||
|
||||
celery-beat:
|
||||
build: .
|
||||
restart: always
|
||||
command: celery -A app.celery beat --loglevel=info
|
||||
environment:
|
||||
DATABASE_URL: postgresql://wizamart_user:${DB_PASSWORD}@db:5432/wizamart_db
|
||||
CELERY_BROKER_URL: redis://redis:6379/1
|
||||
depends_on:
|
||||
- redis
|
||||
|
||||
db:
|
||||
image: postgres:15
|
||||
restart: always
|
||||
environment:
|
||||
POSTGRES_DB: wizamart_db
|
||||
POSTGRES_USER: wizamart_user
|
||||
POSTGRES_PASSWORD: ${DB_PASSWORD}
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U wizamart_user -d wizamart_db"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
redis:
|
||||
image: redis:7-alpine
|
||||
restart: always
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
healthcheck:
|
||||
test: ["CMD", "redis-cli", "ping"]
|
||||
interval: 10s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
|
||||
nginx:
|
||||
image: nginx:alpine
|
||||
restart: always
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
volumes:
|
||||
- ./nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
- ./static:/app/static:ro
|
||||
- ./uploads:/app/uploads:ro
|
||||
- /etc/letsencrypt:/etc/letsencrypt:ro
|
||||
depends_on:
|
||||
- api
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
redis_data:
|
||||
```
|
||||
|
||||
**Troubleshooting Commands:**
|
||||
|
||||
```bash
|
||||
# View all containers
|
||||
docker compose -f docker-compose.prod.yml ps
|
||||
|
||||
# View logs
|
||||
docker compose -f docker-compose.prod.yml logs -f api
|
||||
docker compose -f docker-compose.prod.yml logs -f celery
|
||||
|
||||
# Access container shell
|
||||
docker compose -f docker-compose.prod.yml exec api bash
|
||||
docker compose -f docker-compose.prod.yml exec db psql -U wizamart_user -d wizamart_db
|
||||
|
||||
# Restart specific service
|
||||
docker compose -f docker-compose.prod.yml restart api
|
||||
|
||||
# View resource usage
|
||||
docker stats
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Option 3: Managed Services (Minimal Ops)
|
||||
|
||||
**Best for:** Small teams, focus on product not infrastructure.
|
||||
|
||||
| Component | Service | Cost (approx) |
|
||||
|-----------|---------|---------------|
|
||||
| App Hosting | Railway / Render / Fly.io | $5-25/mo |
|
||||
| Database | Neon / Supabase / PlanetScale | $0-25/mo |
|
||||
| Redis | Upstash / Redis Cloud | $0-10/mo |
|
||||
| File Storage | Cloudflare R2 / AWS S3 | $0-5/mo |
|
||||
| Email | Resend / SendGrid | $0-20/mo |
|
||||
|
||||
**Example: Railway + Neon**
|
||||
|
||||
```bash
|
||||
# Deploy to Railway
|
||||
railway login
|
||||
railway init
|
||||
railway up
|
||||
|
||||
# Configure environment
|
||||
railway variables set DATABASE_URL="postgresql://..."
|
||||
railway variables set REDIS_URL="redis://..."
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Future High-End Architecture
|
||||
|
||||
### Target Production Architecture
|
||||
|
||||
```
|
||||
┌─────────────────┐
|
||||
│ CloudFlare │
|
||||
│ (CDN + WAF) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌────────▼────────┐
|
||||
│ Load Balancer │
|
||||
│ (HA Proxy/ALB) │
|
||||
└────────┬────────┘
|
||||
│
|
||||
┌──────────────────────┼──────────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
||||
│ API Server 1 │ │ API Server 2 │ │ API Server N │
|
||||
│ (Uvicorn) │ │ (Uvicorn) │ │ (Uvicorn) │
|
||||
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
|
||||
│ │ │
|
||||
└──────────────────────┼──────────────────────┘
|
||||
│
|
||||
┌───────────────────────────┼───────────────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
||||
│ PostgreSQL │ │ Redis │ │ S3 / MinIO │
|
||||
│ (Primary) │ │ (Cluster) │ │ (Files) │
|
||||
│ │ │ │ │ │ │
|
||||
│ ┌────▼────┐ │ │ ┌─────────┐ │ │ │
|
||||
│ │ Replica │ │ │ │ Sentinel│ │ │ │
|
||||
│ └─────────┘ │ │ └─────────┘ │ │ │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
│
|
||||
┌──────────────────────┼──────────────────────┐
|
||||
│ │ │
|
||||
┌────────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
|
||||
│ Celery Worker 1 │ │ Celery Worker 2 │ │ Celery Beat │
|
||||
│ (General) │ │ (Import Jobs) │ │ (Scheduler) │
|
||||
└─────────────────┘ └─────────────────┘ └─────────────────┘
|
||||
|
||||
┌─────────────────────────────┐
|
||||
│ Monitoring Stack │
|
||||
│ ┌─────────┐ ┌───────────┐ │
|
||||
│ │Prometheus│ │ Grafana │ │
|
||||
│ └─────────┘ └───────────┘ │
|
||||
│ ┌─────────┐ ┌───────────┐ │
|
||||
│ │ Sentry │ │ Loki │ │
|
||||
│ └─────────┘ └───────────┘ │
|
||||
└─────────────────────────────┘
|
||||
```
|
||||
|
||||
### Celery Task Queues
|
||||
|
||||
```python
|
||||
# app/celery.py (to be implemented)
|
||||
from celery import Celery
|
||||
|
||||
celery_app = Celery(
|
||||
"wizamart",
|
||||
broker=settings.celery_broker_url,
|
||||
backend=settings.celery_result_backend,
|
||||
)
|
||||
|
||||
celery_app.conf.task_queues = {
|
||||
"default": {"exchange": "default", "routing_key": "default"},
|
||||
"imports": {"exchange": "imports", "routing_key": "imports"},
|
||||
"emails": {"exchange": "emails", "routing_key": "emails"},
|
||||
"reports": {"exchange": "reports", "routing_key": "reports"},
|
||||
}
|
||||
|
||||
celery_app.conf.task_routes = {
|
||||
"app.tasks.import_letzshop_products": {"queue": "imports"},
|
||||
"app.tasks.send_email": {"queue": "emails"},
|
||||
"app.tasks.generate_report": {"queue": "reports"},
|
||||
}
|
||||
```
|
||||
|
||||
### Background Tasks to Implement
|
||||
|
||||
| Task | Queue | Priority | Description |
|
||||
|------|-------|----------|-------------|
|
||||
| `import_letzshop_products` | imports | High | Marketplace product sync |
|
||||
| `import_letzshop_orders` | imports | High | Order sync from Letzshop |
|
||||
| `send_order_confirmation` | emails | High | Order emails |
|
||||
| `send_password_reset` | emails | High | Auth emails |
|
||||
| `send_invoice_email` | emails | Medium | Invoice delivery |
|
||||
| `generate_sales_report` | reports | Low | Analytics reports |
|
||||
| `cleanup_expired_sessions` | default | Low | Maintenance |
|
||||
| `sync_stripe_subscriptions` | default | Medium | Billing sync |
|
||||
|
||||
---
|
||||
|
||||
## Component Deep Dives
|
||||
|
||||
### PostgreSQL Configuration
|
||||
|
||||
**Production Settings (`postgresql.conf`):**
|
||||
|
||||
```ini
|
||||
# Memory (adjust based on server RAM)
|
||||
shared_buffers = 256MB # 25% of RAM for dedicated DB server
|
||||
effective_cache_size = 768MB # 75% of RAM
|
||||
work_mem = 16MB
|
||||
maintenance_work_mem = 128MB
|
||||
|
||||
# Connections
|
||||
max_connections = 100
|
||||
|
||||
# Write-Ahead Log
|
||||
wal_level = replica
|
||||
max_wal_senders = 3
|
||||
|
||||
# Query Planning
|
||||
random_page_cost = 1.1 # For SSD storage
|
||||
effective_io_concurrency = 200 # For SSD storage
|
||||
|
||||
# Logging
|
||||
log_min_duration_statement = 1000 # Log queries > 1 second
|
||||
log_line_prefix = '%t [%p]: [%l-1] user=%u,db=%d '
|
||||
```
|
||||
|
||||
**Backup Strategy:**
|
||||
|
||||
```bash
|
||||
# Daily backup script
|
||||
#!/bin/bash
|
||||
BACKUP_DIR=/backups/postgresql
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
pg_dump -U wizamart_user wizamart_db | gzip > $BACKUP_DIR/wizamart_$DATE.sql.gz
|
||||
|
||||
# Keep last 7 days
|
||||
find $BACKUP_DIR -name "*.sql.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
### Redis Configuration
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
| Use Case | Database | TTL | Description |
|
||||
|----------|----------|-----|-------------|
|
||||
| Session Cache | 0 | 24h | User sessions |
|
||||
| API Rate Limiting | 0 | 1h | Request counters |
|
||||
| Celery Broker | 1 | - | Task queue |
|
||||
| Celery Results | 2 | 24h | Task results |
|
||||
| Feature Flags | 3 | 5m | Feature gate cache |
|
||||
|
||||
**Configuration (`redis.conf`):**
|
||||
|
||||
```ini
|
||||
maxmemory 256mb
|
||||
maxmemory-policy allkeys-lru
|
||||
appendonly yes
|
||||
appendfsync everysec
|
||||
```
|
||||
|
||||
### Nginx Tuning
|
||||
|
||||
```nginx
|
||||
# /etc/nginx/nginx.conf
|
||||
worker_processes auto;
|
||||
worker_rlimit_nofile 65535;
|
||||
|
||||
events {
|
||||
worker_connections 4096;
|
||||
use epoll;
|
||||
multi_accept on;
|
||||
}
|
||||
|
||||
http {
|
||||
# Buffers
|
||||
client_body_buffer_size 10K;
|
||||
client_header_buffer_size 1k;
|
||||
client_max_body_size 50M;
|
||||
large_client_header_buffers 2 1k;
|
||||
|
||||
# Timeouts
|
||||
client_body_timeout 12;
|
||||
client_header_timeout 12;
|
||||
keepalive_timeout 15;
|
||||
send_timeout 10;
|
||||
|
||||
# Gzip
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Quick Diagnostics
|
||||
|
||||
```bash
|
||||
# Check all services
|
||||
systemctl status wizamart wizamart-celery postgresql redis nginx
|
||||
|
||||
# Check ports
|
||||
ss -tlnp | grep -E '(8000|5432|6379|80|443)'
|
||||
|
||||
# Check disk space
|
||||
df -h
|
||||
|
||||
# Check memory
|
||||
free -h
|
||||
|
||||
# Check CPU/processes
|
||||
htop
|
||||
```
|
||||
|
||||
### Database Issues
|
||||
|
||||
```bash
|
||||
# Connect to database
|
||||
sudo -u postgres psql wizamart_db
|
||||
|
||||
# Check active connections
|
||||
SELECT count(*) FROM pg_stat_activity;
|
||||
|
||||
# Find slow queries
|
||||
SELECT pid, now() - pg_stat_activity.query_start AS duration, query
|
||||
FROM pg_stat_activity
|
||||
WHERE state != 'idle'
|
||||
ORDER BY duration DESC;
|
||||
|
||||
# Kill stuck query
|
||||
SELECT pg_terminate_backend(pid);
|
||||
|
||||
# Check table sizes
|
||||
SELECT relname, pg_size_pretty(pg_total_relation_size(relid))
|
||||
FROM pg_catalog.pg_statio_user_tables
|
||||
ORDER BY pg_total_relation_size(relid) DESC;
|
||||
|
||||
# Analyze query performance
|
||||
EXPLAIN ANALYZE SELECT ...;
|
||||
```
|
||||
|
||||
### Redis Issues
|
||||
|
||||
```bash
|
||||
# Check connectivity
|
||||
redis-cli ping
|
||||
|
||||
# Monitor real-time commands
|
||||
redis-cli monitor
|
||||
|
||||
# Check memory usage
|
||||
redis-cli info memory
|
||||
|
||||
# List all keys (careful in production!)
|
||||
redis-cli --scan
|
||||
|
||||
# Check queue lengths
|
||||
redis-cli llen celery
|
||||
|
||||
# Flush specific database
|
||||
redis-cli -n 1 flushdb # Flush Celery broker
|
||||
```
|
||||
|
||||
### Celery Issues
|
||||
|
||||
```bash
|
||||
# Check worker status
|
||||
celery -A app.celery inspect active
|
||||
celery -A app.celery inspect reserved
|
||||
celery -A app.celery inspect stats
|
||||
|
||||
# Purge all pending tasks
|
||||
celery -A app.celery purge
|
||||
|
||||
# List registered tasks
|
||||
celery -A app.celery inspect registered
|
||||
```
|
||||
|
||||
### Application Issues
|
||||
|
||||
```bash
|
||||
# Check API health
|
||||
curl -s http://localhost:8000/health | jq
|
||||
|
||||
# View recent logs
|
||||
journalctl -u wizamart --since "10 minutes ago"
|
||||
|
||||
# Check for Python errors
|
||||
journalctl -u wizamart | grep -i error | tail -20
|
||||
|
||||
# Test database connection
|
||||
python -c "from app.core.database import engine; print(engine.connect())"
|
||||
```
|
||||
|
||||
### Common Problems & Solutions
|
||||
|
||||
| Problem | Diagnosis | Solution |
|
||||
|---------|-----------|----------|
|
||||
| 502 Bad Gateway | `systemctl status wizamart` | Restart app: `systemctl restart wizamart` |
|
||||
| Database connection refused | `pg_isready` | Start PostgreSQL: `systemctl start postgresql` |
|
||||
| High memory usage | `free -h`, `ps aux --sort=-%mem` | Restart app, check for memory leaks |
|
||||
| Slow queries | PostgreSQL slow query log | Add indexes, optimize queries |
|
||||
| Celery tasks stuck | `celery inspect active` | Restart workers, check Redis |
|
||||
| Disk full | `df -h` | Clean logs, backups, temp files |
|
||||
|
||||
---
|
||||
|
||||
## Decision Matrix
|
||||
|
||||
### When to Use Each Option
|
||||
|
||||
| Scenario | Recommended | Reason |
|
||||
|----------|-------------|--------|
|
||||
| Solo developer, MVP | Managed (Railway) | Focus on product |
|
||||
| Small team, budget conscious | Traditional VPS | Full control, low cost |
|
||||
| Need direct DB access for debugging | Traditional VPS | Direct psql access |
|
||||
| Familiar with Docker, want consistency | Docker Compose | Reproducible environments |
|
||||
| High availability required | Docker + Orchestration | Easy scaling |
|
||||
| Enterprise, compliance requirements | Kubernetes | Full orchestration |
|
||||
|
||||
### Cost Comparison (Monthly)
|
||||
|
||||
| Setup | Low Traffic | Medium | High |
|
||||
|-------|-------------|--------|------|
|
||||
| Managed (Railway + Neon) | $10 | $50 | $200+ |
|
||||
| VPS (Hetzner/DigitalOcean) | $5 | $20 | $80 |
|
||||
| Docker on VPS | $5 | $20 | $80 |
|
||||
| AWS/GCP Full Stack | $50 | $200 | $1000+ |
|
||||
|
||||
---
|
||||
|
||||
## Migration Path
|
||||
|
||||
### Phase 1: Current (Development)
|
||||
- ✅ PostgreSQL (Docker)
|
||||
- ✅ FastAPI + Uvicorn
|
||||
- ✅ Local file storage
|
||||
|
||||
### Phase 2: Production MVP
|
||||
- ✅ PostgreSQL (managed or VPS)
|
||||
- ✅ FastAPI + Uvicorn (systemd or Docker)
|
||||
- ⏳ Redis (session cache)
|
||||
- ⏳ Celery (background jobs)
|
||||
- ⏳ S3/MinIO (file storage)
|
||||
|
||||
### Phase 3: Scale
|
||||
- Horizontal app scaling (multiple Uvicorn instances)
|
||||
- PostgreSQL read replicas
|
||||
- Redis cluster
|
||||
- CDN for static assets
|
||||
- Dedicated Celery workers per queue
|
||||
|
||||
### Phase 4: High Availability
|
||||
- Multi-region deployment
|
||||
- Database failover
|
||||
- Container orchestration (Kubernetes)
|
||||
- Full monitoring stack
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Add Redis to docker-compose.yml** - For session cache
|
||||
2. **Implement Celery** - Start with email and import tasks
|
||||
3. **Configure S3/MinIO** - For production file storage
|
||||
4. **Set up Sentry** - Error tracking
|
||||
5. **Choose production deployment** - VPS or Docker based on team preference
|
||||
|
||||
See also:
|
||||
- [Production Deployment Guide](production.md)
|
||||
- [Docker Deployment](docker.md)
|
||||
- [Environment Configuration](environment.md)
|
||||
@@ -0,0 +1,399 @@
|
||||
# Traditional VPS Deployment
|
||||
|
||||
This guide covers deploying Wizamart to a traditional VPS (Ubuntu 22.04+) without containers.
|
||||
|
||||
**Best for:** Teams who want direct server access and familiar Linux administration.
|
||||
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Ubuntu 22.04 LTS or newer
|
||||
- 4GB+ RAM recommended
|
||||
- Root or sudo access
|
||||
- Domain name with DNS configured
|
||||
|
||||
---
|
||||
|
||||
## Quick Start
|
||||
|
||||
```bash
|
||||
# 1. Install system packages
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
sudo apt install -y nginx postgresql-15 redis-server python3.11 python3.11-venv git curl
|
||||
|
||||
# 2. Create application user
|
||||
sudo useradd -m -s /bin/bash wizamart
|
||||
|
||||
# 3. Setup PostgreSQL
|
||||
sudo -u postgres createuser wizamart_user
|
||||
sudo -u postgres createdb wizamart_db -O wizamart_user
|
||||
sudo -u postgres psql -c "ALTER USER wizamart_user WITH PASSWORD 'your-secure-password';"
|
||||
|
||||
# 4. Clone and setup application
|
||||
sudo su - wizamart
|
||||
git clone <repository-url> ~/app
|
||||
cd ~/app
|
||||
python3.11 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
|
||||
# 5. Configure environment
|
||||
cp .env.example .env
|
||||
nano .env # Edit with production values
|
||||
|
||||
# 6. Initialize database
|
||||
alembic upgrade head
|
||||
python scripts/init_production.py
|
||||
|
||||
# 7. Exit wizamart user
|
||||
exit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Systemd Services
|
||||
|
||||
### Main Application
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/wizamart.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Wizamart API Server
|
||||
After=network.target postgresql.service redis.service
|
||||
|
||||
[Service]
|
||||
User=wizamart
|
||||
Group=wizamart
|
||||
WorkingDirectory=/home/wizamart/app
|
||||
Environment="PATH=/home/wizamart/app/.venv/bin"
|
||||
EnvironmentFile=/home/wizamart/app/.env
|
||||
ExecStart=/home/wizamart/app/.venv/bin/uvicorn main:app --host 127.0.0.1 --port 8000 --workers 4
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Celery Worker (when implemented)
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/wizamart-celery.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Wizamart Celery Worker
|
||||
After=network.target redis.service postgresql.service
|
||||
|
||||
[Service]
|
||||
User=wizamart
|
||||
Group=wizamart
|
||||
WorkingDirectory=/home/wizamart/app
|
||||
Environment="PATH=/home/wizamart/app/.venv/bin"
|
||||
EnvironmentFile=/home/wizamart/app/.env
|
||||
ExecStart=/home/wizamart/app/.venv/bin/celery -A app.celery worker --loglevel=info --concurrency=4
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
StandardOutput=journal
|
||||
StandardError=journal
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Celery Beat (Scheduler)
|
||||
|
||||
```bash
|
||||
sudo nano /etc/systemd/system/wizamart-celery-beat.service
|
||||
```
|
||||
|
||||
```ini
|
||||
[Unit]
|
||||
Description=Wizamart Celery Beat Scheduler
|
||||
After=network.target redis.service
|
||||
|
||||
[Service]
|
||||
User=wizamart
|
||||
Group=wizamart
|
||||
WorkingDirectory=/home/wizamart/app
|
||||
Environment="PATH=/home/wizamart/app/.venv/bin"
|
||||
EnvironmentFile=/home/wizamart/app/.env
|
||||
ExecStart=/home/wizamart/app/.venv/bin/celery -A app.celery beat --loglevel=info
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
```
|
||||
|
||||
### Enable Services
|
||||
|
||||
```bash
|
||||
sudo systemctl daemon-reload
|
||||
sudo systemctl enable wizamart wizamart-celery wizamart-celery-beat
|
||||
sudo systemctl start wizamart wizamart-celery wizamart-celery-beat
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Nginx Configuration
|
||||
|
||||
```bash
|
||||
sudo nano /etc/nginx/sites-available/wizamart
|
||||
```
|
||||
|
||||
```nginx
|
||||
# Redirect HTTP to HTTPS
|
||||
server {
|
||||
listen 80;
|
||||
server_name yourdomain.com www.yourdomain.com;
|
||||
return 301 https://$server_name$request_uri;
|
||||
}
|
||||
|
||||
# Main HTTPS server
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name yourdomain.com www.yourdomain.com;
|
||||
|
||||
# SSL (managed by Certbot)
|
||||
ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;
|
||||
include /etc/letsencrypt/options-ssl-nginx.conf;
|
||||
ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header X-XSS-Protection "1; mode=block" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
|
||||
# Logging
|
||||
access_log /var/log/nginx/wizamart.access.log;
|
||||
error_log /var/log/nginx/wizamart.error.log;
|
||||
|
||||
# Static files (served directly)
|
||||
location /static {
|
||||
alias /home/wizamart/app/static;
|
||||
expires 30d;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Uploaded files
|
||||
location /uploads {
|
||||
alias /home/wizamart/app/uploads;
|
||||
expires 7d;
|
||||
add_header Cache-Control "public";
|
||||
}
|
||||
|
||||
# Application
|
||||
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;
|
||||
|
||||
# WebSocket support
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
|
||||
# Timeouts
|
||||
proxy_connect_timeout 60s;
|
||||
proxy_send_timeout 60s;
|
||||
proxy_read_timeout 60s;
|
||||
}
|
||||
|
||||
# Block sensitive files
|
||||
location ~ /\. {
|
||||
deny all;
|
||||
}
|
||||
location ~ \.env$ {
|
||||
deny all;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Enable Site
|
||||
|
||||
```bash
|
||||
sudo ln -s /etc/nginx/sites-available/wizamart /etc/nginx/sites-enabled/
|
||||
sudo nginx -t
|
||||
sudo systemctl reload nginx
|
||||
```
|
||||
|
||||
### SSL with Certbot
|
||||
|
||||
```bash
|
||||
sudo apt install certbot python3-certbot-nginx
|
||||
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Firewall
|
||||
|
||||
```bash
|
||||
sudo ufw allow OpenSSH
|
||||
sudo ufw allow 'Nginx Full'
|
||||
sudo ufw enable
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daily Operations
|
||||
|
||||
### View Logs
|
||||
|
||||
```bash
|
||||
# Application logs
|
||||
sudo journalctl -u wizamart -f
|
||||
|
||||
# Celery logs
|
||||
sudo journalctl -u wizamart-celery -f
|
||||
|
||||
# Nginx logs
|
||||
sudo tail -f /var/log/nginx/wizamart.access.log
|
||||
sudo tail -f /var/log/nginx/wizamart.error.log
|
||||
|
||||
# PostgreSQL logs
|
||||
sudo tail -f /var/log/postgresql/postgresql-15-main.log
|
||||
```
|
||||
|
||||
### Restart Services
|
||||
|
||||
```bash
|
||||
sudo systemctl restart wizamart
|
||||
sudo systemctl restart wizamart-celery
|
||||
sudo systemctl restart nginx
|
||||
```
|
||||
|
||||
### Database Access
|
||||
|
||||
```bash
|
||||
# Connect as wizamart user
|
||||
sudo -u postgres psql wizamart_db
|
||||
|
||||
# Or with password
|
||||
psql -h localhost -U wizamart_user -d wizamart_db
|
||||
```
|
||||
|
||||
### Deploy Updates
|
||||
|
||||
```bash
|
||||
sudo su - wizamart
|
||||
cd ~/app
|
||||
git pull origin main
|
||||
source .venv/bin/activate
|
||||
pip install -r requirements.txt
|
||||
alembic upgrade head
|
||||
exit
|
||||
sudo systemctl restart wizamart wizamart-celery
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Backups
|
||||
|
||||
### Database Backup Script
|
||||
|
||||
```bash
|
||||
sudo nano /home/wizamart/backup.sh
|
||||
```
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
BACKUP_DIR=/home/wizamart/backups
|
||||
DATE=$(date +%Y%m%d_%H%M%S)
|
||||
|
||||
# Create backup directory
|
||||
mkdir -p $BACKUP_DIR
|
||||
|
||||
# Backup database
|
||||
pg_dump -U wizamart_user wizamart_db | gzip > $BACKUP_DIR/db_$DATE.sql.gz
|
||||
|
||||
# Backup uploads
|
||||
tar -czf $BACKUP_DIR/uploads_$DATE.tar.gz -C /home/wizamart/app uploads/
|
||||
|
||||
# Keep last 7 days
|
||||
find $BACKUP_DIR -name "*.gz" -mtime +7 -delete
|
||||
|
||||
echo "Backup completed: $DATE"
|
||||
```
|
||||
|
||||
```bash
|
||||
chmod +x /home/wizamart/backup.sh
|
||||
```
|
||||
|
||||
### Cron Job
|
||||
|
||||
```bash
|
||||
sudo -u wizamart crontab -e
|
||||
```
|
||||
|
||||
```cron
|
||||
# Daily backup at 2 AM
|
||||
0 2 * * * /home/wizamart/backup.sh >> /home/wizamart/backup.log 2>&1
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Monitoring
|
||||
|
||||
### Basic Health Check
|
||||
|
||||
```bash
|
||||
curl -s http://localhost:8000/health | jq
|
||||
```
|
||||
|
||||
### Process Monitoring
|
||||
|
||||
```bash
|
||||
# Check all services
|
||||
systemctl status wizamart wizamart-celery postgresql redis nginx
|
||||
|
||||
# Resource usage
|
||||
htop
|
||||
df -h
|
||||
free -h
|
||||
```
|
||||
|
||||
### Set Up Sentry (Error Tracking)
|
||||
|
||||
Add to `.env`:
|
||||
```env
|
||||
SENTRY_DSN=https://your-sentry-dsn
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
See [Infrastructure Guide - Troubleshooting](infrastructure.md#troubleshooting-guide) for detailed diagnostics.
|
||||
|
||||
### Quick Checks
|
||||
|
||||
```bash
|
||||
# Is the app running?
|
||||
systemctl status wizamart
|
||||
|
||||
# Can we connect to the database?
|
||||
pg_isready -h localhost -U wizamart_user
|
||||
|
||||
# Is Redis running?
|
||||
redis-cli ping
|
||||
|
||||
# Check open ports
|
||||
ss -tlnp | grep -E '(8000|5432|6379|80|443)'
|
||||
|
||||
# View recent errors
|
||||
journalctl -u wizamart --since "1 hour ago" | grep -i error
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user