Python

How to Deploy Python WSGI Apps with CherryPy Behind Nginx on Ubuntu 24.04

CherryPy is a lightweight Python web framework that can serve any WSGI application. By putting CherryPy behind Nginx, you get the performance, security, and manageability benefits of a full-featured HTTP server. In this guide, you’ll learn how to:

  1. Install system dependencies (Python, pip, CherryPy, Nginx)
  2. Create a simple WSGI app served by CherryPy
  3. Configure CherryPy to run as a background service with Systemd
  4. Set up Nginx to reverse-proxy HTTP requests to CherryPy
  5. (Optional) Secure your site with Let’s Encrypt SSL certificates

By the end, your Python app will be running on http://your-domain.com (or https://…) with Nginx handling client traffic and CherryPy serving WSGI.


🧰 Prerequisites

  • Ubuntu 24.04 (or similar Linux) server with SSH access
  • A user with sudo privileges
  • A registered domain name pointing to your server’s public IP
  • Basic familiarity with the command line

Before you begin, update your package lists:

bashCopyEditsudo apt update && sudo apt upgrade -y

1. Install System Dependencies

1.1. Install Python 3 and pip

Ubuntu 24.04 comes with Python 3.10 by default, but let’s ensure it’s installed and then install pip:

bashCopyEditsudo apt install -y python3 python3-venv python3-pip

Verify versions:

bashCopyEditpython3 --version   # e.g., Python 3.10.x
pip3 --version      # e.g., pip 23.x.x

1.2. Create a Dedicated System User

It’s best practice to run your CherryPy app under a non-root user:

bashCopyEditsudo adduser --system --group --no-create-home cherrypy

This creates a system user cherrypy (no interactive login).

1.3. Install CherryPy and Other Python Dependencies

Create a project directory—e.g., /opt/myapp—and set permissions:

bashCopyEditsudo mkdir -p /opt/myapp
sudo chown cherrypy:cherrypy /opt/myapp

Now switch to that user’s context (for file ownership) or use a virtual environment:

bashCopyEditcd /opt/myapp
sudo -u cherrypy python3 -m venv venv
sudo -u cherrypy bash -c "source venv/bin/activate && pip install cherrypy"

If your WSGI app uses Flask, Django, or other libraries, install them inside the same venv. For example, to install Flask:

bashCopyEditsudo -u cherrypy bash -c "source venv/bin/activate && pip install flask"

2. Create a Simple WSGI Application

We’ll build a minimal “Hello, World” WSGI app. Create a file /opt/myapp/app.py owned by cherrypy:

bashCopyEditsudo -u cherrypy tee /opt/myapp/app.py > /dev/null << 'EOF'
import cherrypy

class HelloApp:
    @cherrypy.expose
    def index(self):
        return "Hello from CherryPy WSGI!"

def start_app():
    # Mount the WSGI app at "/"
    cherrypy.tree.graft(HelloApp(), "/")
    # Unsubscribe the default HTTP server (CherryPy’s own) since we'll rely on Nginx
    cherrypy.server.unsubscribe()
    # Configure CherryPy’s WSGI server to listen on localhost:8000
    cherrypy.config.update({
        'server.socket_host': '127.0.0.1',
        'server.socket_port': 8000,
        'engine.autoreload.on': False
    })
    cherrypy.engine.start()
    cherrypy.engine.block()

if __name__ == "__main__":
    start_app()
EOF

Explanation:

  • We define HelloApp with a single index() method exposed at /.
  • We call cherrypy.tree.graft(...), which allows CherryPy to serve this as a WSGI app.
  • We unsubscribe CherryPy’s built-in HTTP server so that this process only listens on 127.0.0.1:8000. Nginx will proxy incoming traffic to that port.
  • engine.autoreload.on=False disables live‐reload—important in production.

3. Configure CherryPy as a Systemd Service

We want CherryPy to start on boot and run in the background. Create a Systemd service file at /etc/systemd/system/cherrypy.service:

bashCopyEditsudo tee /etc/systemd/system/cherrypy.service > /dev/null << 'EOF'
[Unit]
Description=CherryPy WSGI Application
After=network.target

[Service]
Type=simple
User=cherrypy
Group=cherrypy
WorkingDirectory=/opt/myapp
Environment="PATH=/opt/myapp/venv/bin"
ExecStart=/opt/myapp/venv/bin/python /opt/myapp/app.py
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
  • User/Group: Runs under cherrypy:cherrypy.
  • WorkingDirectory: /opt/myapp.
  • Environment: Ensures the virtual environment’s python and pip are used.
  • ExecStart: Launches our app.py.

