Replace all ~1,086 occurrences of Wizamart/wizamart/WIZAMART/WizaMart with Orion/orion/ORION across 184 files. This includes database identifiers, email addresses, domain references, R2 bucket names, DNS prefixes, encryption salt, Celery app name, config defaults, Docker configs, CI configs, documentation, seed data, and templates. Renames homepage-wizamart.html template to homepage-orion.html. Fixes duplicate file_pattern key in api.yaml architecture rule. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
Docker Deployment
This guide covers deploying Orion 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
# 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
# docker-compose.prod.yml
services:
api:
build:
context: .
dockerfile: Dockerfile
restart: always
ports:
- "127.0.0.1:8000:8000"
environment:
DATABASE_URL: postgresql://orion_user:${DB_PASSWORD}@db:5432/orion_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://orion_user:${DB_PASSWORD}@db:5432/orion_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: orion_db
POSTGRES_USER: orion_user
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U orion_user -d orion_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
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/store/css/tailwind.css -o ./static/store/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/shop/css/tailwind.css -o ./static/shop/css/tailwind.output.css --minify \
&& tailwindcss -i ./static/public/css/tailwind.css -o ./static/public/css/tailwind.output.css --minify
# Create non-root user
RUN useradd -m -u 1000 orion && chown -R orion:orion /app
USER orion
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
3. Nginx Configuration
mkdir -p nginx/conf.d
# nginx/conf.d/orion.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
# 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/seed/init_production.py
Daily Operations
View Logs
# 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
# API container
docker compose -f docker-compose.prod.yml exec api bash
# Database
docker compose -f docker-compose.prod.yml exec db psql -U orion_user -d orion_db
# Redis
docker compose -f docker-compose.prod.yml exec redis redis-cli
Restart Services
# Single service
docker compose -f docker-compose.prod.yml restart api
# All services
docker compose -f docker-compose.prod.yml restart
Deploy Updates
# 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
# View image history
docker images orion-api
# Tag current as backup
docker tag orion-api:latest orion-api:backup
# Rollback to previous
docker compose -f docker-compose.prod.yml down api
docker tag orion-api:previous orion-api:latest
docker compose -f docker-compose.prod.yml up -d api
Backups
Database Backup
# Create backup
docker compose -f docker-compose.prod.yml exec db pg_dump -U orion_user orion_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 orion_user -d orion_db
Volume Backup
# Backup all volumes
docker run --rm \
-v orion_postgres_data:/data \
-v $(pwd)/backups:/backup \
alpine tar czf /backup/postgres_$(date +%Y%m%d).tar.gz /data
Monitoring
Resource Usage
docker stats
Health Checks
# 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
# 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
# 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
# Check disk usage
docker system df
# Clean up
docker system prune -a --volumes
Memory Issues
# 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 orion user.
Secret Management
# Use Docker secrets (Swarm mode)
echo "your-password" | docker secret create db_password -
# Or use environment files
# Never commit .env to git
Network Isolation
# 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
# 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).