DEV Community

Cover image for Deploying a Simple Flask API Using Gunicorn, Supervisor & Nginx
Daniel Pepuho
Daniel Pepuho

Posted on

3 1

Deploying a Simple Flask API Using Gunicorn, Supervisor & Nginx

Intro

Hi there! Flask is great for building APIs quickly. But turning your local project into a publicly accessible web service involves a few extra steps that aren’t always obvious.

In this guide, I'll show you how to deploy a Flask API using Gunicorn as the WSGI server, Supervisor to manage the process, and Nginx as a reverse proxy.

Overview

  • Flask: The Python microframework we’ll use to build the API.

  • Gunicorn: A Python WSGI HTTP server for running Flask in production.

  • Supervisor: A process control system to ensure the Gunicorn server stays alive.

  • Nginx: A reverse proxy to handle client requests and route them to Gunicorn.

Flask API Deployment Flow

The diagram below illustrates the flow of a request and response when using Flask, Gunicorn, Supervisor, and Nginx.

When a user sends an HTTP request, it first reaches the Nginx reverse proxy. Nginx forwards the request to Gunicorn, which serves the Flask application via the WSGI protocol. Supervisor ensures that Gunicorn keeps running and automatically restarts it if needed. The response follows the same path back to the user.

Deployment Flow

Requirements

Before starting, make sure you have the following installed on your system:

  • Python 3 and Virtualenv

Check if Python is installed:

$ python3 --version

Python 3.10.14
Enter fullscreen mode Exit fullscreen mode

If not installed, install it:

Ubuntu/Debian:

$ sudo apt update
$ sudo apt install python3 python3-venv -y
Enter fullscreen mode Exit fullscreen mode

CentOS/RHEL:

$ sudo yum install python3 python3-venv -y
Enter fullscreen mode Exit fullscreen mode

Homebrew (macOS):

$ brew install python
Enter fullscreen mode Exit fullscreen mode
  • Nginx

Ubuntu/Debian:

$ sudo apt install nginx -y
Enter fullscreen mode Exit fullscreen mode

CentOS/RHEL:

$ sudo yum install nginx -y
Enter fullscreen mode Exit fullscreen mode

Homebrew (macOS):

$ brew install nginx
Enter fullscreen mode Exit fullscreen mode

After installation, check if Nginx is running:

$ sudo systemctl status nginx
Enter fullscreen mode Exit fullscreen mode

If it’s not running, start and enable it:

$ sudo systemctl start nginx
$ sudo systemctl enable nginx
Enter fullscreen mode Exit fullscreen mode
  • Supervisor

Ubuntu/Debian:

$ sudo apt update
$ sudo apt install supervisor -y
Enter fullscreen mode Exit fullscreen mode

CentOS/RHEL:

$ sudo yum install supervisor -y
Enter fullscreen mode Exit fullscreen mode

Homebrew (macOS):

$ brew install supervisor
Enter fullscreen mode Exit fullscreen mode

After installation, check if Supervisor is running:

$ sudo systemctl status supervisor
Enter fullscreen mode Exit fullscreen mode

If it’s not running, start and enable it:

$ sudo systemctl start supervisor
$ sudo systemctl enable supervisor
Enter fullscreen mode Exit fullscreen mode

Setting Up the Flask Project

First, create a project directory and set up a virtual environment:

$ mkdir flask-api && cd flask-api
Enter fullscreen mode Exit fullscreen mode
$ python3 -m venv venv
$ source venv/bin/activate
Enter fullscreen mode Exit fullscreen mode

With the virtual environment activated, install Flask and Gunicorn:

$ pip install flask gunicorn
Enter fullscreen mode Exit fullscreen mode

You can verify the installation:

$ flask --version
$ gunicorn --version
Enter fullscreen mode Exit fullscreen mode

Next, you need to create a file called app.py inside your project directory. You can use any text editor you prefer, such as nano, vim, or others:

$ vim app.py

