A headless, runtime-configurable nginx environment designed for automation, composition, and production deployments.
- 3 Operation Modes: Reverse proxy, static file serving, load balancing
- SSL/TLS Support: Optional HTTPS with certificate management
- Rate Limiting: API protection and DDoS prevention
- Gzip Compression: Response compression for bandwidth savings
- Security Headers: X-Frame-Options, CSP, HSTS, and more
- Caching: Browser/proxy caching with Cache-Control headers
- WebSocket Proxying: Full WebSocket protocol support
- URL Rewriting: HTTPS redirects and WWW enforcement
- Runtime Configuration: All settings via environment variables
- Zero Interaction: Perfect for CI/CD, containers, and composed environments
- Template-Based: Modify templates for advanced use cases
cd nginx
flox activate -sThis starts nginx as a reverse proxy to 127.0.0.1:8080 on port 80.
NGINX_BACKEND_HOST=api.internal \
NGINX_BACKEND_PORT=3000 \
NGINX_PORT=8080 \
flox activate -sNGINX_MODE=static \
NGINX_ROOT=/var/www/mysite \
NGINX_PORT=8080 \
flox activate -sNGINX_MODE=load_balancer \
NGINX_UPSTREAM_SERVERS="app1:8080,app2:8080,app3:8080" \
NGINX_LB_METHOD=least_conn \
flox activate -s| Variable | Default | Options |
|---|---|---|
NGINX_MODE |
proxy |
proxy, static, load_balancer |
| Variable | Default | Description |
|---|---|---|
NGINX_PORT |
80 |
Listen port |
NGINX_HOST |
0.0.0.0 |
Listen address |
NGINX_WORKER_PROCESSES |
auto |
Number of worker processes |
NGINX_WORKER_CONNECTIONS |
1024 |
Connections per worker |
| Variable | Default | Description |
|---|---|---|
NGINX_BACKEND_HOST |
127.0.0.1 |
Upstream server host |
NGINX_BACKEND_PORT |
8080 |
Upstream server port |
NGINX_PROXY_TIMEOUT |
60s |
Proxy timeout |
| Variable | Default | Description |
|---|---|---|
NGINX_ROOT |
$FLOX_ENV_CACHE/www |
Document root path |
NGINX_INDEX |
index.html index.htm |
Index file(s) |
| Variable | Default | Description |
|---|---|---|
NGINX_UPSTREAM_SERVERS |
(required) | Comma-separated server list |
NGINX_LB_METHOD |
round_robin |
round_robin, least_conn, ip_hash |
| Variable | Default | Description |
|---|---|---|
NGINX_SSL_ENABLED |
false |
Enable HTTPS |
NGINX_SSL_CERT |
Path to SSL certificate | |
NGINX_SSL_KEY |
Path to SSL private key | |
NGINX_SSL_PROTOCOLS |
TLSv1.2 TLSv1.3 |
Allowed TLS protocols |
| Variable | Default | Description |
|---|---|---|
NGINX_RATE_LIMIT_ENABLED |
false |
Enable rate limiting |
NGINX_RATE_LIMIT_RATE |
10r/s |
Requests per second |
NGINX_RATE_LIMIT_BURST |
20 |
Burst capacity |
| Variable | Default | Description |
|---|---|---|
NGINX_GZIP_ENABLED |
false |
Enable gzip compression |
NGINX_GZIP_LEVEL |
6 |
Compression level (1-9) |
NGINX_GZIP_MIN_LENGTH |
1000 |
Minimum response size to compress |
NGINX_GZIP_TYPES |
text/plain text/css application/json ... |
MIME types to compress |
| Variable | Default | Description |
|---|---|---|
NGINX_SECURITY_HEADERS_ENABLED |
false |
Enable security headers |
Headers added when enabled:
X-Frame-Options: SAMEORIGINX-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockReferrer-Policy: no-referrer-when-downgradeStrict-Transport-Security(only when SSL is enabled)
| Variable | Default | Description |
|---|---|---|
NGINX_CACHE_ENABLED |
false |
Enable browser caching (static mode only) |
NGINX_CACHE_HTML_EXPIRE |
1h |
Cache expiration for HTML files |
NGINX_CACHE_STATIC_EXPIRE |
7d |
Cache expiration for static assets (css, js, images) |
| Variable | Default | Description |
|---|---|---|
NGINX_WEBSOCKET_ENABLED |
false |
Enable WebSocket proxying (proxy/load_balancer modes) |
| Variable | Default | Description |
|---|---|---|
NGINX_FORCE_HTTPS |
false |
Force HTTPS redirect (301) |
NGINX_FORCE_WWW |
none |
WWW enforcement: add_www, remove_www, or none |
Proxy requests to a local application:
NGINX_BACKEND_HOST=localhost \
NGINX_BACKEND_PORT=3000 \
flox activate -sNGINX_BACKEND_HOST=api.internal \
NGINX_BACKEND_PORT=8080 \
NGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/path/to/fullchain.pem \
NGINX_SSL_KEY=/path/to/privkey.pem \
flox activate -sNGINX_MODE=static \
NGINX_ROOT=/var/www/mysite \
NGINX_PORT=8080 \
flox activate -sNGINX_MODE=load_balancer \
NGINX_UPSTREAM_SERVERS="app1:8080,app2:8080,app3:8080" \
NGINX_LB_METHOD=ip_hash \
NGINX_RATE_LIMIT_ENABLED=true \
NGINX_RATE_LIMIT_RATE=100r/s \
NGINX_RATE_LIMIT_BURST=200 \
flox activate -sNGINX_MODE=proxy \
NGINX_BACKEND_HOST=api.internal \
NGINX_BACKEND_PORT=8001 \
NGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/certs/cert.pem \
NGINX_SSL_KEY=/certs/key.pem \
NGINX_RATE_LIMIT_ENABLED=true \
NGINX_RATE_LIMIT_RATE=50r/s \
flox activate -sNGINX_MODE=static \
NGINX_ROOT=/var/www/mysite \
NGINX_CACHE_ENABLED=true \
NGINX_CACHE_STATIC_EXPIRE=30d \
NGINX_GZIP_ENABLED=true \
NGINX_GZIP_LEVEL=9 \
flox activate -sNGINX_BACKEND_HOST=app.internal \
NGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/certs/cert.pem \
NGINX_SSL_KEY=/certs/key.pem \
NGINX_SECURITY_HEADERS_ENABLED=true \
NGINX_GZIP_ENABLED=true \
NGINX_RATE_LIMIT_ENABLED=true \
flox activate -sNGINX_MODE=load_balancer \
NGINX_UPSTREAM_SERVERS="app1:8080,app2:8080,app3:8080,app4:8080" \
NGINX_LB_METHOD=least_conn \
NGINX_GZIP_ENABLED=true \
NGINX_RATE_LIMIT_ENABLED=true \
NGINX_RATE_LIMIT_RATE=1000r/s \
NGINX_RATE_LIMIT_BURST=2000 \
flox activate -sNGINX_BACKEND_HOST=ws.internal \
NGINX_BACKEND_PORT=3000 \
NGINX_WEBSOCKET_ENABLED=true \
flox activate -sNGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/certs/cert.pem \
NGINX_SSL_KEY=/certs/key.pem \
NGINX_FORCE_HTTPS=true \
NGINX_FORCE_WWW=add_www \
flox activate -sNGINX_MODE=proxy \
NGINX_BACKEND_HOST=app.internal \
NGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/certs/fullchain.pem \
NGINX_SSL_KEY=/certs/privkey.pem \
NGINX_SECURITY_HEADERS_ENABLED=true \
NGINX_GZIP_ENABLED=true \
NGINX_RATE_LIMIT_ENABLED=true \
NGINX_WEBSOCKET_ENABLED=true \
NGINX_FORCE_HTTPS=true \
NGINX_FORCE_WWW=remove_www \
flox activate -sRoutes incoming requests to a backend server. Includes:
- Proxy headers (Host, X-Real-IP, X-Forwarded-For, X-Forwarded-Proto)
- Configurable timeouts
- Transparent proxying
Use Cases:
- API gateway
- Microservices routing
- SSL termination for backend services
Serves static files from a directory. Includes:
- MIME type detection
- Efficient sendfile
- Directory index support
- 404 error handling
Use Cases:
- Single-page applications
- Static websites
- Documentation hosting
Distributes traffic across multiple backend servers. Includes:
- Three load balancing algorithms
- Dynamic upstream generation
- Health checking (via nginx defaults)
Load Balancing Methods:
round_robin: Distribute evenly across serversleast_conn: Route to server with fewest connectionsip_hash: Sticky sessions based on client IP
Use Cases:
- Scaling web applications
- High-availability setups
- Traffic distribution
Enables HTTPS with custom certificates. Automatically:
- Configures TLS 1.2 and 1.3
- Sets secure cipher suites
- Adds SSL directives to all modes
Certificate Requirements:
NGINX_SSL_CERT: Full certificate chain (PEM format)NGINX_SSL_KEY: Private key (PEM format)
Generating Self-Signed Cert (Testing Only):
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout /tmp/nginx-selfsigned.key \
-out /tmp/nginx-selfsigned.crt \
-subj "/CN=localhost"
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/tmp/nginx-selfsigned.crt \
NGINX_SSL_KEY=/tmp/nginx-selfsigned.key \
flox activate -sProtects against abuse and DDoS attacks using nginx's leaky bucket algorithm:
- Limits requests per client IP address
- Configurable rate and burst capacity
nodelaymode (reject excess immediately)
How It Works:
NGINX_RATE_LIMIT_RATE: Sustained rate (e.g.,10r/s= 10 requests per second)NGINX_RATE_LIMIT_BURST: Temporary burst capacity above sustained rate
Example: Rate 10r/s with burst 20:
- Steady state: Accept 10 requests per second
- Burst: Accept up to 20 requests instantly, then enforce rate limit
Compresses responses to reduce bandwidth usage and improve load times:
- Automatically compresses configured MIME types
- Adjustable compression level (1-9, higher = better compression but more CPU)
- Minimum size threshold prevents compressing tiny responses
- Adds
Vary: Accept-Encodingheader for proper caching
How It Works:
NGINX_GZIP_LEVEL: Compression level (6 is a good balance)NGINX_GZIP_MIN_LENGTH: Skip compression for responses smaller than this (default: 1000 bytes)NGINX_GZIP_TYPES: Only compress specified MIME types
Performance Tips:
- Use level 6 for best performance/compression balance
- Higher levels (7-9) use significantly more CPU for marginal gains
- Don't compress already-compressed formats (images, videos)
Adds common security headers to protect against web vulnerabilities:
- X-Frame-Options: Prevents clickjacking attacks
- X-Content-Type-Options: Prevents MIME type sniffing
- X-XSS-Protection: Enables browser XSS protection
- Referrer-Policy: Controls referrer information
- Strict-Transport-Security (HSTS): Forces HTTPS (only added when SSL is enabled)
HSTS Behavior:
- Automatically added when both
NGINX_SECURITY_HEADERS_ENABLED=trueandNGINX_SSL_ENABLED=true - Sets max-age to 1 year (31536000 seconds)
- Includes subdomains
Security Best Practices:
- Always enable security headers for production deployments
- Combine with SSL/TLS for maximum protection
- Test HSTS carefully - once enabled, browsers cache it
Configures browser/proxy caching to reduce server load and improve performance:
- Works in static mode only
- Separate cache policies for HTML and static assets
- Adds appropriate
Cache-ControlandExpiresheaders
Cache Policies:
- HTML files: Shorter cache (default: 1 hour) - allows content updates
- Static assets (CSS, JS, images): Longer cache (default: 7 days) - marked as immutable
- Assets matched:
jpg, jpeg, png, gif, ico, css, js, svg, woff, woff2, ttf, eot
How It Works:
- HTML gets
Cache-Control: public, max-age=3600andExpiresheader - Static assets get
Cache-Control: public, immutableand longer expiration - Browsers won't re-request cached files until expiration
Best Practices:
- Use cache busting (e.g.,
app.js?v=123) for versioned assets - Longer cache for static assets = faster repeat visits
- Shorter cache for HTML = faster content updates
Enables full WebSocket protocol support for real-time applications:
- Works in proxy and load_balancer modes only
- Automatically upgrades HTTP connections to WebSocket
- Adds required headers:
Upgrade,Connection - Sets
proxy_http_versionto 1.1
How It Works:
- Detects WebSocket upgrade requests via
$http_upgradevariable - Preserves WebSocket connection through proxy
- Supports both
ws://andwss://(with SSL enabled)
Use Cases:
- Real-time chat applications
- Live dashboards and monitoring
- WebSocket APIs
- Gaming servers
- Collaborative editing tools
Example Configuration:
# WebSocket server on ws://backend:3000
NGINX_BACKEND_HOST=backend
NGINX_BACKEND_PORT=3000
NGINX_WEBSOCKET_ENABLED=true
flox activate -sProvides common URL rewriting and redirect patterns:
- Force HTTPS redirects (HTTP → HTTPS)
- WWW enforcement (add or remove www subdomain)
- 301 permanent redirects for SEO
- Works across all modes
Force HTTPS:
- Redirects all HTTP traffic to HTTPS
- Returns 301 (permanent redirect)
- Preserves full request URI
- Requires SSL to be configured on the HTTPS port
WWW Enforcement:
add_www: Redirectsexample.com→www.example.comremove_www: Redirectswww.example.com→example.comnone: No WWW redirect (default)
How It Works:
- Uses nginx
ifdirectives with$schemeand$hostvariables - 301 redirects preserve query strings and paths
- Executes before location block processing
Use Cases:
- SEO: Consolidate domain authority to one canonical URL
- Security: Force HTTPS for all traffic
- Consistency: Ensure all traffic uses same domain format
Example:
# Force HTTPS and remove www
NGINX_PORT=443
NGINX_SSL_ENABLED=true
NGINX_SSL_CERT=/certs/cert.pem
NGINX_SSL_KEY=/certs/key.pem
NGINX_FORCE_HTTPS=true
NGINX_FORCE_WWW=remove_www
flox activate -sImportant Notes:
- HTTPS redirect requires SSL configuration on port 443
- Test WWW redirects carefully - affects SEO
- Both redirects can be enabled simultaneously
# myapp/.flox/env/manifest.toml
[include]
environments = [
{ remote = "yourorg/nginx" },
]
[hook]
on-activate = '''
export NGINX_MODE="proxy"
export NGINX_BACKEND_PORT="3000"
'''# fullstack/.flox/env/manifest.toml
[include]
environments = [
{ remote = "yourorg/nginx" },
{ remote = "yourorg/postgres" },
{ remote = "yourorg/redis" },
]
[hook]
on-activate = '''
export NGINX_MODE="proxy"
export NGINX_BACKEND_HOST="localhost"
export NGINX_BACKEND_PORT="3000"
'''Start entire stack: flox activate -s
# Start service
flox activate -s
# Check status
flox services status
# View logs
flox services logs nginx
# Restart with new configuration
flox services restart nginx
# Stop services
flox services stopTemplates are stored in $FLOX_ENV_CACHE/config/:
nginx-proxy.template- Reverse proxy configurationnginx-static.template- Static file serving configurationnginx-loadbalancer.template- Load balancer configuration
Edit templates directly for advanced use cases:
- Add custom headers
- Configure caching
- Add location blocks
- Modify proxy behavior
After editing templates, restart the service:
flox services restart nginxThe final nginx configuration is at:
$FLOX_ENV_CACHE/config/nginx.conf
View generated config:
cat .flox/cache/config/nginx.confLogs are stored in $FLOX_ENV_CACHE/logs/:
nginx.log- Combined stdout/stderraccess.log- HTTP access logserror.log- nginx error logs
View access logs:
tail -f .flox/cache/logs/access.logCheck logs for errors:
flox services logs nginxCommon issues:
- Port already in use: Change
NGINX_PORT - Permission denied: Ports < 1024 require root (use ports ≥ 1024)
- SSL cert not found: Verify
NGINX_SSL_CERTandNGINX_SSL_KEYpaths
View the generated configuration:
cat .flox/cache/config/nginx.confTest configuration manually:
nginx -t -c .flox/cache/config/nginx.confEnsure NGINX_UPSTREAM_SERVERS is set:
NGINX_UPSTREAM_SERVERS="server1:8080,server2:8080"Verify servers are reachable from nginx host.
Increase rate or burst:
NGINX_RATE_LIMIT_RATE=100r/s \
NGINX_RATE_LIMIT_BURST=200 \
flox activate -s- Linux/macOS: Full support
- Ports: Use ports ≥ 1024 for non-root operation
- Permissions: SSL certificate files must be readable by the user running nginx
- Use SSL/TLS for any external-facing deployment
- Enable rate limiting to prevent abuse
- Restrict network access via firewall rules
- Use separate user for nginx process (configure via templates)
- Keep certificates secure (permissions 600, owned by nginx user)
- Use Let's Encrypt for free, valid certificates
- Renew certificates before expiration
- Use strong TLS protocols (default: TLSv1.2, TLSv1.3)
- Review cipher suites for security requirements
NGINX_BACKEND_PORT=3000 flox activate -sNGINX_BACKEND_HOST=staging.internal \
NGINX_RATE_LIMIT_ENABLED=true \
flox activate -sNGINX_PORT=443 \
NGINX_SSL_ENABLED=true \
NGINX_SSL_CERT=/etc/letsencrypt/live/example.com/fullchain.pem \
NGINX_SSL_KEY=/etc/letsencrypt/live/example.com/privkey.pem \
NGINX_RATE_LIMIT_ENABLED=true \
NGINX_RATE_LIMIT_RATE=100r/s \
NGINX_BACKEND_HOST=app.internal \
NGINX_BACKEND_PORT=8080 \
flox activate -s- ollama - AI/ML inference server (works well with nginx proxy)
- postgres - PostgreSQL database
- redis - Redis cache/session store
- nginx Documentation
- nginx Rate Limiting Guide
- Let's Encrypt - Free SSL certificates
For issues with this Flox environment:
- Check logs:
flox services logs nginx - Validate config:
nginx -t -c .flox/cache/config/nginx.conf - Review generated config:
.flox/cache/config/nginx.conf
For nginx-specific issues: