Deploy Python ASGI applications with Hypercorn server, systemd service management, and Nginx reverse proxy for production environments. Complete setup with SSL termination and performance optimization.
Prerequisites
- Root or sudo access
- Python 3.8 or higher
- Basic understanding of web servers
What this solves
Hypercorn is a high-performance ASGI server for Python web applications like FastAPI, Django, and Quart. This tutorial shows you how to deploy ASGI applications with Hypercorn behind an Nginx reverse proxy, manage it with systemd, and configure SSL termination for production environments.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you have the latest versions available.
sudo apt update && sudo apt upgrade -y
Install Python and pip
Install Python 3 and pip package manager if not already installed.
sudo apt install -y python3 python3-pip python3-venv
Create application user and directory
Create a dedicated user to run your application for security isolation.
sudo useradd --system --shell /bin/false --home /opt/myapp myapp
sudo mkdir -p /opt/myapp
sudo chown myapp:myapp /opt/myapp
Create sample FastAPI application
Create a basic FastAPI application to demonstrate the setup.
sudo -u myapp mkdir -p /opt/myapp/app
sudo -u myapp python3 -m venv /opt/myapp/venv
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI(title="Sample API", version="1.0.0")
@app.get("/")
async def read_root():
return JSONResponse(content={"message": "Hello from Hypercorn!", "status": "running"})
@app.get("/health")
async def health_check():
return JSONResponse(content={"status": "healthy"})
@app.get("/info")
async def app_info():
return JSONResponse(content={
"app": "Sample FastAPI Application",
"server": "Hypercorn ASGI",
"version": "1.0.0"
})
Install Hypercorn and dependencies
Install Hypercorn and FastAPI in the virtual environment.
sudo -u myapp /opt/myapp/venv/bin/pip install --upgrade pip
sudo -u myapp /opt/myapp/venv/bin/pip install hypercorn fastapi uvloop
Create Hypercorn configuration file
Configure Hypercorn with production-ready settings for performance and security.
[hypercorn]
bind = "127.0.0.1:8000"
workers = 4
worker_class = "asyncio"
backlog = 2048
max_requests = 1000
max_requests_jitter = 100
preload_app = true
timeout = 120
keepalive_timeout = 2
max_concurrent = 1000
logger_class = "hypercorn.logging.Config"
accesslog = "/var/log/myapp/access.log"
errorlog = "/var/log/myapp/error.log"
loglevel = "info"
use_reloader = false
SSL settings (uncomment for direct SSL)
certfile = "/etc/ssl/certs/example.com.pem"
keyfile = "/etc/ssl/private/example.com.key"
Security settings
server_name = "Hypercorn"
max_buffer_size = 16384
h11_max_incomplete_size = 16384
Create log directory
Create the log directory with proper permissions for the application user.
sudo mkdir -p /var/log/myapp
sudo chown myapp:myapp /var/log/myapp
sudo chmod 755 /var/log/myapp
Create systemd service file
Configure systemd to manage the Hypercorn process with proper resource limits and security settings.
[Unit]
Description=Hypercorn ASGI Server for MyApp
After=network.target
Requires=network.target
[Service]
Type=exec
User=myapp
Group=myapp
WorkingDirectory=/opt/myapp
Environment=PATH=/opt/myapp/venv/bin
Environment=PYTHONPATH=/opt/myapp/app
ExecStart=/opt/myapp/venv/bin/hypercorn --config /opt/myapp/hypercorn.toml app.main:app
ExecReload=/bin/kill -s HUP $MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/myapp
NoNewPrivileges=true
User=myapp
Group=myapp
Resource limits
LimitNOFILE=65536
LimitNPROC=32768
Restart policy
Restart=always
RestartSec=1
StartLimitBurst=3
StartLimitIntervalSec=60
[Install]
WantedBy=multi-user.target
Set file ownership and permissions
Ensure all application files have correct ownership and secure permissions.
sudo chown -R myapp:myapp /opt/myapp
sudo chmod -R 755 /opt/myapp
sudo chmod 644 /opt/myapp/hypercorn.toml
sudo chmod 644 /opt/myapp/app/main.py
Install and configure Nginx
Install Nginx to act as a reverse proxy with SSL termination and load balancing.
sudo apt install -y nginx
Configure Nginx reverse proxy
Create an Nginx virtual host configuration with caching, compression, and security headers.
upstream hypercorn_backend {
server 127.0.0.1:8000;
keepalive 32;
}
server {
listen 80;
server_name example.com www.example.com;
# Security headers
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
add_header X-XSS-Protection "1; mode=block";
add_header Referrer-Policy "strict-origin-when-cross-origin";
# Gzip compression
gzip on;
gzip_vary on;
gzip_min_length 1024;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
# Client body size limit
client_max_body_size 16M;
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
location / {
proxy_pass http://hypercorn_backend;
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;
# Connection settings
proxy_http_version 1.1;
proxy_set_header Connection "";
# Buffer settings
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
}
location /health {
proxy_pass http://hypercorn_backend/health;
access_log off;
}
location /static {
alias /opt/myapp/static;
expires 30d;
add_header Cache-Control "public, immutable";
}
# Log files
access_log /var/log/nginx/myapp_access.log;
error_log /var/log/nginx/myapp_error.log;
}
Enable Nginx site
Enable the site configuration and test the Nginx configuration.
sudo ln -sf /etc/nginx/sites-available/myapp /etc/nginx/sites-enabled/myapp
sudo rm -f /etc/nginx/sites-enabled/default
sudo nginx -t
Configure log rotation
Set up log rotation to prevent log files from growing too large.
/var/log/myapp/*.log {
daily
rotate 14
compress
delaycompress
missingok
create 0644 myapp myapp
postrotate
systemctl reload myapp
endscript
}
Start and enable services
Enable both services to start automatically on boot and start them now.
sudo systemctl daemon-reload
sudo systemctl enable --now myapp
sudo systemctl enable --now nginx
sudo systemctl status myapp
sudo systemctl status nginx
Performance tuning
Optimize Hypercorn workers
Adjust worker count based on your server's CPU cores. The general rule is (CPU cores × 2) + 1.
nproc
If you have 4 cores, use 9 workers
sudo sed -i 's/workers = 4/workers = 9/' /opt/myapp/hypercorn.toml
Configure system limits
Increase system limits for file descriptors and processes to handle more concurrent connections.
myapp soft nofile 65536
myapp hard nofile 65536
myapp soft nproc 32768
myapp hard nproc 32768
Optimize kernel parameters
Tune kernel network parameters for better performance with high connection loads.
net.core.somaxconn = 65536
net.ipv4.tcp_max_syn_backlog = 65536
net.core.netdev_max_backlog = 5000
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 120
net.ipv4.tcp_keepalive_intvl = 30
net.ipv4.tcp_keepalive_probes = 3
sudo sysctl -p /etc/sysctl.d/99-myapp.conf
SSL configuration
Install Certbot for SSL certificates
Install Certbot to automatically obtain and renew SSL certificates from Let's Encrypt.
sudo apt install -y certbot python3-certbot-nginx
Obtain SSL certificate
Get an SSL certificate for your domain. Replace example.com with your actual domain.
sudo certbot --nginx -d example.com -d www.example.com
Monitoring setup
Create monitoring script
Create a simple monitoring script to check application health and restart if needed.
#!/bin/bash
Health check endpoint
HEALTH_URL="http://127.0.0.1:8000/health"
MAX_RETRIES=3
RETRY_DELAY=5
for i in $(seq 1 $MAX_RETRIES); do
if curl -f -s --max-time 10 "$HEALTH_URL" > /dev/null; then
echo "$(date): Application is healthy"
exit 0
fi
echo "$(date): Health check failed (attempt $i/$MAX_RETRIES)"
if [ $i -lt $MAX_RETRIES ]; then
sleep $RETRY_DELAY
fi
done
echo "$(date): Application unhealthy, restarting service"
logger "myapp: Health check failed, restarting service"
sudo systemctl restart myapp
exit 1
sudo chmod +x /opt/myapp/monitor.sh
sudo chown myapp:myapp /opt/myapp/monitor.sh
Schedule monitoring with cron
Set up a cron job to run the health check every 5 minutes.
sudo crontab -u myapp -e
Add this line to the crontab:
/5 * /opt/myapp/monitor.sh >> /var/log/myapp/monitor.log 2>&1
Verify your setup
Test your Hypercorn deployment with these verification commands:
# Check service status
sudo systemctl status myapp
sudo systemctl status nginx
Test application directly
curl -i http://127.0.0.1:8000/
curl -i http://127.0.0.1:8000/health
Test through Nginx proxy
curl -i http://your-domain.com/
curl -i http://your-domain.com/health
Check logs
sudo tail -f /var/log/myapp/access.log
sudo tail -f /var/log/myapp/error.log
sudo tail -f /var/log/nginx/myapp_access.log
Verify SSL (if configured)
openssl s_client -connect your-domain.com:443 -servername your-domain.com
Test worker processes
ps aux | grep hypercorn
Check listening ports
sudo netstat -tlnp | grep -E ':80|:443|:8000'
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| Service fails to start | Permission denied or missing files | Check sudo journalctl -u myapp and verify file ownership with sudo chown -R myapp:myapp /opt/myapp |
| 502 Bad Gateway | Hypercorn not running or wrong port | Verify service with sudo systemctl status myapp and check port binding in config |
| High memory usage | Too many workers for available RAM | Reduce workers in /opt/myapp/hypercorn.toml and restart service |
| Slow response times | Insufficient worker processes | Increase workers to (CPU cores × 2) + 1 in configuration file |
| Log permission errors | Wrong log directory ownership | Fix with sudo chown -R myapp:myapp /var/log/myapp |
| SSL certificate errors | Expired or missing certificates | Renew with sudo certbot renew --dry-run |
| Connection refused errors | Firewall blocking ports | Check firewall rules and ensure ports 80 and 443 are open |
For additional troubleshooting, check our guide on fixing systemd service restart loops or managing systemd services.
Next steps
- Configure Uvicorn ASGI server as an alternative
- Advanced Nginx reverse proxy configuration
- Set up monitoring with Prometheus and Grafana
- Load balance multiple Hypercorn instances with HAProxy
- Containerize your FastAPI applications with Docker
- Enable WebSocket support for real-time features
Running this in production?
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
APP_NAME=${1:-myapp}
DOMAIN=${2:-localhost}
BIND_IP=${3:-127.0.0.1}
BIND_PORT=${4:-8000}
# Usage message
usage() {
echo "Usage: $0 [app_name] [domain] [bind_ip] [bind_port]"
echo "Example: $0 myapp example.com 127.0.0.1 8000"
exit 1
}
# Error handling and cleanup
cleanup() {
echo -e "${RED}Installation failed. Cleaning up...${NC}"
systemctl stop "$APP_NAME" 2>/dev/null || true
systemctl disable "$APP_NAME" 2>/dev/null || true
rm -f "/etc/systemd/system/$APP_NAME.service"
userdel "$APP_NAME" 2>/dev/null || true
rm -rf "/opt/$APP_NAME"
rm -rf "/var/log/$APP_NAME"
}
trap cleanup ERR
# Check if running as root
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root${NC}"
exit 1
fi
# Validate arguments
if [[ ! "$APP_NAME" =~ ^[a-zA-Z][a-zA-Z0-9_-]*$ ]]; then
echo -e "${RED}Invalid app name. Use alphanumeric characters, hyphens, and underscores only.${NC}"
usage
fi
echo -e "${GREEN}Installing Hypercorn ASGI server with systemd for $APP_NAME${NC}"
# Auto-detect distro
echo "[1/12] Detecting distribution..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
PKG_UPDATE="apt update && apt upgrade -y"
PYTHON_PKG="python3 python3-pip python3-venv"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
PKG_UPDATE="dnf update -y"
PYTHON_PKG="python3 python3-pip python3-venv"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
PKG_UPDATE="yum update -y"
PYTHON_PKG="python3 python3-pip python3-venv"
;;
*)
echo -e "${RED}Unsupported distro: $ID${NC}"
exit 1
;;
esac
else
echo -e "${RED}Cannot detect distribution${NC}"
exit 1
fi
echo -e "${GREEN}Detected: $PRETTY_NAME (using $PKG_MGR)${NC}"
# Update system packages
echo "[2/12] Updating system packages..."
$PKG_UPDATE
# Install Python and dependencies
echo "[3/12] Installing Python and pip..."
$PKG_INSTALL $PYTHON_PKG
# Create application user
echo "[4/12] Creating application user and directory..."
if ! id "$APP_NAME" &>/dev/null; then
useradd --system --shell /bin/false --home "/opt/$APP_NAME" "$APP_NAME"
fi
mkdir -p "/opt/$APP_NAME"
chown "$APP_NAME:$APP_NAME" "/opt/$APP_NAME"
# Create sample FastAPI application
echo "[5/12] Creating sample FastAPI application..."
sudo -u "$APP_NAME" mkdir -p "/opt/$APP_NAME/app"
sudo -u "$APP_NAME" python3 -m venv "/opt/$APP_NAME/venv"
cat > "/opt/$APP_NAME/app/main.py" << EOF
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI(title="$APP_NAME API", version="1.0.0")
@app.get("/")
async def read_root():
return JSONResponse(content={"message": "Hello from Hypercorn!", "status": "running"})
@app.get("/health")
async def health_check():
return JSONResponse(content={"status": "healthy"})
@app.get("/info")
async def app_info():
return JSONResponse(content={
"app": "$APP_NAME FastAPI Application",
"server": "Hypercorn ASGI",
"version": "1.0.0"
})
EOF
chown "$APP_NAME:$APP_NAME" "/opt/$APP_NAME/app/main.py"
chmod 644 "/opt/$APP_NAME/app/main.py"
# Install Hypercorn and dependencies
echo "[6/12] Installing Hypercorn and FastAPI..."
sudo -u "$APP_NAME" "/opt/$APP_NAME/venv/bin/pip" install --upgrade pip
sudo -u "$APP_NAME" "/opt/$APP_NAME/venv/bin/pip" install hypercorn fastapi uvloop
# Create log directory
echo "[7/12] Creating log directory..."
mkdir -p "/var/log/$APP_NAME"
chown "$APP_NAME:$APP_NAME" "/var/log/$APP_NAME"
chmod 755 "/var/log/$APP_NAME"
# Create Hypercorn configuration file
echo "[8/12] Creating Hypercorn configuration..."
cat > "/opt/$APP_NAME/hypercorn.toml" << EOF
bind = "$BIND_IP:$BIND_PORT"
workers = 4
worker_class = "asyncio"
backlog = 2048
max_requests = 1000
max_requests_jitter = 100
preload_app = true
timeout = 120
keepalive_timeout = 2
max_concurrent = 1000
accesslog = "/var/log/$APP_NAME/access.log"
errorlog = "/var/log/$APP_NAME/error.log"
loglevel = "info"
use_reloader = false
server_name = "Hypercorn"
max_buffer_size = 16384
h11_max_incomplete_size = 16384
EOF
chown "$APP_NAME:$APP_NAME" "/opt/$APP_NAME/hypercorn.toml"
chmod 644 "/opt/$APP_NAME/hypercorn.toml"
# Create systemd service file
echo "[9/12] Creating systemd service..."
cat > "/etc/systemd/system/$APP_NAME.service" << EOF
[Unit]
Description=Hypercorn ASGI Server for $APP_NAME
After=network.target
Requires=network.target
[Service]
Type=exec
User=$APP_NAME
Group=$APP_NAME
WorkingDirectory=/opt/$APP_NAME
Environment=PATH=/opt/$APP_NAME/venv/bin
Environment=PYTHONPATH=/opt/$APP_NAME/app
ExecStart=/opt/$APP_NAME/venv/bin/hypercorn --config /opt/$APP_NAME/hypercorn.toml app.main:app
ExecReload=/bin/kill -s HUP \$MAINPID
KillMode=mixed
TimeoutStopSec=5
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/log/$APP_NAME
NoNewPrivileges=true
LimitNOFILE=65536
LimitNPROC=32768
Restart=always
RestartSec=1
StartLimitBurst=3
StartLimitIntervalSec=60
[Install]
WantedBy=multi-user.target
EOF
chmod 644 "/etc/systemd/system/$APP_NAME.service"
# Set proper permissions
echo "[10/12] Setting file permissions..."
chown -R "$APP_NAME:$APP_NAME" "/opt/$APP_NAME"
find "/opt/$APP_NAME" -type d -exec chmod 755 {} \;
find "/opt/$APP_NAME" -type f -exec chmod 644 {} \;
chmod 755 "/opt/$APP_NAME/venv/bin"/*
# Configure SELinux if enabled
if command -v getenforce >/dev/null 2>&1 && [[ $(getenforce) != "Disabled" ]]; then
echo "[11/12] Configuring SELinux..."
setsebool -P httpd_can_network_connect 1
semanage fcontext -a -t bin_t "/opt/$APP_NAME/venv/bin/.*" 2>/dev/null || true
restorecon -Rv "/opt/$APP_NAME" 2>/dev/null || true
fi
# Start and enable service
echo "[12/12] Starting and enabling $APP_NAME service..."
systemctl daemon-reload
systemctl enable "$APP_NAME"
systemctl start "$APP_NAME"
# Verification
echo -e "${YELLOW}Verifying installation...${NC}"
sleep 3
if systemctl is-active --quiet "$APP_NAME"; then
echo -e "${GREEN}✓ Service is running${NC}"
else
echo -e "${RED}✗ Service failed to start${NC}"
systemctl status "$APP_NAME" --no-pager
exit 1
fi
if curl -s "http://$BIND_IP:$BIND_PORT/health" >/dev/null; then
echo -e "${GREEN}✓ Health check passed${NC}"
else
echo -e "${RED}✗ Health check failed${NC}"
exit 1
fi
# Installation complete
echo -e "${GREEN}Installation completed successfully!${NC}"
echo -e "${YELLOW}Application details:${NC}"
echo " Service: $APP_NAME"
echo " URL: http://$BIND_IP:$BIND_PORT"
echo " Logs: journalctl -u $APP_NAME -f"
echo " Config: /opt/$APP_NAME/hypercorn.toml"
echo " App directory: /opt/$APP_NAME/app"
echo ""
echo -e "${YELLOW}Next steps:${NC}"
echo " 1. Configure Nginx reverse proxy"
echo " 2. Set up SSL certificates"
echo " 3. Configure firewall rules"
echo " 4. Test with: curl http://$BIND_IP:$BIND_PORT/health"
Review the script before running. Execute with: bash install.sh