Flask

Deploying Flask with Nginx and Let's Encrypt

2026-04-27

Introduction

Once your Flask app is working locally, the next step is making it accessible to the world. In this tutorial we will use a Linux server (Ubuntu), Nginx as a reverse proxy, Gunicorn as the Python application server, and Let's Encrypt for a free HTTPS certificate.


What You Will Need

Before starting, make sure you have the following ready.


- A Linux server (Ubuntu 22.04 recommended) from any provider such as DigitalOcean, Linode, AWS, or host your own on an old computer or Raspberry Pi.

- A domain name pointed at your server's IP address via an A record

- SSH access to your server

- Your Flask project files


Step 1 — Connect to Your Server

From your local machine, connect to your server over SSH.


ssh your_user@your_server_ip


Step 2 — Update the System

Always start by updating your system packages.


sudo apt update && sudo apt upgrade -y


Step 3 — Install Python and Dependencies

Install Python, pip, and venv if they are not already installed.


sudo apt install python3 python3-pip python3-venv -y


Step 4 — Upload Your Flask Project

Copy your project to the server. A simple way is to use scp from your local machine.


scp -r /path/to/your/project your_user@your_server_ip:/home/your_user/myapp


Alternatively use Git to clone your repository directly on the server.


git clone https://github.com/yourusername/yourrepo.git myapp


Step 5 — Set Up a Virtual Environment

Navigate into your project folder and create a virtual environment.


cd /home/your_user/myapp

python3 -m venv venv

source venv/bin/activate

pip install -r requirements.txt


Step 6 — Install Gunicorn

Gunicorn is a production-grade Python server. Flask's built-in server is not suitable for production.


pip install gunicorn


Test that Gunicorn can serve your app by running this command. Replace app with the name of your Python file and app with the name of your Flask instance.


gunicorn --bind 0.0.0.0:8000 app:app


Visit http://your_server_ip:8000 to confirm it works, then stop it with Ctrl+C.


Step 7 — Create a Systemd Service

A systemd service keeps your app running in the background and restarts it automatically if it crashes.


Create a new service file.


sudo nano /etc/systemd/system/myapp.service


Paste the following content into the file. Replace the paths and username with your own.


[Unit]

Description=Gunicorn instance to serve myapp

After=network.target


[Service]

User=your_user

Group=www-data

WorkingDirectory=/home/your_user/myapp

Environment="PATH=/home/your_user/myapp/venv/bin"

ExecStart=/home/your_user/myapp/venv/bin/gunicorn --workers 3 --bind unix:myapp.sock -m 007 app:app


[Install]

WantedBy=multi-user.target


Save the file, then enable and start the service.


sudo systemctl daemon-reload

sudo systemctl start myapp

sudo systemctl enable myapp


Check that it is running correctly.


sudo systemctl status myapp


Step 8 — Install Nginx

Nginx will sit in front of Gunicorn and handle incoming web traffic.


sudo apt install nginx -y


Step 9 — Configure Nginx as a Reverse Proxy

Create a new Nginx configuration file for your site.


sudo nano /etc/nginx/sites-available/myapp


Paste the following configuration. Replace your_domain.com with your actual domain name.


server {

listen 80;

server_name your_domain.com www.your_domain.com;


location / {

include proxy_params;

proxy_pass http://unix:/home/your_user/myapp/myapp.sock;

}


location /static/ {

alias /home/your_user/myapp/static/;

}

}


Enable the configuration by creating a symbolic link.


sudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled


Test the Nginx configuration for syntax errors.


sudo nginx -t


If the test passes, restart Nginx.


sudo systemctl restart nginx


Your site should now be accessible at http://your_domain.com over plain HTTP.


Step 10 — Allow HTTP and HTTPS Through the Firewall

If you have UFW enabled, allow Nginx traffic.


sudo ufw allow 'Nginx Full'

sudo ufw delete allow 'Nginx HTTP'


Step 11 — Install Certbot for Let's Encrypt

Certbot automates obtaining and renewing free SSL certificates from Let's Encrypt.


sudo apt install certbot python3-certbot-nginx -y


Step 12 — Obtain an SSL Certificate

Run Certbot and follow the prompts. Replace the domain with your own.


sudo certbot --nginx -d your_domain.com -d www.your_domain.com


Certbot will automatically update your Nginx configuration to handle HTTPS and redirect HTTP traffic to HTTPS. Your site is now available at https://your_domain.com.


Step 13 — Verify Auto-Renewal

Let's Encrypt certificates expire every 90 days. Certbot installs a timer to renew them automatically. Test the renewal process with a dry run.


sudo certbot renew --dry-run


If no errors appear, automatic renewal is working correctly.


Step 14 — Restarting After Code Changes

Whenever you update your Flask app, reload the Gunicorn service to apply the changes.


sudo systemctl restart myapp


Troubleshooting

Here are some common issues and how to fix them.


If you see a 502 Bad Gateway error, check that your Gunicorn service is running with sudo systemctl status myapp and look at the logs with sudo journalctl -u myapp.


If Nginx fails to start, check for configuration errors with sudo nginx -t and review logs with sudo tail -n 50 /var/log/nginx/error.log.


If Certbot fails, make sure your domain's DNS A record is pointing to your server's IP and that port 80 is open on your firewall.


Summary

You now have a production Flask deployment with the following stack.


- Gunicorn serving your Python app as a Unix socket

- Nginx acting as a reverse proxy and serving static files directly

- Let's Encrypt providing a free auto-renewing HTTPS certificate

- Systemd keeping your app alive and starting it on boot