from flask import Flask

app = Flask(__name__)

@app.route("/api/hello")
def hello():
    return {"message": "Hello from Flask API!"}
Enter fullscreen mode Exit fullscreen mode

Then, try to run your Flask app using Gunicorn command:

$ gunicorn app:app

[2025-04-30 20:37:49 +0700] [1085004] [INFO] Starting gunicorn 23.0.0
[2025-04-30 20:37:49 +0700] [1085004] [INFO] Listening at: http://127.0.0.1:8000 (1085004)
[2025-04-30 20:37:49 +0700] [1085004] [INFO] Using worker: sync
[2025-04-30 20:37:49 +0700] [1085005] [INFO] Booting worker with pid: 1085005
[2025-04-30 20:38:58 +0700] [1085004] [INFO] Handling signal: winch
Enter fullscreen mode Exit fullscreen mode

To try your app, just open another terminal session or window. You can use a tool like curl:

$ curl http://127.0.0.1:8000/api/hello

{"message":"Hello from Flask API!"}
Enter fullscreen mode Exit fullscreen mode

Running with Multiple Workers

You can run Gunicorn with multiple worker processes using the -w option. For example, to run your app with 3 workers:

$ gunicorn -w 3 app:app

[2025-04-30 20:49:13 +0700] [1085759] [INFO] Starting gunicorn 23.0.0
[2025-04-30 20:49:13 +0700] [1085759] [INFO] Listening at: http://127.0.0.1:8000 (1085759)
[2025-04-30 20:49:13 +0700] [1085759] [INFO] Using worker: sync
[2025-04-30 20:49:13 +0700] [1085760] [INFO] Booting worker with pid: 1085759
[2025-04-30 20:49:13 +0700] [1085761] [INFO] Booting worker with pid: 1085760
[2025-04-30 20:49:13 +0700] [1085762] [INFO] Booting worker with pid: 1085761
Enter fullscreen mode Exit fullscreen mode

To confirm that Gunicorn is running with multiple workers, you can use tools like top or htop.

Install htop (optional but nicer to read):

$ sudo apt install htop -y
Enter fullscreen mode Exit fullscreen mode

Then run:

$ htop
Enter fullscreen mode Exit fullscreen mode

Gunicorn Process

To bind it to a different port (e.g., 8081) and listen on all interfaces:

$ gunicorn -b 0.0.0.0:8081 app:app

[2025-04-30 21:14:29 +0700] [1085847] [INFO] Starting gunicorn 23.0.0
[2025-04-30 21:14:29 +0700] [1085847] [INFO] Listening at: http://0.0.0.0:8081 (1085847)
[2025-04-30 21:14:29 +0700] [1085847] [INFO] Using worker: sync
[2025-04-30 21:14:29 +0700] [1085848] [INFO] Booting worker with pid: 1085848
Enter fullscreen mode Exit fullscreen mode

Adding Supervisor and Nginx Configuration

Supervisor Setup

To make sure Gunicorn runs in the background and restarts automatically if it crashes, you'll want to use Supervisor. Here's how to set it up:

Create a configuration file for your app:

$ sudo vim /etc/supervisor/conf.d/flask-api.conf
Enter fullscreen mode Exit fullscreen mode

Insert this configuration (adjust paths as needed):

[program:flask-api]
directory=/home/youruser/flask-api
command=/home/youruser/flask-api/venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 app:app
autostart=true
autorestart=true
user=www-data
stdout_logfile=/var/log/flask-api.out.log
stderr_logfile=/var/log/flask-api.err.log
environment=PATH="/home/youruser/flask-api/venv/bin"
Enter fullscreen mode Exit fullscreen mode

directory=/home/youruser/flask-api → The working directory where your Flask project is located.

command=/home/youruser/flask-api/venv/bin/gunicorn -w 3 -b 127.0.0.1:8000 app:app → Runs the Gunicorn server with 3 workers, binding to localhost on port 8000.

