Complete guide to deploying Verso-Backend in production environments.
- Deployment Options
- VPS Deployment
- Docker Deployment
- Cloud Platforms
- Background Worker
- SSL/TLS Configuration
- Security Hardening
- Monitoring
- Database Migrations
| Option | Best For | Cost | Complexity |
|---|---|---|---|
| VPS (Recommended) | Full control, enterprise | $5-50/mo | Medium |
| Docker | Consistency, DevOps workflows | Variable | Medium |
| Heroku | Quick deployment, small teams | $7-50/mo | Low |
| Railway/Render | Modern PaaS, auto-deploy | $5-25/mo | Low |
| Kubernetes | Large scale, enterprise | Variable | High |
Recommended for production. Full control over infrastructure.
- Ubuntu 22.04 LTS or Debian 12
- SSH access with sudo
- Domain name pointing to server IP
# Update system
sudo apt update && sudo apt upgrade -y
# Install dependencies
sudo apt install -y python3.10 python3.10-venv python3-pip \
postgresql nginx certbot python3-certbot-nginx \
supervisor git
# Create application user
sudo useradd -m -s /bin/bash verso
sudo usermod -aG sudo verso# Start PostgreSQL
sudo systemctl enable postgresql
sudo systemctl start postgresql
# Create database and user
sudo -u postgres psql <<EOF
CREATE USER verso WITH PASSWORD 'secure_password_here';
CREATE DATABASE verso_production OWNER verso;
GRANT ALL PRIVILEGES ON DATABASE verso_production TO verso;
EOF# Switch to app user
sudo su - verso
# Clone repository
git clone https://github.com/versoindustries/verso-backend.git
cd verso-backend
# Create virtual environment
python3.10 -m venv venv
source venv/bin/activate
# Install dependencies
pip install --upgrade pip
pip install -r requirements.txt
pip install gunicorn
# Configure environment
cat > .env <<EOF
FLASK_APP=app
SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
DATABASE_URL=postgresql://verso:secure_password_here@localhost/verso_production
MAIL_SERVER=smtp.your-provider.com
MAIL_PORT=587
MAIL_USE_TLS=True
MAIL_USERNAME=your_email
MAIL_PASSWORD=your_password
MAIL_DEFAULT_SENDER=noreply@yourdomain.com
STRIPE_PUBLISHABLE_KEY=pk_live_...
STRIPE_SECRET_KEY=sk_live_...
STRIPE_WEBHOOK_SECRET=whsec_...
EOF
# Initialize database
flask db upgrade
flask create-roles
flask seed-business-configCreate /etc/systemd/system/verso-web.service:
[Unit]
Description=Verso Backend Web Server
After=network.target
[Service]
User=verso
Group=verso
WorkingDirectory=/home/verso/verso-backend
Environment="PATH=/home/verso/verso-backend/venv/bin"
EnvironmentFile=/home/verso/verso-backend/.env
ExecStart=/home/verso/verso-backend/venv/bin/gunicorn \
--workers 4 \
--bind 127.0.0.1:8000 \
--timeout 120 \
--access-logfile /var/log/verso/access.log \
--error-logfile /var/log/verso/error.log \
"app:create_app()"
Restart=always
[Install]
WantedBy=multi-user.target# Create log directory
sudo mkdir -p /var/log/verso
sudo chown verso:verso /var/log/verso
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable verso-web
sudo systemctl start verso-webCreate /etc/nginx/sites-available/verso:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
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 messaging)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
location /static {
alias /home/verso/verso-backend/app/static;
expires 30d;
add_header Cache-Control "public, no-transform";
}
client_max_body_size 16M;
}# Enable site
sudo ln -s /etc/nginx/sites-available/verso /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginxCreate Dockerfile in project root:
FROM python:3.10-slim
WORKDIR /app
# Install system dependencies
RUN apt-get update && apt-get install -y \
libpq-dev gcc \
&& rm -rf /var/lib/apt/lists/*
# Install Python dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt gunicorn
# Copy application
COPY . .
# Build frontend
RUN apt-get update && apt-get install -y nodejs npm \
&& npm install && npm run build \
&& apt-get remove -y nodejs npm && apt-get autoremove -y
EXPOSE 8000
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "4", "app:create_app()"]Create docker-compose.yml:
version: '3.8'
services:
web:
build: .
ports:
- "8000:8000"
environment:
- FLASK_APP=app
- SECRET_KEY=${SECRET_KEY}
- DATABASE_URL=postgresql://verso:${DB_PASSWORD}@db:5432/verso_production
depends_on:
- db
- redis
restart: unless-stopped
worker:
build: .
command: flask run-worker
environment:
- FLASK_APP=app
- SECRET_KEY=${SECRET_KEY}
- DATABASE_URL=postgresql://verso:${DB_PASSWORD}@db:5432/verso_production
depends_on:
- db
- redis
restart: unless-stopped
db:
image: postgres:15
volumes:
- postgres_data:/var/lib/postgresql/data
environment:
- POSTGRES_USER=verso
- POSTGRES_PASSWORD=${DB_PASSWORD}
- POSTGRES_DB=verso_production
restart: unless-stopped
redis:
image: redis:7-alpine
volumes:
- redis_data:/data
restart: unless-stopped
volumes:
postgres_data:
redis_data:# Set environment variables
export SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
export DB_PASSWORD=secure_database_password
# Build and start
docker-compose up -d
# Run migrations
docker-compose exec web flask db upgrade
docker-compose exec web flask create-roles
docker-compose exec web flask seed-business-config
# View logs
docker-compose logs -f# Install Heroku CLI and login
heroku login
# Create app
heroku create your-app-name
# Add PostgreSQL
heroku addons:create heroku-postgresql:essential-0
# Set environment variables
heroku config:set SECRET_KEY=$(python -c "import secrets; print(secrets.token_hex(32))")
heroku config:set FLASK_APP=app
heroku config:set MAIL_SERVER=smtp.your-provider.com
# ... other variables
# Deploy
git push heroku main
# Run migrations
heroku run flask db upgrade
heroku run flask create-roles
heroku run flask seed-business-config
# Scale worker
heroku ps:scale worker=1- Connect GitHub repository at railway.app
- Add PostgreSQL service
- Set environment variables in dashboard
- Deploy triggers automatically on push
- Run migrations via Railway shell
- Create new App from GitHub
- Set environment variables
- Add managed PostgreSQL database
- Configure health checks (
/health) - Deploy
The worker process handles background tasks (email, reports, cleanup).
Create /etc/supervisor/conf.d/verso-worker.conf:
[program:verso-worker]
command=/home/verso/verso-backend/venv/bin/flask run-worker
directory=/home/verso/verso-backend
user=verso
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
stderr_logfile=/var/log/verso/worker.err.log
stdout_logfile=/var/log/verso/worker.out.log
environment=FLASK_APP="app"sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl statusCreate /etc/systemd/system/verso-worker.service:
[Unit]
Description=Verso Background Worker
After=network.target
[Service]
User=verso
Group=verso
WorkingDirectory=/home/verso/verso-backend
Environment="PATH=/home/verso/verso-backend/venv/bin"
EnvironmentFile=/home/verso/verso-backend/.env
ExecStart=/home/verso/verso-backend/venv/bin/flask run-worker
Restart=always
[Install]
WantedBy=multi-user.target# Install certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Auto-renewal (already configured by certbot)
sudo certbot renew --dry-runAfter obtaining certificate, update Nginx:
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;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# ... rest of configuration
}
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
return 301 https://$server_name$request_uri;
}- Strong
SECRET_KEY(minimum 32 bytes) -
DEBUG=Falsein production - HTTPS enforced
- Database password is strong
- Firewall configured (UFW)
- SSH key-only authentication
- Regular security updates enabled
# Enable UFW
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow ssh
sudo ufw allow 'Nginx Full'
sudo ufw enableVerify headers are set in app/modules/security_headers.py:
# These should be included:
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Strict-Transport-Security: max-age=31536000; includeSubDomains
Content-Security-Policy: default-src 'self'; ...Configure rate limiting in Nginx:
# In http block
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
# In server block
location /api/ {
limit_req zone=api burst=20 nodelay;
proxy_pass http://127.0.0.1:8000;
}The /health endpoint returns application status:
curl https://yourdomain.com/health
# {"status": "healthy", "database": "connected"}Create /etc/logrotate.d/verso:
/var/log/verso/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 verso verso
sharedscripts
postrotate
systemctl reload verso-web
endscript
}
Recommended services:
- Uptime: UptimeRobot, Pingdom
- Errors: Sentry (
SENTRY_DSNin .env) - Metrics: Prometheus + Grafana
# On development, create migration
flask db migrate -m "Description of changes"
# Test migration
flask db upgrade
flask db downgrade
flask db upgrade
# Commit migration file to git
git add migrations/
git commit -m "Add migration: Description of changes"# SSH to production server
flask db upgrade
# If migration fails, rollback
flask db downgradeFor critical changes:
- Deploy new code that works with old AND new schema
- Run migration
- Deploy code that only uses new schema
- Clean up old code
Last Updated: December 2024