Files
StarPunk/nginx.conf.example
Phil Skentelbery c559f89a7f feat: add production container support with health check endpoint
Implements Phase 5 containerization specification:
- Add /health endpoint for container monitoring
- Create multi-stage Containerfile (Podman/Docker compatible)
- Add compose.yaml for orchestration
- Add Caddyfile.example for reverse proxy (auto-HTTPS)
- Add nginx.conf.example as alternative
- Update .env.example with container and RSS feed variables
- Add gunicorn WSGI server to requirements.txt

Container features:
- Multi-stage build for smaller image size
- Non-root user (starpunk:1000)
- Health check with database connectivity test
- Volume mount for data persistence
- Resource limits and logging configuration
- Security headers and HTTPS configuration examples

Health check endpoint:
- Tests database connectivity
- Verifies filesystem access
- Returns JSON with status, version, and environment

Following Phase 5 design in docs/designs/phase-5-rss-and-container.md
2025-11-19 10:02:41 -07:00

189 lines
5.8 KiB
Plaintext

# Nginx Configuration for StarPunk
# Alternative to Caddy for reverse proxy
#
# Installation:
# 1. Install Nginx: sudo apt install nginx
# 2. Install Certbot: sudo apt install certbot python3-certbot-nginx
# 3. Copy this file: sudo cp nginx.conf.example /etc/nginx/sites-available/starpunk
# 4. Update your-domain.com to your actual domain
# 5. Create symlink: sudo ln -s /etc/nginx/sites-available/starpunk /etc/nginx/sites-enabled/
# 6. Test config: sudo nginx -t
# 7. Get SSL cert: sudo certbot --nginx -d your-domain.com
# 8. Reload: sudo systemctl reload nginx
# Upstream definition for StarPunk container
upstream starpunk {
server localhost:8000;
keepalive 32;
}
# HTTP server - redirect to HTTPS
server {
listen 80;
listen [::]:80;
server_name your-domain.com;
# ACME challenge for Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/html;
}
# Redirect all other HTTP to HTTPS
location / {
return 301 https://$server_name$request_uri;
}
}
# HTTPS server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name your-domain.com;
# SSL certificates (managed by certbot)
# Update paths after running certbot
ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
# SSL configuration (Mozilla Intermediate)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
# SSL session cache
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
# OCSP stapling
ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/your-domain.com/chain.pem;
# Security headers
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self'; frame-ancestors 'none';" always;
# Logging
access_log /var/log/nginx/starpunk-access.log;
error_log /var/log/nginx/starpunk-error.log;
# Max upload size (for future media uploads)
client_max_body_size 10M;
# Root location - proxy to StarPunk
location / {
# Proxy to upstream
proxy_pass http://starpunk;
# Proxy headers
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;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# WebSocket support (for future features)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Timeouts
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
# Buffering
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 8 4k;
proxy_busy_buffers_size 8k;
# No caching for dynamic content
add_header Cache-Control "no-cache, private" always;
}
# Static files - aggressive caching
location /static/ {
proxy_pass http://starpunk;
proxy_set_header Host $host;
# Long-term caching for static assets
add_header Cache-Control "public, max-age=31536000, immutable";
# Compression
gzip on;
gzip_vary on;
gzip_types text/css application/javascript image/svg+xml;
}
# RSS feed - short-term caching
location /feed.xml {
proxy_pass http://starpunk;
proxy_set_header Host $host;
# Cache for 5 minutes
add_header Cache-Control "public, max-age=300";
# Compression
gzip on;
gzip_types application/rss+xml application/xml;
}
# Health check endpoint - no caching
location /health {
proxy_pass http://starpunk;
proxy_set_header Host $host;
# No caching
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
# Allow monitoring systems access
# Optional: restrict to specific IPs
# allow 10.0.0.0/8; # Internal network
# deny all;
}
# Admin routes - no caching, security
location /admin/ {
proxy_pass http://starpunk;
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;
# No caching for admin
add_header Cache-Control "no-store, no-cache, must-revalidate" always;
# Optional: IP whitelist for admin
# allow 1.2.3.4; # Your IP
# deny all;
}
# Deny access to hidden files
location ~ /\. {
deny all;
access_log off;
log_not_found off;
}
}
# Optional: Redirect www to non-www
# server {
# listen 80;
# listen [::]:80;
# listen 443 ssl http2;
# listen [::]:443 ssl http2;
# server_name www.your-domain.com;
#
# ssl_certificate /etc/letsencrypt/live/your-domain.com/fullchain.pem;
# ssl_certificate_key /etc/letsencrypt/live/your-domain.com/privkey.pem;
#
# return 301 https://your-domain.com$request_uri;
# }