Anasayfa / Software / Deploy a Flask Web Application on a Linux Server: A Step‑by‑Step Guide

Deploy a Flask Web Application on a Linux Server: A Step‑by‑Step Guide

Flask deployment

Deploying a Flask web application to a production‑grade Linux server can feel daunting, especially when you’re used to running the built‑in development server on your laptop. In this guide we’ll walk you through every stage—from provisioning a fresh VPS to serving your app behind Nginx with Gunicorn, securing it with Let’s Encrypt, and troubleshooting the most common snags. By the end you’ll have a solid, repeatable process that you can apply to any Flask project, and you’ll understand why each component matters in a production stack.

What You’ll Need

  • A fresh Linux server (Ubuntu 22.04 LTS is used in examples)
  • Root or sudo access to the server
  • A domain name pointing to the server’s IP address
  • Python 3.9+ installed on the server
  • Basic familiarity with the Linux command line
  • Git installed (optional but recommended)
  • Firewall access (UFW or equivalent)
  • An email address for Let’s Encrypt notifications

Step 1: Prepare the Server

Start by logging into your server via SSH. Update the package index and upgrade existing packages to ensure a clean base:

sudo apt update && sudo apt upgrade -y

Install essential tools that we’ll need later:

sudo apt install -y python3-pip python3-venv git nginx ufw

Enable the firewall and allow only SSH, HTTP, and HTTPS traffic:

sudo ufw allow OpenSSH
sudo ufw allow 'Nginx Full'
sudo ufw enable

At this point the server is secure, up‑to‑date, and ready for the next steps.

Step 2: Set Up a Python Virtual Environment

Navigate to a directory where you want to keep your projects, then create a dedicated folder for the Flask app:

mkdir -p ~/apps/flask_demo && cd ~/apps/flask_demo

Create a virtual environment using python3 -m venv and activate it:

python3 -m venv venv
source venv/bin/activate

When the prompt changes to (venv), you know you’re working inside the isolated environment. This prevents system‑wide packages from interfering with your app.

Step 3: Install Flask and Create a Sample Application

With the virtual environment active, install Flask and Gunicorn (the WSGI server we’ll use later):

pip install --upgrade pip
pip install Flask gunicorn

Create a minimal Flask app called app.py:

cat > app.py << 'EOF'
from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello():
    return "Hello, Flask on Linux!"

if __name__ == '__main__':
    app.run()
EOF

Test it locally:

flask run --host=0.0.0.0 --port=5000

Visit http://your_server_ip:5000 in a browser. You should see the greeting. Press Ctrl+C to stop the dev server.

Step 4: Configure Gunicorn as the WSGI Server

Gunicorn will serve the Flask app in a production‑ready way. First, try a quick one‑off command to verify it works:

gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app

We need a wsgi.py file that exposes the Flask app object:

cat > wsgi.py << 'EOF'
from app import app

if __name__ == "__main__":
    app.run()
EOF

Now run Gunicorn again, this time using the new entry point:

gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app

If you navigate to http://127.0.0.1:8000 on the server (or use curl from your local machine), you should receive the same “Hello, Flask on Linux!” response. When you’re satisfied, stop Gunicorn with Ctrl+C.

Step 5: Create a systemd Service for Gunicorn

Running Gunicorn manually isn’t practical. We’ll create a systemd service so the process starts on boot and restarts on failure.

sudo tee /etc/systemd/system/flask_demo.service > /dev/null << 'EOF'
[Unit]
Description=Gunicorn instance to serve Flask Demo
After=network.target

[Service]
User=$USER
Group=www-data
WorkingDirectory=/home/$USER/apps/flask_demo
Environment="PATH=/home/$USER/apps/flask_demo/venv/bin"
ExecStart=/home/$USER/apps/flask_demo/venv/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app

[Install]
WantedBy=multi-user.target
EOF

Replace $USER with your actual username if the variable isn’t expanded. Then enable and start the service:

sudo systemctl daemon-reload
sudo systemctl start flask_demo
sudo systemctl enable flask_demo
sudo systemctl status flask_demo

