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.