tee /etc/systemd/system/uvicorn.service > /dev/null << 'EOF'
[Unit]
Description=Uvicorn ASGI server for event_scheduler
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/...
ExecStart=/var/www/.../venv/bin/uvicorn \
event_scheduler.asgi:application \
--host 127.0.0.1 \
--port 8001 \
--workers 1
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
Description=Uvicorn ASGI server for event_scheduler
After=network.target
[Service]
User=www-data
Group=www-data
WorkingDirectory=/var/www/...
ExecStart=/var/www/.../venv/bin/uvicorn \
event_scheduler.asgi:application \
--host 127.0.0.1 \
--port 8001 \
--workers 1
Restart=on-failure
RestartSec=5
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable uvicorn
sudo systemctl start uvicorn
sudo systemctl status uvicorn
sudo systemctl enable uvicorn
sudo systemctl start uvicorn
sudo systemctl status uvicorn
What does this mean:
/etc/systemd/system# sudo systemctl status uvicorn
● uvicorn.service - Uvicorn ASGI server for event_scheduler
Loaded: loaded (/etc/systemd/system/uvicorn.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-06-08 04:03:45 UTC; 1min 42s ago
Invocation: 97cc03106bee4426a74bfc670c7cb9ec
Main PID: 12778 (uvicorn)
Tasks: 3 (limit: 2262)
Memory: 81.4M (peak: 118.4M)
CPU: 1.576s
CGroup: /system.slice/uvicorn.service
└─12778 /var/www/.../venv/bin/python3 /var/www/python_dja>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: from . import views
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: from .agent1 import agent1_executor
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: openai.api_key = os.environ["OPENAI_API_KEY"] # "sk-proj-y>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "<frozen os>", line 717, in __getitem__
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: KeyError: 'OPENAI_API_KEY'
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: INFO: 127.0.0.1:39684 - "GET /favicon.ico HTTP/1.1" 500 In>
root@hal-server-685502:/etc/systemd/system#
/etc/systemd/system# sudo systemctl status uvicorn
● uvicorn.service - Uvicorn ASGI server for event_scheduler
Loaded: loaded (/etc/systemd/system/uvicorn.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-06-08 04:03:45 UTC; 1min 42s ago
Invocation: 97cc03106bee4426a74bfc670c7cb9ec
Main PID: 12778 (uvicorn)
Tasks: 3 (limit: 2262)
Memory: 81.4M (peak: 118.4M)
CPU: 1.576s
CGroup: /system.slice/uvicorn.service
└─12778 /var/www/.../venv/bin/python3 /var/www/python_dja>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: from . import views
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: from .agent1 import agent1_executor
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "/var/www/...>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: openai.api_key = os.environ["OPENAI_API_KEY"] # "sk-proj-y>
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: ~~~~~~~~~~^^^^^^^^^^^^^^^^^^
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: File "<frozen os>", line 717, in __getitem__
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: KeyError: 'OPENAI_API_KEY'
Jun 08 04:03:53 hal-server-685502 uvicorn[12778]: INFO: 127.0.0.1:39684 - "GET /favicon.ico HTTP/1.1" 500 In>
root@hal-server-685502:/etc/systemd/system#
Fix Option 2 (recommended): Use an environment file
Create:
sudo nano /etc/event_scheduler.env
Contents:
OPENAI_API_KEY=sk-...
Then in uvicorn.service:
[Service]
EnvironmentFile=/etc/event_scheduler.env
Reload:
sudo systemctl daemon-reload
sudo systemctl restart uvicorn
root@hal-server-685502:/etc/systemd/system# sudo systemctl status uvicorn
● uvicorn.service - Uvicorn ASGI server for event_scheduler
Loaded: loaded (/etc/systemd/system/uvicorn.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-06-08 04:10:19 UTC; 11s ago
Invocation: 610d449665164a1d81d09924907a22ca
Main PID: 14084 (uvicorn)
Tasks: 1 (limit: 2262)
Memory: 54.7M (peak: 54.9M)
CPU: 332ms
CGroup: /system.slice/uvicorn.service
└─14084 /var/www/.../venv/bin/python3 /var/www/python_dja>
Jun 08 04:10:19 hal-server-685502 systemd[1]: Started uvicorn.service - Uvicorn ASGI server for event_scheduler.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Started server process [14084]
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Waiting for application startup.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: ASGI 'lifespan' protocol appears unsupported.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Application startup complete.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL>
lines 1-17/17 (END)
● uvicorn.service - Uvicorn ASGI server for event_scheduler
Loaded: loaded (/etc/systemd/system/uvicorn.service; enabled; preset: enabled)
Active: active (running) since Mon 2026-06-08 04:10:19 UTC; 11s ago
Invocation: 610d449665164a1d81d09924907a22ca
Main PID: 14084 (uvicorn)
Tasks: 1 (limit: 2262)
Memory: 54.7M (peak: 54.9M)
CPU: 332ms
CGroup: /system.slice/uvicorn.service
└─14084 /var/www/.../venv/bin/python3 /var/www/python_dja>
Jun 08 04:10:19 hal-server-685502 systemd[1]: Started uvicorn.service - Uvicorn ASGI server for event_scheduler.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Started server process [14084]
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Waiting for application startup.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: ASGI 'lifespan' protocol appears unsupported.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Application startup complete.
Jun 08 04:10:19 hal-server-685502 uvicorn[14084]: INFO: Uvicorn running on http://127.0.0.1:8001 (Press CTRL>
lines 1-17/17 (END)
Step 2 â Update Apache config (/etc/apache2/event_scheduler.conf). Add these lines inside the <VirtualHost> block, before the closing tag:
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:8001/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
Step 3 â Enable the wstunnel module and reload Apache:
sudo a2enmod proxy_wstunnel rewrite
sudo systemctl reload apache2
A couple of notes:
- --workers 1 is intentional â Django Channels with in-memory channel layers doesn't work correctly with multiple workers (each worker has its own memory). If you're using Redis as the channel layer, you can increase it.
- Uvicorn will serve only the WebSocket (and any ASGI) traffic on port 8001; Apache continues to serve regular HTTP via mod_wsgi on port 8000.
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:8001/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
Step 3 â Enable the wstunnel module and reload Apache:
sudo a2enmod proxy_wstunnel rewrite
sudo systemctl reload apache2
A couple of notes:
- --workers 1 is intentional â Django Channels with in-memory channel layers doesn't work correctly with multiple workers (each worker has its own memory). If you're using Redis as the channel layer, you can increase it.
- Uvicorn will serve only the WebSocket (and any ASGI) traffic on port 8001; Apache continues to serve regular HTTP via mod_wsgi on port 8000.
==============
1. No SSL/HTTPS VirtualHost â the site only listens on port 8000, not 443.
The browser is trying wss://mountainviews.space/ws/voice/ (port 443 over TLS), but Apache only has a VirtualHost *:8000 with no SSL configured. There's no VirtualHost on port 443 at all, so the connection is refused before it even reaches the WebSocket proxy rules.
2. The proxy rules use HTTP, not HTTPS port.
Even the existing port-8000 vhost proxies WS correctly, but it will never handle wss:// traffic on 443.
What needs to happen:
Apache needs a SSL-enabled VirtualHost on port 443 that proxies WebSocket connections to uvicorn. Something like:
<VirtualHost *:443>
ServerName mountainviews.space
SSLEngine on
SSLCertificateFile
SSLCertificateKeyFile /path/to/key.pem
# Proxy WebSocket to uvicorn
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:8001/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
# Regular HTTP â Django (via WSGI or forward to uvicorn)
ProxyPass / http://127.0.0.1:8001/
ProxyPassReverse / http://127.0.0.1:8001/
</VirtualHost>
Do you have SSL certificates for mountainviews.space (e.g. from Let's Encrypt via certbot)? If so, I can add the proper VirtualHost config. If not, running certbot --apache -d mountainviews.space will obtain and install them automatically.
The browser is trying wss://mountainviews.space/ws/voice/ (port 443 over TLS), but Apache only has a VirtualHost *:8000 with no SSL configured. There's no VirtualHost on port 443 at all, so the connection is refused before it even reaches the WebSocket proxy rules.
2. The proxy rules use HTTP, not HTTPS port.
Even the existing port-8000 vhost proxies WS correctly, but it will never handle wss:// traffic on 443.
What needs to happen:
Apache needs a SSL-enabled VirtualHost on port 443 that proxies WebSocket connections to uvicorn. Something like:
<VirtualHost *:443>
ServerName mountainviews.space
SSLEngine on
SSLCertificateFile
SSLCertificateKeyFile /path/to/key.pem
# Proxy WebSocket to uvicorn
RewriteEngine On
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{HTTP:Connection} upgrade [NC]
RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
ProxyPass /ws/ ws://127.0.0.1:8001/ws/
ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
# Regular HTTP â Django (via WSGI or forward to uvicorn)
ProxyPass / http://127.0.0.1:8001/
ProxyPassReverse / http://127.0.0.1:8001/
</VirtualHost>
Do you have SSL certificates for mountainviews.space (e.g. from Let's Encrypt via certbot)? If so, I can add the proper VirtualHost config. If not, running certbot --apache -d mountainviews.space will obtain and install them automatically.
Fix: vi /etc/apache2/sites-available/000-default-le-ssl.conf
Added:
PassEnv OPENAI_API_KEY
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
+ ProxyPass /ws/ ws://127.0.0.1:8001/ws/
+ ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
+ RewriteEngine On
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
+ RewriteCond %{HTTP:Connection} upgrade [NC]
+ RewriteRule ^/ws/(.*) ws://127.0.0.1:8001/ws/$1 [P,L]
+ ProxyPass /ws/ ws://127.0.0.1:8001/ws/
+ ProxyPassReverse /ws/ ws://127.0.0.1:8001/ws/
No comments:
Post a Comment