Production Environment Setup
This guide covers deploying the Flask application to a production server using Gunicorn as the WSGI server and Nginx as a reverse proxy.
Prerequisites
Linux server (Ubuntu 22.04 LTS recommended)
Python 3.11 or higher
MySQL 8.0 or higher
Root or sudo access
Domain name (optional but recommended)
SECRET_KEY Generation
The SECRET_KEY is critical for session security and CSRF protection (see config.py:22). Generate a cryptographically secure random key:
Using Python
import secrets
print (secrets.token_hex( 32 ))
Using OpenSSL
Using /dev/urandom
head -c 32 /dev/urandom | base64
Never reuse the same SECRET_KEY across environments or share it publicly. The application will refuse to start in production without a SECRET_KEY set (config.py:25-26).
Database Configuration for Production
Create Production Database
CREATE DATABASE furniture_store_prod CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER ' furniture_app '@ 'localhost' IDENTIFIED BY 'strong_password_here' ;
GRANT SELECT , INSERT , UPDATE , DELETE , CREATE , INDEX , ALTER ON furniture_store_prod. *
TO 'furniture_app' @ 'localhost' ;
FLUSH PRIVILEGES;
Add to config.py for production optimization:
class ProductionConfig ( Config ):
SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size' : 10 , # Number of persistent connections
'pool_recycle' : 3600 , # Recycle connections after 1 hour
'pool_pre_ping' : True , # Verify connections before using
'max_overflow' : 20 , # Additional connections when pool is full
'pool_timeout' : 30 , # Timeout for getting connection from pool
}
Production Database Settings
Create a production environment file /var/www/furniture-store/.env.production:
FLASK_ENV = production
SECRET_KEY = your_generated_secret_key_here
# Database Configuration
DB_USER = furniture_app
DB_PASSWORD = strong_password_here
DB_HOST = localhost
DB_PORT = 3306
DB_NAME = furniture_store_prod
Ensure .env files have restrictive permissions: chmod 600 /var/www/furniture-store/.env.production
WSGI Server Setup
Gunicorn Installation
Gunicorn is not in requirements.txt, so install it separately:
Gunicorn Configuration
Create /var/www/furniture-store/gunicorn.conf.py:
import multiprocessing
# Server socket
bind = '127.0.0.1:8000'
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = 'sync'
worker_connections = 1000
timeout = 30
keepalive = 2
# Logging
accesslog = '/var/log/furniture-store/gunicorn-access.log'
errorlog = '/var/log/furniture-store/gunicorn-error.log'
loglevel = 'info'
# Process naming
proc_name = 'furniture-store'
# Server mechanics
daemon = False
pidfile = '/var/run/furniture-store/gunicorn.pid'
user = 'www-data'
group = 'www-data'
# SSL (if terminating SSL at Gunicorn instead of Nginx)
# keyfile = '/path/to/keyfile'
# certfile = '/path/to/certfile'
Systemd Service File
Create /etc/systemd/system/furniture-store.service:
[Unit]
Description =Furniture Store Backend
After =network.target mysql.service
Requires =mysql.service
[Service]
Type =notify
User =www-data
Group =www-data
WorkingDirectory =/var/www/furniture-store
Environment = "PATH=/var/www/furniture-store/venv/bin"
EnvironmentFile =/var/www/furniture-store/.env.production
ExecStart =/var/www/furniture-store/venv/bin/gunicorn \
--config /var/www/furniture-store/gunicorn.conf.py \
run:app
ExecReload =/bin/kill -s HUP $MAINPID
KillMode =mixed
TimeoutStopSec =5
PrivateTmp =true
Restart =on-failure
RestartSec =10
[Install]
WantedBy =multi-user.target
Start Gunicorn Service
# Create log directory
sudo mkdir -p /var/log/furniture-store
sudo chown www-data:www-data /var/log/furniture-store
# Create PID directory
sudo mkdir -p /var/run/furniture-store
sudo chown www-data:www-data /var/run/furniture-store
# Reload systemd and enable service
sudo systemctl daemon-reload
sudo systemctl enable furniture-store
sudo systemctl start furniture-store
# Check status
sudo systemctl status furniture-store
Alternative: uWSGI Setup
If you prefer uWSGI over Gunicorn, create /var/www/furniture-store/uwsgi.ini:
[uwsgi]
module = run:app
master = true
processes = 5
threads = 2
socket = 127.0.0.1:8000
vacuum = true
die-on-term = true
logto = /var/log/furniture-store/uwsgi.log
Nginx Reverse Proxy Configuration
Install Nginx
sudo apt update
sudo apt install nginx
Nginx Configuration
Create /etc/nginx/sites-available/furniture-store:
upstream furniture_app {
server 127.0.0.1:8000 fail_timeout = 0 ;
}
server {
listen 80 ;
listen [::]:80;
server_name furniture-store.example.com;
# Redirect HTTP to HTTPS
return 301 https://$ server_name $ request_uri ;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name furniture-store.example.com;
# SSL Configuration
ssl_certificate /etc/ssl/certs/furniture-store.crt;
ssl_certificate_key /etc/ssl/private/furniture-store.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on ;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff always;
add_header X-Frame-Options DENY always;
add_header X-XSS-Protection "1; mode=block" always;
# Logging
access_log /var/log/nginx/furniture-store-access.log;
error_log /var/log/nginx/furniture-store-error.log;
# Static files
location /static {
alias /var/www/furniture-store/app/static;
expires 30d ;
add_header Cache-Control "public, immutable" ;
}
# Application
location / {
proxy_pass http://furniture_app;
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_redirect off ;
proxy_buffering off ;
# Timeouts
proxy_connect_timeout 60s ;
proxy_send_timeout 60s ;
proxy_read_timeout 60s ;
}
# Health check endpoint
location /health {
access_log off ;
proxy_pass http://furniture_app;
}
# Deny access to sensitive files
location ~ /\.(?!well-known) {
deny all ;
}
# Client body size limit
client_max_body_size 10M ;
}
Enable Nginx Site
# Create symbolic link
sudo ln -s /etc/nginx/sites-available/furniture-store /etc/nginx/sites-enabled/
# Test configuration
sudo nginx -t
# Reload Nginx
sudo systemctl reload nginx
SSL Certificate with Let’s Encrypt
# Install Certbot
sudo apt install certbot python3-certbot-nginx
# Obtain certificate
sudo certbot --nginx -d furniture-store.example.com
# Certbot will automatically configure Nginx for SSL
Environment Variable Security
Secure Storage Practices
File Permissions
Restrict access to environment files: chmod 600 /var/www/furniture-store/.env.production
chown www-data:www-data /var/www/furniture-store/.env.production
Exclude from Version Control
Ensure .gitignore includes: .env
.env.*
!.env-template
Use Secret Management
For enterprise deployments, consider:
AWS Secrets Manager
HashiCorp Vault
Azure Key Vault
Google Secret Manager
Regular Rotation
Rotate secrets periodically:
SECRET_KEY: Every 90 days
Database passwords: Every 90 days
SSL certificates: Before expiration
Environment Loading
The application uses python-dotenv to load environment variables (config.py:3-5). Ensure the correct .env file is loaded based on the environment.
Monitoring and Logging
Application Logging
Add to app/init .py for production logging:
import logging
from logging.handlers import RotatingFileHandler
import os
def create_app ():
app = Flask( __name__ )
app.config.from_object(Config)
# Production logging configuration
if not app.debug:
if not os.path.exists( 'logs' ):
os.mkdir( 'logs' )
file_handler = RotatingFileHandler(
'logs/furniture-store.log' ,
maxBytes = 10485760 , # 10MB
backupCount = 10
)
file_handler.setFormatter(logging.Formatter(
' %(asctime)s %(levelname)s : %(message)s [in %(pathname)s : %(lineno)d ]'
))
file_handler.setLevel(logging. INFO )
app.logger.addHandler(file_handler)
app.logger.setLevel(logging. INFO )
app.logger.info( 'Furniture Store startup' )
# ... rest of initialization
return app
Log Monitoring
Monitor application logs in real-time:
# Application logs
tail -f /var/log/furniture-store/gunicorn-error.log
# Nginx logs
tail -f /var/log/nginx/furniture-store-error.log
# Systemd journal
journalctl -u furniture-store -f
Error Tracking
Integrate error tracking services:
Sentry : Real-time error tracking and monitoring
Rollbar : Exception tracking and deployment tracking
Datadog : Full-stack observability platform
Health Monitoring
Create a health check endpoint in app/init .py:
@app.route ( '/health' )
def health ():
try :
# Check database connection
db.session.execute( 'SELECT 1' )
return { 'status' : 'healthy' , 'database' : 'connected' }, 200
except Exception as e:
return { 'status' : 'unhealthy' , 'error' : str (e)}, 503
Uptime Monitoring
Configure external monitoring services:
UptimeRobot : Free uptime monitoring
Pingdom : Comprehensive website monitoring
StatusCake : Uptime and performance monitoring
Backup Strategies
Database Backups
Automated Daily Backups
Create /usr/local/bin/backup-furniture-db.sh:
#!/bin/bash
BACKUP_DIR = "/var/backups/furniture-store"
DATE = $( date +"%Y%m%d_%H%M%S" )
DB_NAME = "furniture_store_prod"
DB_USER = "furniture_app"
DB_PASSWORD = "your_password"
mkdir -p $BACKUP_DIR
# Create backup
mysqldump -u $DB_USER -p $DB_PASSWORD $DB_NAME | gzip > $BACKUP_DIR /furniture_db_ $DATE .sql.gz
# Keep only last 30 days of backups
find $BACKUP_DIR -name "furniture_db_*.sql.gz" -mtime +30 -delete
# Upload to S3 (optional)
# aws s3 cp $BACKUP_DIR/furniture_db_$DATE.sql.gz s3://your-bucket/backups/
Make executable and add to cron:
chmod +x /usr/local/bin/backup-furniture-db.sh
# Add to crontab (daily at 2 AM)
crontab -e
0 2 * * * /usr/local/bin/backup-furniture-db.sh
Application Backups
Backup application code and configuration:
#!/bin/bash
BACKUP_DIR = "/var/backups/furniture-store"
DATE = $( date +"%Y%m%d_%H%M%S" )
APP_DIR = "/var/www/furniture-store"
mkdir -p $BACKUP_DIR
# Backup application files (excluding venv)
tar -czf $BACKUP_DIR /app_ $DATE .tar.gz \
--exclude= 'venv' \
--exclude= '*.pyc' \
--exclude= '__pycache__' \
$APP_DIR
# Keep only last 7 days of application backups
find $BACKUP_DIR -name "app_*.tar.gz" -mtime +7 -delete
Disaster Recovery Plan
Regular Testing
Test backup restoration quarterly to ensure backups are valid
Offsite Storage
Store backups in different geographical location (S3, Azure Blob, etc.)
Recovery Documentation
Document step-by-step recovery procedures
RTO/RPO Definition
Define Recovery Time Objective (RTO) and Recovery Point Objective (RPO)
Deployment Checklist
Before going live, verify:
Troubleshooting
Application Won’t Start
# Check systemd status
sudo systemctl status furniture-store
# View recent logs
sudo journalctl -u furniture-store -n 50
# Check Gunicorn error log
tail -f /var/log/furniture-store/gunicorn-error.log
Database Connection Errors
# Test database connection
mysql -u furniture_app -p -h localhost furniture_store_prod
# Check if MySQL is running
sudo systemctl status mysql
# Verify environment variables
sudo -u www-data env | grep DB_
502 Bad Gateway
Check if Gunicorn is running: sudo systemctl status furniture-store
Verify Nginx configuration: sudo nginx -t
Check Nginx error logs: tail -f /var/log/nginx/furniture-store-error.log
Ensure Gunicorn socket/port matches Nginx upstream
High Memory Usage
Reduce Gunicorn worker count
Check for memory leaks in application code
Monitor with htop or ps aux --sort=-%mem
Consider worker timeout and restart policies
Next Steps
Database Configuration Learn about production database setup
Environment Setup Learn more about environment configuration