Migrate from nginx

bext replaces nginx entirely — TLS, reverse proxy, static serving, compression, PHP, virtual hosts — in a single binary. There are two migration paths:

Option A: Automatic (Zero Config)

bext can read your existing nginx config directly. No manual translation needed.

# Build with nginx-compat feature
cargo build -p bext-server --features nginx-compat --release

# Launch the preflight dashboard (auto-detects /etc/nginx/nginx.conf)
sudo ./target/release/bext-server nginx-takeover

This opens a web dashboard on http://localhost:9999 showing:

- All virtual hosts with server names, ports, SSL, locations

- Preflight checks (cert access, port availability, permissions)

- Click "Take Over" to stop nginx and serve all traffic via bext

- Click "Rollback" to instantly restore nginx

What gets auto-detected

nginx feature bext handling
server {} blocks Virtual host routing
server_name (exact, wildcard, regex) Hostname matching
listen 80/443 ssl http2 Port binding + TLS
ssl_certificate / ssl_certificate_key rustls cert loading
location (=, ^~, ~, ~*, prefix) nginx-compatible priority matching
proxy_pass + proxy_set_header Reverse proxy with header injection
upstream {} (least_conn, ip_hash) Load balancing
try_files File probing + SPA/PHP fallback
fastcgi_pass Embedded PHP runtime
gzip gzip + brotli + zstd compression
add_header Response headers
return / redirects HTTP→HTTPS, domain redirects
include (recursive globs) Full include expansion

What's skipped (with warnings)

Directives like rewrite with captures, map blocks, if conditionals, auth_basic, and njs scripting are parsed without errors but don't translate. The dashboard shows these as warnings.

Option B: Manual Translation

For a clean start, translate your nginx config to bext.config.toml.

Static Site

# nginx
server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate /etc/ssl/cert.pem;
    ssl_certificate_key /etc/ssl/key.pem;
    root /var/www/html;
    index index.html;
    location / {
        try_files $uri $uri/ =404;
    }
}
# bext.config.toml
[server]
listen = "0.0.0.0:443"
static_dir = "/var/www/html"

[tls]
mode = "manual"
cert = "/etc/ssl/cert.pem"
key = "/etc/ssl/key.pem"

Reverse Proxy

# nginx
upstream api {
    least_conn;
    server 10.0.0.1:8080 weight=3;
    server 10.0.0.2:8080;
    server 10.0.0.3:8080 backup;
    keepalive 32;
}
server {
    listen 80;
    location /api/ {
        proxy_pass http://api;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}
# bext.config.toml
[server]
listen = "0.0.0.0:80"

[upstreams.api]
strategy = "least_conn"
keepalive = 32
servers = [
    { url = "http://10.0.0.1:8080", weight = 3 },
    { url = "http://10.0.0.2:8080" },
    { url = "http://10.0.0.3:8080", backup = true },
]

[[route_rules]]
pattern = "/api/**"
proxy = "api"

PHP (Laravel / WordPress)

# nginx
server {
    listen 80;
    root /var/www/html;
    index index.php index.html;
    location / {
        try_files $uri $uri/ /index.php?$query_string;
    }
    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }
}
# bext.config.toml
[server]
listen = "0.0.0.0:80"

[php]
enabled = true
document_root = "/var/www/html"
workers = 4
index = "index.php"

bext uses an embedded PHP runtime (not external PHP-FPM), so no socket/TCP configuration is needed.

SSL with Auto-Renewal

# nginx (with certbot)
server {
    listen 443 ssl http2;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
}
server {
    listen 80;
    return 301 https://$host$request_uri;
}
# bext.config.toml
[server]
listen = "0.0.0.0:443"

[tls]
mode = "acme"
domains = ["example.com"]
email = "admin@example.com"

bext handles ACME challenges, certificate provisioning, and auto-renewal internally — no certbot needed.

Gzip Compression

# nginx
gzip on;
gzip_types text/plain text/css application/json application/javascript;
gzip_comp_level 6;
gzip_min_length 256;

bext enables gzip + brotli + zstd compression by default. No config needed. To customize:

# bext.config.toml (optional — defaults are sensible)
[[route_rules]]
pattern = "/**"
compression = "balanced"  # off | fast | balanced | max

Key Differences

nginx bext
External PHP-FPM over FastCGI Embedded PHP runtime (no socket config)
certbot for Let's Encrypt Built-in ACME (no certbot needed)
Separate gzip module gzip + brotli + zstd built-in
sites-available / sites-enabled Single bext.config.toml or auto-detection
nginx -t for config test bext config validate
nginx -s reload POST /api/reload or bext-server hot-reload
PID file + master/worker Single process with thread pool
Access log files Structured JSON logs + OpenTelemetry
deny all / allow WAF with IP filtering, geo-blocking, rate limiting
limit_req_zone Built-in rate limiter (in-memory or Redis-backed)
proxy_cache ISR cache with tag-based invalidation

Rollback

If you used the automatic takeover and need to revert:

1. Click "Rollback to nginx" in the preflight dashboard 2. Or manually: sudo systemctl start nginx

bext records nginx's systemd state and restores it exactly on rollback.