autostart=true → Automatically starts the app when Supervisor starts (e.g., on boot).

autorestart=true → Restarts the app automatically if it crashes.

user=www-data → Runs the process as the www-data user (you can change this to your own user if needed).

stdout_logfile=/var/log/api/flask-api.out.log → File where standard output (including errors) is logged.

stderr_logfile=/var/log/api/flask-api.err.log → (Optional if using redirect_stderr) File for capturing standard error output.

environment=PATH="..." → Ensures Supervisor uses the correct Python virtual environment for Gunicorn.

Then reload Supervisor to pick up the new config:

$ sudo supervisorctl reread
$ sudo supervisorctl update
$ sudo supervisorctl status
Enter fullscreen mode Exit fullscreen mode

If the API is not running, check the logs:

$ cat /var/log/flask-api.out.log
$ cat /var/log/flask-api.err.log
Enter fullscreen mode Exit fullscreen mode

Or use Supervisor’s built-in log viewer:

$ tail -f flask-api

==> Press Ctrl-C to exit <==
Enter fullscreen mode Exit fullscreen mode

Nginx Configuration

Now that Gunicorn is running your Flask app on port 8000, you’ll want Nginx to act as a reverse proxy.

Create a new Nginx config file:

$ sudo vim /etc/nginx/sites-available/flask-api
Enter fullscreen mode Exit fullscreen mode
server {
    listen 80;
    server_name _;

    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;
    }
}
Enter fullscreen mode Exit fullscreen mode

Enable the site and test:

$ sudo ln -s /etc/nginx/sites-available/flask-api /etc/nginx/sites-enabled
$ sudo nginx -t
$ sudo systemctl restart nginx
Enter fullscreen mode Exit fullscreen mode

Finally, ff everything is set up correctly, you should now be able to access your Flask API at http://YOUR_SERVER_IP/api/hello if you're using a public server or VPS.

Optional: Project Structure & Requirements

To easily reinstall dependencies later:

$ pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

However, it’s better to specify only the packages you need manually:

$ sudo vim requirements.txt
Enter fullscreen mode Exit fullscreen mode
flask
gunicorn
Enter fullscreen mode Exit fullscreen mode

To install all requirements, just run pip install:

$ pip install -r requirements.txt
Enter fullscreen mode Exit fullscreen mode

Your project structure should look like this:

flask-api/
├── app.py
├── venv/
└── requirements.txt
Enter fullscreen mode Exit fullscreen mode

Common Issues

Here are a few quick troubleshooting tips:

502 Bad Gateway: Usually means Gunicorn isn't running or the Nginx config has the wrong port.

Supervisor status shows STOPPED: Check your config file paths and the logs:
sudo tail -f /var/log/flask-api.err.log

Permission errors: Ensure all paths used by Supervisor and Gunicorn are accessible by the appropriate user.

Conclusion

In this guide, we deployed a Flask API using Gunicorn as the WSGI server, Supervisor to keep the app running reliably, and Nginx as a reverse proxy to handle incoming requests. With this setup, your Flask app is ready to serve real traffic efficiently and automatically recover from crashes. Thanks for reading — and good luck with your deployment! 🚀

Project Reference:

ACI image

ACI.dev: The Only MCP Server Your AI Agents Need

ACI.dev’s open-source tool-use platform and Unified MCP Server turns 600+ functions into two simple MCP tools on one server—search and execute. Comes with multi-tenant auth and natural-language permission scopes. 100% open-source under Apache 2.0.

Star our GitHub!

Top comments (0)

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

👋 Kindness is contagious

Delve into a trove of insights in this thoughtful post, celebrated by the welcoming DEV Community. Programmers of every stripe are invited to share their viewpoints and enrich our collective expertise.

A simple “thank you” can brighten someone’s day—drop yours in the comments below!

On DEV, exchanging knowledge lightens our path and forges deeper connections. Found this valuable? A quick note of gratitude to the author can make all the difference.

Get Started