Flask is a lightweight Python web framework well-suited for building APIs and web applications on RHEL 8. Gunicorn acts as the production-grade WSGI server that runs your Flask app, while Nginx sits in front as a reverse proxy handling incoming traffic. This guide walks through deploying a Flask application on RHEL 8 using a Python virtual environment, Gunicorn, and Nginx with a systemd service for process management. By the end you will have a production-ready deployment that survives reboots automatically.
Prerequisites
- A RHEL 8 server with a non-root sudo user
- Python 3.8 or higher (available via AppStream)
- Nginx installed (
dnf install -y nginx) - Firewall ports 80 and 443 open (
firewall-cmd --permanent --add-service=http) - Basic familiarity with the command line
Step 1 — Install Python and Create a Virtual Environment
RHEL 8 ships Python 3 via AppStream. Install it along with the virtual environment tooling, then create an isolated environment for your project.
sudo dnf install -y python39 python39-pip python39-devel
mkdir -p ~/myflaskapp && cd ~/myflaskapp
python3.9 -m venv venv
source venv/bin/activate
pip install --upgrade pip
Step 2 — Install Flask and Gunicorn
With the virtual environment active, install Flask and Gunicorn using pip.
pip install flask gunicorn
Create a minimal Flask application in app.py:
cat > ~/myflaskapp/app.py << 'EOF'
from flask import Flask
app = Flask(__name__)
app.config['SECRET_KEY'] = 'change-this-to-a-random-secret'
app.config['DATABASE_URI'] = 'sqlite:///app.db'
@app.route('/')
def index():
return 'Hello from Flask on RHEL 8
'
@app.route('/health')
def health():
return {'status': 'ok'}, 200
if __name__ == '__main__':
app.run(debug=False)
EOF
Step 3 — Run the Application with Gunicorn
Test that Gunicorn can serve the application before configuring it as a service. The -w 4 flag spawns four worker processes — a common starting point for multi-core servers.
cd ~/myflaskapp
source venv/bin/activate
gunicorn -w 4 -b 0.0.0.0:5000 app:app
Visit http://your-server-ip:5000 to verify the response, then press Ctrl+C to stop. Never use Flask’s built-in development server (app.run(debug=True)) in production — it is single-threaded and not hardened for public traffic.
Step 4 — Create a systemd Service for Gunicorn
A systemd unit file ensures Gunicorn restarts automatically on failure or reboot. Replace youruser with your actual username.
sudo tee /etc/systemd/system/gunicorn.service > /dev/null << 'EOF'
[Unit]
Description=Gunicorn daemon for Flask application
After=network.target
[Service]
User=youruser
Group=youruser
WorkingDirectory=/home/youruser/myflaskapp
ExecStart=/home/youruser/myflaskapp/venv/bin/gunicorn
--workers 4
--bind unix:/home/youruser/myflaskapp/gunicorn.sock
app:app
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable --now gunicorn
sudo systemctl status gunicorn
Using a Unix socket (gunicorn.sock) rather than a TCP port is preferred when Nginx runs on the same host — it avoids network overhead and is more secure.
Step 5 — Configure Nginx as a Reverse Proxy
Create an Nginx server block that forwards requests to the Gunicorn socket.
sudo tee /etc/nginx/conf.d/myflaskapp.conf > /dev/null << 'EOF'
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://unix:/home/youruser/myflaskapp/gunicorn.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;
}
}
EOF
sudo nginx -t
sudo systemctl enable --now nginx
Step 6 — Verify the Deployment
Check that both services are running and confirm the application responds through Nginx.
sudo systemctl status gunicorn nginx
curl -I http://localhost/
journalctl -u gunicorn -n 30
If you see a 502 Bad Gateway from Nginx, the socket file permissions are likely the issue. Ensure the Nginx worker process user (nginx) can read the socket directory by adding it to the application user’s group or by adjusting directory permissions.
Conclusion
You now have a Flask application running under Gunicorn on RHEL 8, managed by systemd, and served through Nginx as a reverse proxy. This stack is battle-tested, lightweight, and easy to scale by increasing the Gunicorn worker count or adding upstream servers in Nginx. For HTTPS, obtain a certificate with Certbot (dnf install -y certbot python3-certbot-nginx) and run certbot --nginx -d your-domain.com.
Next steps: Securing Flask with HTTPS and Certbot on RHEL 8, Using PostgreSQL with SQLAlchemy in Flask, and Containerizing a Flask App with Podman on RHEL 8.