Reload Systemd, enable, and start:

bashCopyEditsudo systemctl daemon-reload
sudo systemctl enable cherrypy
sudo systemctl start cherrypy

Check status:

bashCopyEditsudo systemctl status cherrypy

You should see CherryPy listening on 127.0.0.1:8000. Verify with:

bashCopyEditsudo ss -tlnp | grep 8000

4. Install and Configure Nginx as a Reverse Proxy

4.1. Install Nginx

If not already installed:

bashCopyEditsudo apt install -y nginx
sudo systemctl enable --now nginx

Verify:

bashCopyEditsudo systemctl status nginx

4.2. Create an Nginx Site Configuration

Create /etc/nginx/sites-available/myapp (replace myapp with your app’s name):

bashCopyEditsudo tee /etc/nginx/sites-available/myapp > /dev/null << 'EOF'
server {
    listen 80;
    server_name your-domain.com www.your-domain.com;

    # Increase client body size if you expect large POSTs
    client_max_body_size 20M;

    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_http_version 1.1;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_buffering off;
    }

    access_log /var/log/nginx/myapp_access.log;
    error_log  /var/log/nginx/myapp_error.log;
}
EOF

Replace your-domain.com with your actual domain. The proxy_set_header directives ensure the correct client IP and host headers are passed to CherryPy.

4.3. Enable the Nginx Site & Test

bashCopyEditsudo ln -s /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx

4.4. Test in a Browser

Navigate to http://your-domain.com/. You should see:

csharpCopyEditHello from CherryPy WSGI!

If you see that, Nginx is successfully proxying requests to CherryPy.


5. (Optional) Secure with Let’s Encrypt SSL

To serve your app over HTTPS, use Certbot to obtain a free certificate:

5.1. Install Certbot

bashCopyEditsudo apt install -y certbot python3-certbot-nginx

5.2. Run Certbot

bashCopyEditsudo certbot --nginx --agree-tos --redirect --hsts --staple-ocsp \
  --email [email protected] \
  -d your-domain.com -d www.your-domain.com

Certbot will:

  • Obtain certificates for your-domain.com and www.your-domain.com
  • Edit your Nginx config to listen on port 443
  • Redirect HTTP → HTTPS automatically

After it finishes, visit https://your-domain.com to verify.


6. Common Tips & Troubleshooting

  1. Logs
    • CherryPy logs to stdout/stderr, view with journalctl -u cherrypy -f.
    • Nginx logs are in /var/log/nginx/myapp_access.log and myapp_error.log.
  2. Firewall
    • Ensure ports 80 and 443 are open: bashCopyEditsudo ufw allow 'Nginx Full'
    • If using iptables directly, open TCP 80 and 443.
  3. Scaling
    • For higher load, consider running multiple CherryPy workers (e.g., via multiprocessing or spawning multiple Systemd services on different ports) and adjust Nginx’s upstream block accordingly.
  4. Environment Variables
    • If your WSGI app needs environment variables (e.g., DATABASE_URL), you can add them under [Service] in the Systemd file: iniCopyEditEnvironment="DATABASE_URL=postgres://user:pass@localhost/dbname"
    • Then reload Systemd (sudo systemctl daemon-reload && sudo systemctl restart cherrypy).
  5. Updating Your App
    • Pull new code into /opt/myapp (e.g., via git pull).
    • Install any new dependencies inside venv (sudo -u cherrypy bash -c "source /opt/myapp/venv/bin/activate && pip install -r requirements.txt").
    • Restart CherryPy: bashCopyEditsudo systemctl restart cherrypy

🚀 Final Thoughts

By combining CherryPy as a pure‐Python WSGI server with Nginx as a reverse proxy (and SSL terminator), you get a robust, production‐ready deployment. CherryPy handles your Python application logic, while Nginx takes care of TLS, static content (if any), and client‐facing HTTP optimizations.


🌿 Hosting Solutions from Greenhost

At Greenhost, we provide reliable, developer-friendly hosting solutions tailored to modern web applications, including:

  • WordPress Hosting – Optimized, secure, and ready for PHP‐based sites
  • Email Hosting – Business‐grade email with high uptime
  • Shared Web Hosting – Affordable plans that support Python, PHP, and more
  • Website Builder – Launch a professional website without writing code

Whether you need a CherryPy-backed Python environment, a LAMP/LEMP stack, or a containerized platform, Greenhost has you covered with fast, secure, and eco-friendly infrastructure. Deploy with confidence on our scalable servers.