The status command should show active (running). If it fails, check the logs with journalctl -u flask_demo -e.

Step 6: Configure Nginx as a Reverse Proxy

Nginx will accept traffic on ports 80/443, terminate TLS, and forward requests to Gunicorn listening on 127.0.0.1:8000. Create a new server block:

sudo tee /etc/nginx/sites-available/flask_demo > /dev/null << 'EOF'
server {
    listen 80;
    server_name example.com www.example.com;

    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;
    }

    location /static {
        alias /home/$USER/apps/flask_demo/static;
    }
}
EOF

Enable the site and test the configuration:

sudo ln -s /etc/nginx/sites-available/flask_demo /etc/nginx/sites-enabled/
sudo nginx -t

If the test passes, reload Nginx:

sudo systemctl reload nginx

Now visiting http://example.com should display the same “Hello, Flask on Linux!” message, but served through Nginx.

Step 7: Secure the Site with Let’s Encrypt SSL

Plain HTTP is fine for testing, but production sites need HTTPS. Install Certbot and obtain a certificate:

sudo apt install -y certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

Follow the interactive prompts—choose to redirect all HTTP traffic to HTTPS when asked. Certbot will automatically edit your Nginx config, reload the service, and set up a renewal timer.

Verify renewal works:

sudo certbot renew --dry-run

At this point your Flask app is reachable via a secure https:// URL, with automatic certificate renewal handled for you.

Common Mistakes to Avoid

1. Running Gunicorn as root. Always run the service under a non‑privileged user (the same user that owns the code).
2. Binding Gunicorn to 0.0.0.0. Exposing the WSGI server directly to the internet bypasses Nginx and defeats the purpose of the reverse proxy. Keep it bound to 127.0.0.1.
3. Forgetting to activate the virtual environment in the systemd file. The Environment="PATH=…" line is crucial; otherwise gunicorn won’t be found.
4. Incorrect file permissions. Nginx runs as www-data. Ensure the app directory is readable by that group, especially static assets.
5. Skipping firewall rules. If ports 80/443 are blocked, external users will never reach Nginx.
6. Mismatched domain names. The domain you pass to Certbot must resolve to the server’s IP; otherwise certificate issuance fails.

Tips and Tricks

• Use a .env file. Store secrets (API keys, DB passwords) outside of source code and load them with python-dotenv.
• Enable Gunicorn’s access logs. Add --access-logfile - to the ExecStart line for real‑time debugging.
• Scale with more workers. A rule of thumb is 2‑4 workers per CPU core, but monitor memory usage.
• Automate deployments. Tools like Fabric, Ansible, or GitHub Actions can push new code, run pip install -r requirements.txt, and restart the systemd service with a single command.
• Use a process manager. If you prefer Docker, replace Gunicorn + systemd with a lightweight container orchestrated by docker-compose.

Frequently Asked Questions

Do I need to use Gunicorn, or can I run Flask directly with Nginx?

Flask’s built‑in server is designed for development only; it’s single‑threaded and lacks many production features (e.g., graceful reloads, proper logging). Gunicorn (or uWSGI) provides a robust WSGI layer that handles concurrency, worker management, and integrates cleanly with Nginx.

What if my app uses a database?

Install the appropriate client library (e.g., psycopg2 for PostgreSQL) inside the virtual environment and configure the DB connection string in an environment variable. Make sure the database server allows connections from your app’s IP and that firewalls permit the required port.

How do I update the app without downtime?

Use a rolling restart: first pull the latest code, then run sudo systemctl reload flask_demo. Reload sends a SIGHUP to Gunicorn, which gracefully shuts down old workers after they finish current requests and spawns new ones with the updated code.

Conclusion

Deploying Flask on a Linux server doesn’t have to be a black box. By separating concerns—Python virtual environment for dependencies, Gunicorn for WSGI serving, systemd for process supervision, Nginx for reverse‑proxy and TLS termination—you build a resilient stack that scales and stays secure. Follow the steps above, watch out for the common pitfalls, and you’ll have a production‑grade Flask app up and running in no time. Happy coding!

Photo by Zach Graves on Unsplash

Etiketlendi: