Back to articles
Technology Insight

A Comprehensive Guide to Deploying Python Django/Flask Applications on VPS with Gunicorn and Supervisor

May 17, 2026

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:

  1. Update system packages: sudo apt update && sudo apt upgrade -y
  2. Create a dedicated application user: sudo adduser --system --group appuser
  3. Configure firewall rules to allow only necessary ports (22 for SSH, 80 for HTTP, 443 for HTTPS)
  4. Set up SSH key authentication and disable password login for enhanced security
  5. 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 pip

Install 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.txt

Production Settings Configuration

Both Django and Flask require specific production configurations. For Django, update your settings.py file:

  • Set DEBUG = False to disable development features
  • Configure ALLOWED_HOSTS with 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 = True

The 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:application

For 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 myapp

Monitor your application's status with sudo supervisorctl status myapp. Supervisor provides several useful commands for managing your application:

  • sudo supervisorctl restart myapp: Restart the application
  • sudo supervisorctl stop myapp: Stop the application gracefully
  • sudo 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 nginx

The 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.com

Certbot 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:

  1. Pull latest code changes to a staging directory
  2. Run database migrations (if applicable)
  3. Collect static files
  4. Reload Gunicorn with sudo supervisorctl restart myapp
  5. 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.