> ## Documentation Index
> Fetch the complete documentation index at: https://help.freakhosting.com/llms.txt
> Use this file to discover all available pages before exploring further.

# How to Deploy a Flask or Django App with Gunicorn and Nginx

> Deploy a Flask or Django app on a VPS: install it in a virtualenv, run Gunicorn as a systemd service on a Unix socket and put Nginx in front with proxy_pass.

To deploy a Flask or Django app with Gunicorn and Nginx, install the app and `gunicorn` in a Python virtual environment, run `gunicorn --bind unix:/run/myapp/gunicorn.sock app:app` (or `mysite.wsgi` for Django) as a systemd service, then add an Nginx server block with `proxy_pass http://unix:/run/myapp/gunicorn.sock;`. Nginx handles the domain, HTTPS and static files; Gunicorn runs your Python code.

| Difficulty | Time | Works on | Needs |
| - | - | - | - |
| Intermediate | 30 minutes | Ubuntu 22.04 / 24.04, Debian 12 / 13 (AlmaLinux / Rocky: see notes) | Nginx, a non-root user with sudo |

## Before you start

* [Install Nginx](https://help.freakhosting.com/portals/other/how-to-install-nginx.md) and point your domain at the server.
* Run the app as a normal user, not `root`. This guide uses the user `alex` and the folder `/home/alex/myapp`.
* Flask's and Django's built-in servers (`flask run`, `runserver`) are for development only. Gunicorn is a production WSGI server: it runs several worker processes that handle requests in parallel.

## How to install the app and Gunicorn

**1. Install Python tools**

```bash theme={null}
sudo apt update
sudo apt install python3-venv python3-pip
```

On AlmaLinux / Rocky, `python3` already includes `venv`.

**2. Create a virtual environment**

```bash theme={null}
cd /home/alex/myapp
python3 -m venv venv
./venv/bin/pip install gunicorn flask
```

For Django, install `django` instead of `flask`, plus your `requirements.txt` with `./venv/bin/pip install -r requirements.txt`.

**3. Test Gunicorn by hand**

For a Flask app in `app.py` with `app = Flask(__name__)`:

```bash theme={null}
./venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 app:app
```

For a Django project called `mysite`:

```bash theme={null}
./venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 mysite.wsgi
```

In a second SSH window, `curl http://127.0.0.1:8000` should return your page. Press `Ctrl+C` to stop.

The format is `module:variable`: `app:app` means "the `app` object in `app.py`". Gunicorn's docs suggest `(2 × CPU cores) + 1` workers as a starting point.

## How to run Gunicorn as a systemd service

Create `/etc/systemd/system/myapp.service`:

```ini theme={null}
[Unit]
Description=Gunicorn for myapp
After=network.target

[Service]
User=alex
Group=www-data
WorkingDirectory=/home/alex/myapp
RuntimeDirectory=myapp
ExecStart=/home/alex/myapp/venv/bin/gunicorn --workers 3 --bind unix:/run/myapp/gunicorn.sock --umask 007 app:app
Restart=on-failure

[Install]
WantedBy=multi-user.target
```

* `RuntimeDirectory=myapp` creates `/run/myapp` at each start.
* `Group=www-data` and `--umask 007` make the socket readable by Nginx, which runs as `www-data` on Ubuntu and Debian.
* For Django, replace `app:app` with `mysite.wsgi`.

Start it:

```bash theme={null}
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
systemctl status myapp
```

## How to configure Nginx

Create `/etc/nginx/sites-available/myapp`:

```nginx theme={null}
server {
    listen 80;
    listen [::]:80;
    server_name example.com www.example.com;

    location /static/ {
        alias /var/www/myapp/static/;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/run/myapp/gunicorn.sock;
    }
}
```

`proxy_params` (Ubuntu / Debian) sets the `Host`, `X-Real-IP`, `X-Forwarded-For` and `X-Forwarded-Proto` headers. Enable and reload:

```bash theme={null}
sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
```

Then add HTTPS with `sudo certbot --nginx -d example.com -d www.example.com` ([Certbot guide](https://help.freakhosting.com/portals/other/how-to-get-a-free-ssl-certificate-with-certbot.md)).

## Django settings for production

In `settings.py`:

```python theme={null}
DEBUG = False
ALLOWED_HOSTS = ["example.com", "www.example.com"]
STATIC_ROOT = "/var/www/myapp/static/"
CSRF_TRUSTED_ORIGINS = ["https://example.com", "https://www.example.com"]
```

Collect static files into the folder Nginx serves:

```bash theme={null}
sudo mkdir -p /var/www/myapp/static
sudo chown alex:alex /var/www/myapp/static
./venv/bin/python manage.py collectstatic
```

Static files live under `/var/www` because Ubuntu home folders are not readable by other users, so Nginx could not serve them from `/home/alex`.

> [!NOTE]
>
> **AlmaLinux / Rocky:** Nginx runs as `nginx`, there is no `proxy_params` file, and SELinux limits what Nginx may connect to. The simplest setup is to bind Gunicorn to `127.0.0.1:8000`, use `proxy_pass http://127.0.0.1:8000;` with the `proxy_set_header` lines from the [reverse proxy guide](https://help.freakhosting.com/portals/other/how-to-set-up-nginx-as-a-reverse-proxy.md), and run `sudo setsebool -P httpd_can_network_connect 1`.

## How to check it worked

```bash theme={null}
curl -H "Host: example.com" http://127.0.0.1/
```

You should see your app's response. If Nginx returns `502 Bad Gateway`, read the reason in `sudo tail /var/log/nginx/error.log` and the app's own log with `sudo journalctl -u myapp -n 50`. See [fix 502 Bad Gateway](https://help.freakhosting.com/portals/other/how-to-fix-502-bad-gateway-nginx.md).

## How to deploy an update

```bash theme={null}
cd /home/alex/myapp
git pull
./venv/bin/pip install -r requirements.txt
sudo systemctl restart myapp
```

For Django, also run `./venv/bin/python manage.py migrate` and `collectstatic` before the restart.

## Frequently asked questions

### Do I need Nginx in front of Gunicorn?

Yes, for public sites. Gunicorn's documentation strongly recommends a proxy such as Nginx, which buffers slow clients, serves static files and handles HTTPS.

### Django shows "Bad Request (400)". Why?

The domain is missing from `ALLOWED_HOSTS`. Add it and restart the service.

### How do I see Gunicorn errors?

`sudo journalctl -u myapp -f` follows the service log. Gunicorn writes startup errors, such as a wrong `module:variable`, there.

### Can I use uWSGI or Uvicorn instead?

Yes. For async frameworks such as FastAPI, run Uvicorn workers. The systemd and Nginx parts stay the same.

## Related guides

> [card] [VPS and dedicated server guides](https://help.freakhosting.com/portals/other/vps-and-dedicated-server-guides.md)
>
> Every server guide in one place.

> [card] [Nginx reverse proxy](https://help.freakhosting.com/portals/other/how-to-set-up-nginx-as-a-reverse-proxy.md)
>
> Proxy headers explained.

> [card] [Create a systemd service](https://help.freakhosting.com/portals/other/how-to-create-a-systemd-service.md)
>
> Units and restart rules.

> [card] [Install PostgreSQL](https://help.freakhosting.com/portals/other/how-to-install-postgresql.md)
>
> A database for Django.

> [card] [Fix 502 Bad Gateway](https://help.freakhosting.com/portals/other/how-to-fix-502-bad-gateway-nginx.md)
>
> When Nginx can't reach Gunicorn.

***

> [!NOTE]
>
> **Checked against the official [Gunicorn deployment documentation](https://gunicorn.org/deploy/) and [Django deployment checklist](https://docs.djangoproject.com/en/stable/howto/deployment/checklist/)** on 4 November 2026; Gunicorn 26.2.0 with Flask 3.1 and Django 6.1 tested on Ubuntu 24.04 LTS behind Nginx 1.24.0 on a Unix socket, and the systemd unit checked with `systemd-analyze verify`. Commands can differ on other distributions or releases.

> [!NOTE]
>
> ### Need Extra Help?
>
> If you encounter any issues, our support team is ready to assist:
>
> * **Live Chat:** Quick assistance via our website.
> * **Support Ticket:** [Open a Ticket](https://freakhosting.com/clientarea/submitticket.php)
> * **Discord:** [Join our Community](https://discord.gg/freakhosting)
> * **Email:** [support@freakhosting.com](mailto:support@freakhosting.com)

> [!TIP]
>
> ### Save on Your Hosting
>
> Ready to upgrade your hosting? Use code **KB20** at checkout for **20% off** your first order!

***

**Last Updated:** November 2026


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.