Deployment Guide¶
Complete guide for deploying Poolula Platform to production and staging environments.
Overview¶
Poolula Platform deployment supports multiple environments:
-
Local Development - SQLite database, hot reload
-
Staging - Test environment with production-like setup
-
Production - Live system for LLC operations
Deployment Checklist¶
Before deploying:
-
All tests passing (
uv run pytest) -
Database migrations created and tested
-
Environment variables configured
-
Backup created (
python scripts/backup.py) -
Dependencies updated (
uv sync) -
API documentation reviewed
-
Security review complete (secrets, permissions)
Local Development¶
Starting the API Server¶
# Start with hot reload
uv run uvicorn apps.api.main:app --reload --port 8082
# Access at:
# http://localhost:8082
Development Configuration¶
Environment variables (.env):
# Database
DATABASE_URL=sqlite:///./poolula.db
# API
API_HOST=0.0.0.0
API_PORT=8082
API_RELOAD=true
# AI (Phase 2+)
ANTHROPIC_API_KEY=sk-ant-...
# Logging
DEBUG=false
LOG_LEVEL=INFO
Running All Services Locally¶
# Terminal 1: API server
uv run uvicorn apps.api.main:app --reload --port 8082
# Terminal 2: Watch tests (optional)
uv run pytest-watch
# Terminal 3: Frontend (Phase 4+)
cd frontend && npm run dev
Staging Deployment¶
Purpose¶
Staging mirrors production for testing:
-
Production database engine (PostgreSQL)
-
Production-like data volume
-
Same deployment process
-
Integration testing
-
QA validation
Setup Staging Environment¶
1. Create staging database:
# PostgreSQL (recommended for staging)
createdb poolula_staging
# Set connection string
export DATABASE_URL=postgresql://user:pass@localhost/poolula_staging
2. Apply migrations:
3. Seed staging data:
# Use seed scripts
uv run python scripts/seed_database.py --initial
uv run python scripts/seed_obligations.py --year 2025
4. Start staging server:
Staging Workflow¶
Testing on staging:
# Run full test suite
uv run pytest
# Run API integration tests
curl http://staging.poolula.com:8083/health
# Test chatbot queries
curl -X POST http://staging.poolula.com:8083/api/v1/chat/query \
-H "Content-Type: application/json" \
-d '{"query": "What is our property address?"}'
Production Deployment¶
Production Architecture¶
Components:
-
API Server - FastAPI on port 8082 (or 80/443 with reverse proxy)
-
Database - SQLite (current) or PostgreSQL (recommended for scale)
-
Vector Store - ChromaDB for document embeddings
-
Web Server - Nginx reverse proxy (optional)
-
SSL/TLS - Let's Encrypt certificates (if public)
Deployment Options¶
Option 1: Simple VPS (Recommended for Phase 1-3)
Deploy to a single VPS (DigitalOcean, Linode, etc.):
-
1-2 GB RAM
-
20-40 GB SSD
-
Ubuntu 22.04 LTS
Option 2: Docker (Phase 4+)
Containerized deployment with Docker Compose.
Option 3: Cloud Platform (Future)
AWS, Google Cloud, or Azure for scale.
VPS Deployment (Step-by-Step)¶
1. Provision Server¶
# SSH into server
ssh user@poolula-server.com
# Update system
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y python3.13 python3-pip git sqlite3 nginx
2. Setup Application¶
# Clone repository
git clone https://github.com/dagny099/poolula-platform.git
cd poolula-platform
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install dependencies
uv sync
# Install production dependencies (Phase 2+)
uv sync --group rag
3. Configure Environment¶
# Create production .env
cat > .env << EOF
DATABASE_URL=sqlite:///./poolula.db
API_HOST=0.0.0.0
API_PORT=8082
ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
DEBUG=false
LOG_LEVEL=WARNING
EOF
# Secure permissions
chmod 600 .env
4. Initialize Database¶
# Run migrations
.venv/bin/alembic upgrade head
# Seed initial data
uv run python scripts/seed_database.py --initial
uv run python scripts/seed_obligations.py
5. Setup Systemd Service¶
Create service file for automatic restart:
Service configuration:
[Unit]
Description=Poolula Platform API
After=network.target
[Service]
Type=simple
User=poolula
Group=poolula
WorkingDirectory=/home/poolula/poolula-platform
Environment="PATH=/home/poolula/poolula-platform/.venv/bin"
ExecStart=/home/poolula/poolula-platform/.venv/bin/uvicorn apps.api.main:app --host 0.0.0.0 --port 8082
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.target
Enable and start service:
# Reload systemd
sudo systemctl daemon-reload
# Enable service (start on boot)
sudo systemctl enable poolula-api
# Start service
sudo systemctl start poolula-api
# Check status
sudo systemctl status poolula-api
6. Configure Nginx Reverse Proxy¶
Nginx configuration:
# /etc/nginx/sites-available/poolula
server {
listen 80;
server_name poolula.yourdomain.com;
location / {
proxy_pass http://127.0.0.1:8082;
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;
}
}
Enable site:
# Create symlink
sudo ln -s /etc/nginx/sites-available/poolula /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
7. Setup SSL with Let's Encrypt¶
# Install Certbot
sudo apt install -y certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d poolula.yourdomain.com
# Test auto-renewal
sudo certbot renew --dry-run
Nginx will be automatically updated for HTTPS:
server {
listen 443 ssl;
server_name poolula.yourdomain.com;
ssl_certificate /etc/letsencrypt/live/poolula.yourdomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/poolula.yourdomain.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8082;
...
}
}
8. Setup Monitoring¶
Basic health check:
# Create health check script
cat > ~/check_health.sh << 'EOF'
#!/bin/bash
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8082/health)
if [ "$RESPONSE" != "200" ]; then
echo "API health check failed: $RESPONSE"
sudo systemctl restart poolula-api
fi
EOF
chmod +x ~/check_health.sh
Add to crontab:
# Run every 5 minutes
crontab -e
# Add line:
*/5 * * * * /home/poolula/check_health.sh >> /var/log/poolula-health.log 2>&1
Docker Deployment (Phase 4+)¶
Dockerfile¶
FROM python:3.13-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install -r requirements.txt
# Copy application
COPY . .
# Run migrations and start server
CMD ["sh", "-c", "alembic upgrade head && uvicorn apps.api.main:app --host 0.0.0.0 --port 8082"]
Docker Compose¶
docker-compose.yml:
version: '3.8'
services:
api:
build: .
ports:
- "8082:8082"
environment:
- DATABASE_URL=postgresql://poolula:password@db:5432/poolula
- ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
depends_on:
- db
restart: always
db:
image: postgres:15-alpine
environment:
- POSTGRES_USER=poolula
- POSTGRES_PASSWORD=password
- POSTGRES_DB=poolula
volumes:
- postgres_data:/var/lib/postgresql/data
restart: always
nginx:
image: nginx:alpine
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./certs:/etc/nginx/certs:ro
depends_on:
- api
restart: always
volumes:
postgres_data:
Deploy with Docker Compose:
# Build and start
docker-compose up -d
# View logs
docker-compose logs -f
# Stop
docker-compose down
Database Migration in Production¶
Pre-Deployment¶
# 1. Create backup
python scripts/backup.py
# 2. Test migration locally
.venv/bin/alembic upgrade head
# 3. Test rollback locally
.venv/bin/alembic downgrade -1
.venv/bin/alembic upgrade head
During Deployment¶
# 1. Stop API (brief downtime)
sudo systemctl stop poolula-api
# 2. Backup production database
python scripts/backup.py
# 3. Apply migrations
.venv/bin/alembic upgrade head
# 4. Start API
sudo systemctl start poolula-api
# 5. Verify health
curl http://localhost:8082/health
Zero-Downtime Migration¶
For critical production systems:
-
Add new columns (nullable)
-
Deploy code that writes to old + new columns
-
Backfill data
-
Deploy code that only uses new columns
-
Drop old columns
See Migrations Guide for details.
Rollback Strategy¶
API Rollback¶
# Revert to previous git commit
git checkout <previous-commit>
# Restart service
sudo systemctl restart poolula-api
Database Rollback¶
# Rollback one migration
.venv/bin/alembic downgrade -1
# Restore from backup (if needed)
python scripts/backup.py --restore latest
Full Rollback Procedure¶
# 1. Stop API
sudo systemctl stop poolula-api
# 2. Restore database from backup
python scripts/backup.py --restore <backup-name>
# 3. Checkout previous code version
git checkout <previous-tag>
# 4. Restart API
sudo systemctl start poolula-api
# 5. Verify
curl http://localhost:8082/health
Monitoring and Logging¶
Application Logs¶
Systemd journal:
# View logs
sudo journalctl -u poolula-api -f
# Last 100 lines
sudo journalctl -u poolula-api -n 100
# Since yesterday
sudo journalctl -u poolula-api --since yesterday
Application log file:
Error Tracking¶
Log rotation:
Configuration:
/var/log/poolula/*.log {
daily
rotate 14
compress
delaycompress
notifempty
create 0640 poolula poolula
sharedscripts
postrotate
systemctl reload poolula-api > /dev/null 2>&1 || true
endscript
}
Performance Monitoring¶
Basic metrics:
# API response time
curl -w "@curl-format.txt" -o /dev/null -s http://localhost:8082/health
# Where curl-format.txt contains:
time_namelookup: %{time_namelookup}\n
time_connect: %{time_connect}\n
time_starttransfer: %{time_starttransfer}\n
time_total: %{time_total}\n
Database size:
# SQLite
du -h poolula.db
# PostgreSQL
psql -c "SELECT pg_size_pretty(pg_database_size('poolula'));"
Security Considerations¶
Environment Variables¶
Never commit secrets:
Use secret management:
# Production: Use systemd environment files
sudo nano /etc/poolula/secrets.env
# Reference in service file:
EnvironmentFile=/etc/poolula/secrets.env
Database Security¶
SQLite:
PostgreSQL:
-
Use strong passwords
-
Restrict network access (bind to localhost or VPC)
-
Enable SSL connections
-
Regular backups
API Security¶
Rate limiting (Phase 4+):
from fastapi import FastAPI
from slowapi import Limiter
limiter = Limiter(key_func=get_remote_address)
app.state.limiter = limiter
CORS configuration:
from fastapi.middleware.cors import CORSMiddleware
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourdomain.com"], # Restrict origins
allow_credentials=True,
allow_methods=["GET", "POST", "PATCH"],
allow_headers=["*"],
)
Firewall Configuration¶
# UFW (Ubuntu)
sudo ufw allow 22/tcp # SSH
sudo ufw allow 80/tcp # HTTP
sudo ufw allow 443/tcp # HTTPS
sudo ufw deny 8082/tcp # Block direct API access
sudo ufw enable
Backup and Recovery¶
Automated Backups¶
Cron job for daily backups:
# Edit crontab
crontab -e
# Add daily backup at 2 AM
0 2 * * * cd /home/poolula/poolula-platform && python scripts/backup.py >> /var/log/poolula/backup.log 2>&1
Backup script features:
-
Automatic timestamping
-
Compression
-
Retention policy (keep last 30 days)
-
Off-site copy (rsync to backup server)
Disaster Recovery¶
Recovery procedure:
# 1. Provision new server
# 2. Install application
# 3. Restore database from backup
python scripts/backup.py --restore latest
# 4. Apply any pending migrations
.venv/bin/alembic upgrade head
# 5. Start services
sudo systemctl start poolula-api
Test recovery quarterly:
CI/CD Pipeline (Phase 4+)¶
GitHub Actions Example¶
.github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
tags:
- 'v*'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: '3.13'
- run: pip install uv
- run: uv sync
- run: uv run pytest
deploy:
needs: test
runs-on: ubuntu-latest
steps:
- name: Deploy to server
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script: |
cd /home/poolula/poolula-platform
git pull origin main
.venv/bin/alembic upgrade head
sudo systemctl restart poolula-api
Troubleshooting¶
Service Won't Start¶
Check logs:
Common issues:
-
Port already in use:
sudo lsof -i :8082 -
Database connection failed: Check DATABASE_URL
-
Missing dependencies:
uv sync -
Permission denied: Check file ownership
Database Migration Fails¶
Check migration status:
Resolve conflicts:
Out of Disk Space¶
Check disk usage:
Clean up:
# Remove old backups
find backups/ -mtime +30 -delete
# Clean up logs
sudo journalctl --vacuum-time=7d
# Clean pip cache
pip cache purge
Related Documentation¶
- Migrations Guide - Database migrations
- Testing Guide - Pre-deployment testing
- Backup tool:
scripts/backup.py - Architecture Overview - System architecture