A Comprehensive Guide to Deploying Python Django/Flask Applications on VPS with Gunicorn and Supervisor
Introduction to Production Python Deployment
Deploying Python web applications to production requires more than just running a development server. While frameworks like Django and Flask include built-in servers for development, these are unsuitable for production environments due to performance limitations and lack of process management. A professional deployment strategy ensures your application remains stable, secure, and scalable under real-world traffic.
This guide provides a comprehensive approach to deploying Python applications on a Virtual Private Server (VPS) using Gunicorn as the WSGI server and Supervisor for process management. We'll cover everything from initial server setup to ongoing maintenance, with specific considerations for both Django and Flask applications.
Understanding the Deployment Architecture
Before diving into implementation, it's crucial to understand the components of a production Python deployment:
- Virtual Private Server (VPS): Your application's hosting environment, providing dedicated resources and full control over the operating system.
- Gunicorn (Green Unicorn): A Python WSGI HTTP server that serves your application, handling multiple requests concurrently through worker processes.
- Supervisor: A process control system that ensures your Gunicorn processes start automatically, restart on failure, and log appropriately.
- Nginx: A high-performance web server that acts as a reverse proxy, handling static files, SSL termination, and load balancing.
This architecture separates concerns: Nginx handles client connections and static content, Gunicorn executes your Python code, and Supervisor manages the application lifecycle. Each component specializes in its role, resulting in a robust production environment.
Preparing Your VPS Environment
Initial Server Setup
Begin with a fresh Ubuntu or Debian VPS instance. Most cloud providers offer one-click installations. After obtaining SSH access, complete these essential security and configuration steps:
- Update system packages:
sudo apt update && sudo apt upgrade -y - Create a dedicated application user:
sudo adduser --system --group appuser - Configure firewall rules to allow only necessary ports (22 for SSH, 80 for HTTP, 443 for HTTPS)
- Set up SSH key authentication and disable password login for enhanced security
- Configure time synchronization with NTP to ensure accurate timestamps in logs
Installing Required Software
Install the Python ecosystem and supporting tools:
- Python 3.8+ and pip:
sudo apt install python3 python3-pip python3-venv -y - Database server (PostgreSQL recommended for production):
sudo apt install postgresql postgresql-contrib -y - Nginx web server:
sudo apt install nginx -y - Supervisor process manager:
sudo apt install supervisor -y
For Django projects, you may need additional system packages for database drivers and image processing. Flask applications might require different dependencies based on your chosen extensions.
Configuring Your Python Application
Environment and Dependencies
Proper environment isolation is critical for production deployments. Create a virtual environment in a secure directory:
sudo -u appuser python3 -m venv /opt/myapp/venv
sudo -u appuser /opt/myapp/venv/bin/pip install --upgrade pipInstall your application requirements. For Django projects, this typically includes Django itself, database adapters, and any additional packages. Flask applications will include Flask and relevant extensions. Use a requirements.txt file for reproducible installations:
sudo -u appuser /opt/myapp/venv/bin/pip install -r /opt/myapp/requirements.txtProduction Settings Configuration
Both Django and Flask require specific production configurations. For Django, update your settings.py file:
- Set
DEBUG = Falseto disable development features - Configure
ALLOWED_HOSTSwith your domain names - Set up proper database connections with production credentials
- Configure static and media file storage locations
- Implement proper logging configuration
For Flask applications, similar considerations apply. Use environment variables for sensitive configuration, and ensure your application factory properly initializes all extensions for production use.
Setting Up Gunicorn
Gunicorn Configuration
Gunicorn serves as the interface between Nginx and your Python application. Create a configuration file at /opt/myapp/gunicorn_config.py:
bind = "unix:/opt/myapp/gunicorn.sock"
workers = 3
worker_class = "sync"
worker_connections = 1000
timeout = 30
keepalive = 2
user = "appuser"
group = "appuser"
loglevel = "info"
accesslog = "/opt/myapp/logs/gunicorn_access.log"
errorlog = "/opt/myapp/logs/gunicorn_error.log"
capture_output = TrueThe worker count should be determined based on your server's CPU cores. A common formula is workers = (2 * CPU cores) + 1. For memory-intensive applications, you might need fewer workers. The Unix socket approach provides better performance than TCP for local communication with Nginx.
Testing Gunicorn
Before integrating with Supervisor, test Gunicorn directly. For Django applications:
sudo -u appuser /opt/myapp/venv/bin/gunicorn --config /opt/myapp/gunicorn_config.py myproject.wsgi:applicationFor Flask applications using an application factory:
sudo -u appuser /opt/myapp/venv/bin/gunicorn --config /opt/myapp/gunicorn_config.py "myapp:create_app()"Verify the application responds correctly by checking the logs and ensuring no errors appear. This step confirms your application works in the production environment before adding process management.
Implementing Supervisor for Process Management
Supervisor Configuration
Supervisor ensures your application starts automatically on system boot and restarts if it crashes. Create a configuration file at /etc/supervisor/conf.d/myapp.conf:
[program:myapp]
command=/opt/myapp/venv/bin/gunicorn --config /opt/myapp/gunicorn_config.py myproject.wsgi:application
directory=/opt/myapp
user=appuser
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/opt/myapp/logs/supervisor.log
stdout_logfile_maxbytes=50MB
stdout_logfile_backups=10
environment=PYTHONPATH="/opt/myapp",DJANGO_SETTINGS_MODULE="myproject.settings"The environment variables should be adjusted for your specific application. For Flask, you might set FLASK_ENV="production" and other required variables.
Managing Supervisor
After creating the configuration, update Supervisor and start your application:
sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl start myappMonitor your application's status with sudo supervisorctl status myapp. Supervisor provides several useful commands for managing your application:
sudo supervisorctl restart myapp: Restart the applicationsudo supervisorctl stop myapp: Stop the application gracefullysudo supervisorctl tail myapp: View application logs
Supervisor will automatically restart your application if it exits unexpectedly, providing high availability for your service.
Configuring Nginx as a Reverse Proxy
Nginx Server Block Configuration
Nginx handles incoming HTTP/HTTPS requests and forwards them to Gunicorn. Create a configuration file at /etc/nginx/sites-available/myapp:
server {
listen 80;
server_name yourdomain.com www.yourdomain.com;
location = /favicon.ico { access_log off; log_not_found off; }
location /static/ {
root /opt/myapp;
expires 1y;
add_header Cache-Control "public, immutable";
}
location /media/ {
root /opt/myapp;
expires 6M;
add_header Cache-Control "public";
}
location / {
include proxy_params;
proxy_pass http://unix:/opt/myapp/gunicorn.sock;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_buffering off;
}
client_max_body_size 100M;
}This configuration serves static files directly from Nginx (much more efficient than through Python), proxies dynamic requests to Gunicorn, and sets appropriate headers. The client_max_body_size directive should be adjusted based on your application's file upload requirements.
Enabling the Site and Testing
Enable your Nginx configuration and test it:
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl restart nginxThe nginx -t command validates your configuration syntax before applying changes. After restarting Nginx, your application should be accessible via your server's IP address or domain name.
Security Hardening and Best Practices
File Permissions and Ownership
Proper file permissions prevent unauthorized access to your application code and data:
- Application directory:
sudo chown -R appuser:appuser /opt/myapp - Log files: Ensure write permissions for the appuser but restrict read access
- Configuration files: Restrict to root ownership with minimal read permissions
- Database credentials: Store in environment variables or protected configuration files
SSL/TLS Implementation
For production deployments, always use HTTPS. Certbot with Let's Encrypt provides free SSL certificates:
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.comCertbot automatically configures Nginx with SSL and sets up automatic certificate renewal. Ensure your Django or Flask application is configured to recognize secure connections (Django's SECURE_PROXY_SSL_HEADER setting or Flask's proxy fix middleware).
Monitoring and Logging
Implement comprehensive logging to diagnose issues and monitor performance:
- Configure application logging to write to files with rotation
- Set up log monitoring with tools like logwatch or fail2ban for security
- Monitor server resources (CPU, memory, disk) with tools like htop or netdata
- Implement health check endpoints in your application for monitoring services
Maintenance and Troubleshooting
Common Issues and Solutions
Even with proper configuration, you may encounter issues in production:
- Database connection errors: Check database service status and connection strings
- Permission denied errors: Verify file ownership and SELinux/AppArmor policies
- Memory leaks: Monitor worker memory usage and adjust Gunicorn configuration
- Static files not loading: Check Nginx configuration and file permissions
Deployment Workflow
Establish a consistent deployment process to minimize downtime:
- Pull latest code changes to a staging directory
- Run database migrations (if applicable)
- Collect static files
- Reload Gunicorn with
sudo supervisorctl restart myapp - Verify the deployment with health checks
For Django applications, include python manage.py collectstatic --noinput and python manage.py migrate in your deployment script. Flask applications may have similar commands depending on your extensions.
Scaling Considerations
As your application grows, consider these scaling strategies:
- Vertical scaling: Increase VPS resources (CPU, RAM) and adjust Gunicorn worker count
- Horizontal scaling: Deploy multiple application instances behind a load balancer
- Database optimization: Implement connection pooling, query optimization, and read replicas
- Caching strategy: Add Redis or Memcached for session storage and frequent queries
The Gunicorn and Supervisor setup described here provides a solid foundation that can be extended for more complex architectures as needed.
Conclusion
Deploying Python applications with Gunicorn and Supervisor on a VPS provides a robust, production-ready environment that balances performance, reliability, and maintainability. This approach gives you full control over your infrastructure while leveraging battle-tested tools from the Python ecosystem.
By following this comprehensive guide, you've established a deployment that automatically starts your application, restarts on failure, serves static files efficiently, and maintains security best practices. Regular monitoring, timely updates, and proper backup procedures will ensure your application remains stable and secure as it serves your users.
Remember that deployment is an iterative process. As your application evolves, revisit your configuration to optimize performance, enhance security, and incorporate new best practices from the Python community.
