Python with Gunicorn (Flask / Django)

Gunicorn is the standard WSGI server for Python applications (Flask, Django, FastAPI). Nginx acts as a reverse proxy in front of Gunicorn, handling HTTPS and static files.

02

Install Python and dependencies

bash
# Python 3 is already included in Ubuntu/Debian
python3 --version

# Install pip and venv
apt install python3-pip python3-venv -y
03

Recommended project structure

/var/www/my-app/ ├── app.py (or wsgi.py) ├── requirements.txt ├── venv/ └── ...
04

Application setup

bash
# Create the directory
mkdir -p /var/www/my-app
cd /var/www/my-app

# Copy your project (or clone from git)
git clone https://github.com/user/project.git .

# Create a virtual environment
python3 -m venv venv

# Activate the virtual environment
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Install Gunicorn in the virtual environment
pip install gunicorn
05

Test Gunicorn

Flask

bash
# If your file is called app.py and the Flask instance is 'app'
gunicorn --bind 0.0.0.0:8000 app:app

Django

bash
# Replace 'myproject' with the name of your Django project
gunicorn --bind 0.0.0.0:8000 myproject.wsgi:application

If the site responds on http://IP:8000, Gunicorn works. Now configure it as a service.

06

systemd service for Gunicorn

bash
nano /etc/systemd/system/my-app.service

Flask

ini
[Unit]
Description=Gunicorn for my Flask app
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/my-app
Environment="PATH=/var/www/my-app/venv/bin"
ExecStart=/var/www/my-app/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/my-app.sock \
    --access-logfile /var/log/gunicorn/my-app-access.log \
    --error-logfile /var/log/gunicorn/my-app-error.log \
    app:app
Restart=always

[Install]
WantedBy=multi-user.target

Django

ini
[Unit]
Description=Gunicorn for my Django app
After=network.target

[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/my-app
Environment="PATH=/var/www/my-app/venv/bin"
ExecStart=/var/www/my-app/venv/bin/gunicorn \
    --workers 3 \
    --bind unix:/run/my-app.sock \
    --access-logfile /var/log/gunicorn/my-app-access.log \
    --error-logfile /var/log/gunicorn/my-app-error.log \
    myproject.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target
bash
mkdir -p /var/log/gunicorn
chown -R www-data:www-data /var/www/my-app /var/log/gunicorn

systemctl daemon-reload
systemctl enable --now my-app
systemctl status my-app
07

Nginx configuration as reverse proxy

bash
nano /etc/nginx/sites-available/my-app
nginx
server {
    listen 80;
    server_name example.com www.example.com;

    # Static files (Django: served by Nginx, not Gunicorn)
    location /static/ {
        alias /var/www/my-app/staticfiles/;
    }

    location /media/ {
        alias /var/www/my-app/media/;
    }

    location / {
        proxy_pass http://unix:/run/my-app.sock;
        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_read_timeout 90s;
    }
}
bash
ln -s /etc/nginx/sites-available/my-app /etc/nginx/sites-enabled/
nginx -t && systemctl reload nginx

# Add SSL
certbot --nginx -d example.com -d www.example.com
08

How many Gunicorn workers to use?

The recommended formula is: (2 × number_of_cores) + 1

bash
# See how many cores your server has
nproc

# Example: 2 cores → 5 workers
# Example: 4 cores → 9 workers
ini
ExecStart=... gunicorn --workers 5 ...
09

Environment variables

Never put secrets in code. Use them via the systemd service:

ini
[Service]
Environment="DATABASE_URL=postgresql://user:pass@localhost/db"
Environment="SECRET_KEY=long-secret-key"
Environment="DEBUG=False"

Or via a .env file (requires python-dotenv):

ini
EnvironmentFile=/var/www/my-app/.env
10

Update the application

bash
cd /var/www/my-app
git pull origin main
source venv/bin/activate
pip install -r requirements.txt

# Django: migrate database and collect static files
python manage.py migrate
python manage.py collectstatic --noinput

# Restart Gunicorn
systemctl restart my-app

DeluxHost, fondata nel 2023, offre soluzioni di hosting di alta qualità per diverse esigenze digitali. Forniamo hosting condiviso, VPS e server dedicati con sicurezza avanzata e datacenter globali.

© DeluxHost, Tutti i diritti riservati. | Partita IVA: IT17734661006
Tutti i sistemi operativi