Flask is a lightweight, open-source Python web framework for building web applications and APIs, offering routing, request handling, and Jinja2 templating out of the box while staying easy to extend. This guide installs Flask on Ubuntu 24.04 inside a Python virtual environment, builds a basic Flask application, and runs it in production behind Gunicorn and Nginx with HTTPS enabled via Let's Encrypt. By the end, you'll have a Flask application served over HTTPS through Nginx and managed as a systemd service.
1. Set Up the Flask Project Environment
Python and Pip come pre-installed on Ubuntu, and Flask works with any active Python version. Set up an isolated virtual environment before installing Flask.
1. Update the server's package index:
$ sudo apt update
2. Check the installed Python version:
$ python3 --version
If Python isn't installed, install it:
$ sudo apt install python3 python3-pip -y
3. Check the installed Pip version:
$ pip --version
Output:
pip 24.0 from /usr/lib/python3/dist-packages/pip (python 3.12)
4. Install the venv Python virtual environment module:
$ sudo apt install python3-venv -y
5. Create a new directory for the Flask project:
$ mkdir flask_project
6. Switch to the Flask project directory:
$ cd flask_project
7. Create a new flaskenv virtual environment:
$ python3 -m venv flaskenv
This creates an isolated flaskenv environment for managing Python packages.
8. Activate the flaskenv virtual environment:
$ source flaskenv/bin/activate
Your shell prompt should change to reflect the active environment:
(flaskenv) example_user@server:/flask_project$
2. Install Flask on Ubuntu 24.04
1. Install Flask using Pip:
$ pip install flask
Output:
Collecting flask
Downloading flask-3.1.0-py3-none-any.whl.metadata (2.7 kB)
Collecting Werkzeug>=3.1 (from flask)..........
Downloading werkzeug-3.1.3-py3-none-any.whl.metadata (3.7 kB)
Downloading MarkupSafe-3.0.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl.metadata (4.0 kB)........
Installing collected packages: MarkupSafe, itsdangerous, click, blinker, Werkzeug, Jinja2, flask
Successfully installed Jinja2-3.1.4 MarkupSafe-3.0.2 Werkzeug-3.1.3 blinker-1.9.0 click-8.1.7 flask-3.1.0 itsdangerous-2.2.0
To install Flask globally without a virtual environment, use this instead:
$ pip install flask --user
2. Check the installed Flask version:
$ flask --version
Output:
Python 3.12.3
Flask 3.1.0
Werkzeug 3.1.3
3. Create a Basic Flask Application
1. Create a new flask_app.py file using a text editor such as nano:
$ nano flask_app.py
2. Add the following contents to the file:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000)
Save and close the file. This application listens on port 5000 across all server interfaces (0.0.0.0) and returns "Hello World!" when accessed.
3. Allow connections to TCP port 5000 through the firewall:
$ sudo ufw allow 5000/tcp
4. Reload UFW to apply the changes:
$ sudo ufw reload
5. Run the application:
$ python3 flask_app.py
Output:
* Serving Flask app 'flask_app'
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:5000
* Running on http://YOUR_SERVER_IP:5000
Press CTRL+C to quit
6. Access port 5000 using your server's IP address in a web browser:
http://SERVER-IP:5000
4. Set Up Gunicorn as a System Service to Serve Flask Applications
Gunicorn is a Python WSGI server used to run Flask, Django, and FastAPI applications in production.
1. Install Gunicorn using Pip:
$ pip install gunicorn
Output:
Collecting gunicorn
Downloading gunicorn-23.0.0-py3-none-any.whl.metadata (4.4 kB)
Collecting packaging (from gunicorn)
Downloading packaging-24.2-py3-none-any.whl.metadata (3.2 kB)
Downloading gunicorn-23.0.0-py3-none-any.whl (85 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 85.0/85.0 kB 7.7 MB/s eta 0:00:00
Downloading packaging-24.2-py3-none-any.whl (65 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 65.5/65.5 kB 27.9 MB/s eta 0:00:00
Installing collected packages: packaging, gunicorn
Successfully installed gunicorn-23.0.0 packaging-24.2
2. Print your working directory and note the Flask project path:
$ pwd
Output should look similar to:
/home/linuxuser/flask_project
3. Create a new gunicorn_conf.py file:
$ sudo nano gunicorn_conf.py
4. Add the following configuration, replacing /home/linuxuser/flask_project with your actual project path:
from multiprocessing import cpu_count
# Socket Path
bind = 'unix:/home/linuxuser/flask_project/gunicorn.sock'
# Worker Options
workers = cpu_count() + 1
worker_class = 'uvicorn.workers.UvicornWorker'
# Logging Options
loglevel = 'debug'
accesslog = '/home/linuxuser/flask_project/access_log'
errorlog = '/home/linuxuser/flask_project/error_log'
Save and close the file. This sets up a UNIX socket, dynamically allocates workers based on CPU cores, and logs debug-level messages.
5. Create a new flask_app.service systemd service file:
$ sudo nano /etc/systemd/system/flask_app.service
6. Add the following, replacing linuxuser with your actual user and /home/linuxuser/flask_project with your project path:
[Unit]
Description=Gunicorn instance to serve Flask application
After=network.target
[Service]
User=linuxuser
Group=www-data
WorkingDirectory=/home/linuxuser/flask_project
Environment="PATH=/home/linuxuser/flask_project/flaskenv/bin"
ExecStart=/home/linuxuser/flask_project/flaskenv/bin/gunicorn --workers 3 --bind unix:/home/linuxuser/flask_project/flask_app.sock flask_app:app
[Install]
WantedBy=multi-user.target
Save and close the file.
7. Reload the systemd daemon:
$ sudo systemctl daemon-reload
8. Enable the flask_app service to start at boot:
$ sudo systemctl enable flask_app
9. Start the flask_app service:
$ sudo systemctl start flask_app
10. Check the service status:
$ sudo systemctl status flask_app
Output:
● flask_app.service - Gunicorn instance to serve Flask application
Loaded: loaded (/etc/systemd/system/flask_app.service; enabled; preset: enabled)
Active: active (running) since Wed 2024-12-04 09:35:59 UTC; 17s ago
Main PID: 15711 (gunicorn)................
11. Alternatively, run the Flask application directly with Gunicorn:
$ gunicorn --bind 0.0.0.0:5000 flask_app:app
5. Configure Nginx as a Reverse Proxy to Secure the Flask Application
1. Deactivate the flaskenv virtual environment:
$ deactivate
2. Install Nginx:
$ sudo apt install nginx -y
3. Create a new flask_app Nginx virtual host configuration:
$ sudo nano /etc/nginx/sites-available/flask_app
4. Add the following, replacing flask.example.com with your domain and /home/linuxuser/flask_project with your project path:
server {
listen 80;
server_name flask.example.com;
location / {
include proxy_params;
proxy_pass http://unix:/home/linuxuser/flask_project/flask_app.sock;
}
}
Save and close the file.
5. Link the configuration to sites-enabled to enable it:
$ sudo ln -s /etc/nginx/sites-available/flask_app /etc/nginx/sites-enabled
6. Test the Nginx configuration:
$ sudo nginx -t
Output:
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
7. Restart Nginx to apply the changes:
$ sudo systemctl restart nginx
8. Check the firewall status:
$ sudo ufw status
9. Remove the earlier port 5000 firewall rule:
$ sudo ufw delete allow 5000/tcp
10. Allow the Nginx Full profile to enable HTTP and HTTPS:
$ sudo ufw allow 'Nginx Full'
11. Reload UFW to apply the changes:
$ sudo ufw reload
12. Set your user's home directory permissions to 755 so Nginx can read it. Replace linuxuser with your actual user:
$ sudo chmod 755 /home/linuxuser/
Generate Trusted SSL Certificates
The Flask app currently serves plain HTTP, which is insecure. Use Let's Encrypt to generate a trusted SSL certificate.
1. Install the Certbot Let's Encrypt client via Snap:
$ sudo snap install --classic certbot
2. Generate a certificate for your domain, replacing flask.example.com and user@example.com with your actual domain and email:
$ sudo certbot --nginx -d flask.example.com -m user@example.com --agree-tos
3. Verify that Certbot auto-renews the certificate:
$ sudo certbot renew --dry-run
Output should look similar to:
Account registered.
Simulating renewal of an existing certificate for flask.example.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/flask.example.com\/fullchain.pem (success)
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
4. Restart Nginx to apply the SSL configuration:
$ sudo systemctl restart nginx
5. Access your domain in a browser and confirm the Flask application loads over HTTPS:
https://flask.example.com
6. Uninstall Flask on Ubuntu 24.04
1. Deactivate the virtual environment:
$ deactivate
2. Delete the Flask project directory:
$ rm -rf ~/flask_project
3. Uninstall Flask if it was installed globally:
$ pip uninstall Flask
7. Troubleshooting
-
Check the Gunicorn logs for detailed errors:
$ journalctl -u flask_app.service -f -
On a 502 Bad Gateway error:
-
Verify the Flask service is running:
$ sudo systemctl status flask_app -
Verify your user's home directory has at least
0755permissions:
$ sudo chmod 755 /home/linuxuser/ -
Check the Nginx error logs:
$ sudo tail -f /var/log/nginx/error.log
-
If the application doesn't return the expected output, double-check your configuration and restart the app.
Next Steps
- Swap the sample route for your real application logic and add additional endpoints.
- Add monitoring and error tracking (e.g., Sentry, Prometheus) for production visibility.
- Automate certificate renewal checks and set up log rotation for Gunicorn and Nginx.
- Explore Flask extensions (Flask-SQLAlchemy, Flask-Migrate, Flask-Login) to expand functionality.
For the full guide with additional tips, visit the original article on Vultr Docs.
Top comments (0)