Base image moves from AlmaLinux 9 to 10, which forces two related changes: EPEL repo URL bumps to the el10 release RPM, and tini is replaced with dumb-init as PID 1 since tini is not packaged in EPEL 10. Both provide the signal forwarding and zombie reaping the wedged-container fix relies on. Nginx 1.26 in AlmaLinux 10 deprecates the `listen ... http2` parameter, so the server block now uses the separate `http2 on;` directive. Also fixes three issues that affected WebSocket apps (socket.io, ws): - `Connection: upgrade` was hardcoded on every proxied request, including ordinary HTTP. Now driven by a `map $http_upgrade $connection_upgrade` so only genuine upgrade requests carry it. - No explicit proxy read/send timeout meant idle WebSockets were cut at nginx's 60s default. Set to 600s, which clears any sane heartbeat without pinning connection slots (worker_connections is 512, two per client). - `large_client_header_buffers 2 1k` returned 400 for any single header line over 1KB, which session cookies and bearer tokens routinely exceed. Raised to the nginx default of 4 8k; buffers are allocated on demand, so this only costs memory for requests that need it. Verified by rendering the generated config and running it under nginx with a Node backend: WebSocket handshakes return 101 and reach the upstream 'upgrade' event, polling requests arrive with `Connection: close`, and a 3KB cookie returns 200 where the old buffer setting returned 400. README gains a WebSocket Support section covering the PM2 cluster-mode trap, the concurrency ceiling, and reconnects on memory restarts. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
69 lines
2.2 KiB
Bash
Executable File
69 lines
2.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Create nginx configuration for reverse proxy to Node.js app
|
|
cat > /etc/nginx/conf.d/default.conf << EOF
|
|
upstream nodejs_backend {
|
|
server 127.0.0.1:3000;
|
|
}
|
|
|
|
server {
|
|
listen 80;
|
|
server_name $domain $serveralias;
|
|
|
|
# Redirect HTTP to HTTPS
|
|
return 301 https://\$server_name\$request_uri;
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
http2 on;
|
|
server_name $domain $serveralias;
|
|
|
|
ssl_certificate /etc/pki/tls/certs/localhost.crt;
|
|
ssl_certificate_key /etc/pki/tls/private/localhost.key;
|
|
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384;
|
|
|
|
access_log /home/$user/logs/nginx/access.log;
|
|
error_log /home/$user/logs/nginx/error.log;
|
|
|
|
location / {
|
|
proxy_pass http://nodejs_backend;
|
|
proxy_http_version 1.1;
|
|
|
|
# WebSocket support (socket.io, ws). \$connection_upgrade is defined by
|
|
# the map in /etc/nginx/nginx.conf: 'upgrade' when the client asked to
|
|
# upgrade, 'close' otherwise -- a hardcoded 'upgrade' would send the
|
|
# header on every ordinary request too.
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection \$connection_upgrade;
|
|
|
|
# An idle WebSocket sends no bytes, and the default 60s read timeout
|
|
# would cut it. 600s clears any sane heartbeat (socket.io pings every
|
|
# 25s by default) without pinning connection slots for an hour --
|
|
# worker_connections is 512 and each client uses two.
|
|
proxy_read_timeout 600s;
|
|
proxy_send_timeout 600s;
|
|
|
|
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 \$http_x_forwarded_proto;
|
|
proxy_set_header X-CLIENT-IP \$http_x_client_ip;
|
|
proxy_cache_bypass \$http_upgrade;
|
|
}
|
|
|
|
location /ping {
|
|
proxy_pass http://nodejs_backend/ping;
|
|
access_log off;
|
|
}
|
|
|
|
# Static files
|
|
location /static/ {
|
|
alias /home/$user/app/public/;
|
|
expires 30d;
|
|
add_header Cache-Control "public, immutable";
|
|
}
|
|
}
|
|
EOF |