Pre-Deployment Checklist
Before deploying to production, ensure:
Environment Variables
Configure production environment variables in .env:
Set FLASK_ENV=production
Generate secure SECRET_KEY
Configure production database credentials
Database Setup
Create production database
Run migrations: flask db upgrade
Verify database connection
Code Quality
Run Black formatter: black .
Run type checking: mypy app/
Review code against conventions
Security
Never commit .env file
Use strong SECRET_KEY
Enable HTTPS in production
Review CSRF protection
Environment Configuration
Production Environment Variables
Create a production .env file:
FLASK_APP = app.py
FLASK_ENV = production
# Database Configuration
DB_USER = production_user
DB_PASSWORD = strong_secure_password
DB_HOST = production-db-host
DB_PORT = 3306
DB_NAME = muebles_roble_db
# Security
SECRET_KEY = your-very-secure-random-secret-key-here
CRITICAL : The application will raise an error if SECRET_KEY is not set in production.
Generating a Secure SECRET_KEY
Generate a cryptographically secure secret key:
import secrets
print (secrets.token_hex( 32 ))
Or using command line:
python -c "import secrets; print(secrets.token_hex(32))"
Database Setup
Create Production Database
CREATE DATABASE muebles_roble_db
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
CREATE USER ' production_user '@ '%'
IDENTIFIED BY 'strong_secure_password' ;
GRANT ALL PRIVILEGES ON muebles_roble_db. *
TO 'production_user' @ '%' ;
FLUSH PRIVILEGES;
Run Migrations
Apply database schema:
Verify Database Connection
The run.py script includes a connection test:
You should see:
Database connection successful!
Production Server Options
Never use Flask’s development server in production . It’s not designed for security or performance.
Option 1: Gunicorn (Recommended)
Install Gunicorn
Run with Gunicorn
gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()"
Parameters:
-w 4 - Number of worker processes (adjust based on CPU cores)
-b 0.0.0.0:8000 - Bind to all interfaces on port 8000
Gunicorn Configuration File
Create gunicorn.conf.py:
import multiprocessing
# Server socket
bind = "0.0.0.0:8000"
backlog = 2048
# Worker processes
workers = multiprocessing.cpu_count() * 2 + 1
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2
# Logging
accesslog = "-"
errorlog = "-"
loglevel = "info"
# Process naming
proc_name = "muebles_roble"
Run with config:
gunicorn -c gunicorn.conf.py "app:create_app()"
Option 2: uWSGI
Install uWSGI
Run with uWSGI
uwsgi --http :8000 --wsgi-file run.py --callable app --processes 4 --threads 2
uWSGI Configuration File
Create uwsgi.ini:
[uwsgi]
module = app:create_app()
callable = app
master = true
processes = 4
threads = 2
http = :8000
vacuum = true
die-on-term = true
Run with config:
Reverse Proxy with Nginx
Install Nginx
# Ubuntu/Debian
sudo apt install nginx
# CentOS/RHEL
sudo yum install nginx
Nginx Configuration
Create /etc/nginx/sites-available/muebles-roble:
server {
listen 80 ;
server_name your-domain.com www.your-domain.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 ;
}
location /static {
alias /path/to/app/static;
expires 30d ;
}
}
Enable Site
sudo ln -s /etc/nginx/sites-available/muebles-roble /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginx
SSL/HTTPS with Let’s Encrypt
Install Certbot
# Ubuntu/Debian
sudo apt install certbot python3-certbot-nginx
# CentOS/RHEL
sudo yum install certbot python3-certbot-nginx
Obtain SSL Certificate
sudo certbot --nginx -d your-domain.com -d www.your-domain.com
Auto-renewal
Certbot automatically sets up auto-renewal. Test it:
sudo certbot renew --dry-run
Systemd Service
Create Service File
Create /etc/systemd/system/muebles-roble.service:
[Unit]
Description =Muebles Roble Gunicorn Application
After =network.target
[Service]
User =www-data
Group =www-data
WorkingDirectory =/path/to/muebles-roble
Environment = "PATH=/path/to/muebles-roble/venv/bin"
ExecStart =/path/to/muebles-roble/venv/bin/gunicorn \
-c gunicorn.conf.py "app:create_app()"
Restart =always
[Install]
WantedBy =multi-user.target
Enable and Start Service
sudo systemctl daemon-reload
sudo systemctl enable muebles-roble
sudo systemctl start muebles-roble
sudo systemctl status muebles-roble
Service Management
# Start service
sudo systemctl start muebles-roble
# Stop service
sudo systemctl stop muebles-roble
# Restart service
sudo systemctl restart muebles-roble
# View logs
sudo journalctl -u muebles-roble -f
Docker Deployment (Optional)
Dockerfile
FROM python:3.10.11-slim
WORKDIR /app
# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
# Copy application
COPY . .
# Expose port
EXPOSE 8000
# Run with gunicorn
CMD [ "gunicorn" , "-c" , "gunicorn.conf.py" , "app:create_app()" ]
Docker Compose
version : '3.8'
services :
web :
build : .
ports :
- "8000:8000"
environment :
- FLASK_ENV=production
env_file :
- .env
depends_on :
- db
restart : always
db :
image : mysql:8.0
environment :
MYSQL_ROOT_PASSWORD : ${DB_PASSWORD}
MYSQL_DATABASE : ${DB_NAME}
MYSQL_USER : ${DB_USER}
MYSQL_PASSWORD : ${DB_PASSWORD}
volumes :
- mysql_data:/var/lib/mysql
restart : always
volumes :
mysql_data :
Run with Docker Compose
Monitoring and Logging
Application Logs
View application logs:
# Systemd service logs
sudo journalctl -u muebles-roble -f
# Docker logs
docker-compose logs -f web
Error Tracking
Consider integrating error tracking services:
Enable Database Connection Pooling
Configure SQLAlchemy connection pooling: SQLALCHEMY_ENGINE_OPTIONS = {
'pool_size' : 10 ,
'pool_recycle' : 3600 ,
'pool_pre_ping' : True ,
}
Add indexes to frequently queried columns: name = db.Column(db.String( 50 ), nullable = False , unique = True , index = True )
In Nginx: gzip on ;
gzip_types text/plain text/css application/json application/javascript;
Set cache headers in Nginx: location /static {
expires 30d ;
add_header Cache-Control "public, immutable" ;
}
Security Best Practices
Environment Variables
Never commit .env to version control
Use strong SECRET_KEY
Rotate secrets regularly
HTTPS
Always use HTTPS in production
Enable HSTS headers
Use valid SSL certificates
Database Security
Use strong database passwords
Restrict database access by IP
Regular backups
Updates
Keep dependencies updated
Monitor security advisories
Apply security patches promptly
Backup Strategy
Database Backups
Create automated MySQL backups:
#!/bin/bash
# backup.sh
BACKUP_DIR = "/backups/mysql"
DATE = $( date +%Y%m%d_%H%M%S )
FILENAME = "muebles_roble_ $DATE .sql"
mysqldump -u $DB_USER -p $DB_PASSWORD $DB_NAME > " $BACKUP_DIR / $FILENAME "
gzip " $BACKUP_DIR / $FILENAME "
# Keep only last 30 days
find $BACKUP_DIR -type f -mtime +30 -delete
Schedule with cron:
0 2 * * * /path/to/backup.sh
Troubleshooting
Check SECRET_KEY is set in .env
Verify database connection
Review error logs: sudo journalctl -u muebles-roble
Ensure virtual environment is activated
Database connection errors
Verify MySQL is running
Check credentials in .env
Test connection: python run.py
Check firewall rules
Ensure Gunicorn/uWSGI is running
Check Nginx configuration
Verify proxy_pass address
Review Nginx error logs
Check file ownership: chown -R www-data:www-data /path/to/app
Verify directory permissions
Check systemd service user
Next Steps
Environment Variables Complete environment configuration reference
Setup Guide Return to development setup
Project Structure Understand the application architecture
Coding Conventions Follow best practices