Troubleshooting
This page covers common issues you may encounter when running bext, along with diagnostic commands and fixes.
Diagnostic Commands#
Start here. These commands give you a snapshot of the system state:
# Pre-flight check: validates config, checks ports, TLS paths, permissions
bext-server check
# Show loaded configuration (merged from file + env + defaults)
bext-server config show
# Print the route table for the current app
bext-server routes
# Cache hit rates, sizes, and per-app breakdown
bext-server cache stats
# Running apps, render worker count, memory usage
bext-server ps
# Health check (DB, cache, isolates, plugins)
bext-server health
# Current metrics snapshot
bext-server metrics
For verbose output, add --log-level debug or --log-level trace:
bext-server --log-level debug run
TLS Certificate Errors#
ACME challenge failing#
Symptom: error: ACME HTTP-01 challenge failed for domain.com
Causes and fixes:
- Port 80 not reachable. ACME HTTP-01 requires incoming traffic on port 80. Check firewall rules and confirm bext is listening on port 80 (it binds automatically when
tls.auto_acme = true).
# Check if port 80 is open
ss -tlnp | grep :80
- DNS not pointing to this server. The domain must resolve to the server's public IP. Verify with:
dig +short domain.com
curl -I http://domain.com/.well-known/acme-challenge/test
- Another process on port 80. If nginx or Apache is already bound to port 80, stop it or use bext's nginx takeover mode.
Certificate not renewing#
Certificates auto-renew 30 days before expiry. If renewal fails, check the logs:
bext-server --log-level debug run 2>&1 | grep -i acme
Common cause: the ACME account rate limit was hit. Wait 1 hour and restart.
Using custom certificates#
If auto-ACME is not suitable, provide cert files directly:
[tls]
auto_acme = false
cert_path = "/etc/ssl/certs/domain.pem"
key_path = "/etc/ssl/private/domain.key"
Ensure the files are readable by the bext process user and the certificate chain is complete (leaf + intermediates).
SSR Failures#
V8 pool exhaustion#
Symptom: Requests queue up, latency spikes, logs show jsc pool: all workers busy, waiting...
Fix: Increase the render worker count:
[render]
workers = 8 # default is 4, increase for high-traffic SSR
Monitor with render_pool_active and render_pool_idle metrics. If render_pool_idle consistently drops to 0, add more workers.
SSR render timeout#
Symptom: error: SSR render timed out after 10000ms
Your React components are taking too long to render server-side. Common causes:
- Fetching data inside components during SSR (move to
getServerSidePropsor server actions) - Infinite loops in rendering logic
- Very large component trees
Increase the timeout as a stopgap, then fix the underlying issue:
[render]
timeout_ms = 15000 # default is 10000
Out of memory (OOM) kills#
Symptom: render workers getting killed, logs show jsc worker OOM: exceeded 256MB limit
Each render worker has a memory limit to prevent a single render from consuming all RAM:
[render]
worker_memory_limit_mb = 512 # default is 256
If renders consistently hit this limit, audit your SSR code for memory leaks (large data structures, unbounded caches in module scope).
Cache Issues#
Stale content after deploy#
Symptom: Old content still being served after deploying new code.
The ISR cache and compression cache need to be cleared:
# Purge all caches for an app
bext-server cache purge --app my-app
# Or purge everything
bext-server cache purge --all
If using Redis L2, the purge command clears both L1 and L2. Automated deploys via bext deploy purge caches automatically.
Cache stampede under load#
Symptom: High CPU spikes when cache entries expire under heavy traffic.
bext's stampede guard coalesces concurrent requests for the same uncached URL. Verify it is active:
bext-server cache stats --json | jq '.stampede_coalesced'
If stampede is still occurring, ensure stale-while-revalidate (SWR) is configured. SWR serves the stale entry while one background request refreshes it:
[cache.isr]
default_swr_ms = 3_600_000 # serve stale for up to 1 hour while revalidating
ISR cache growing too large on disk#
The ISR cache is in-memory by default (bounded by max_entries). If you have Redis L2 enabled, Redis may grow. Set a TTL on L2 entries:
[cache.isr]
l2_ttl_ms = 300_000 # Redis entries expire after 5 minutes
Plugin Issues#
Plugin timeout#
Symptom: warn: plugin "my-plugin" timed out after 5000ms
Plugins have a default execution timeout to prevent runaway code:
[plugins]
timeout_ms = 10000 # increase from default 5000
For WASM plugins, check if the plugin is doing expensive I/O. For QuickJS plugins, check for infinite loops.
Plugin crash / panic#
Symptom: error: plugin "my-plugin" panicked: ...
Plugins run in isolated sandboxes, so a crash does not take down the server. The plugin is disabled for subsequent requests until reloaded.
Reload plugins without restarting the server:
kill -HUP $(cat /run/bext.pid)
Check the plugin's logs at debug level to see the panic backtrace.
Plugin memory limits#
WASM plugins have configurable memory limits:
[plugins.wasm]
max_memory_mb = 64 # per-plugin memory limit
High CPU Usage#
Transform pipeline#
If CPU is high and the http_request_duration_seconds metric shows long tails, the transform pipeline (TypeScript compilation, JSX transforms) may be the bottleneck. In production, transforms should happen at build time, not per-request:
# Build once for production
bext-server build
# Then run the built output
bext-server run
Image optimization#
On-the-fly image resizing is CPU-intensive. Enable the image cache to avoid reprocessing:
[image]
cache = true
cache_dir = "/var/cache/bext/images"
max_width = 2048
quality = 80
Pre-generate common sizes during build for high-traffic images.
Disk Space#
Log files growing#
Enable log rotation:
[logging]
rotate = true
rotate_max_size_mb = 100
rotate_max_files = 10
rotate_compress = true
Or use logrotate externally:
/var/log/bext/*.log {
daily
rotate 14
compress
missingok
notifempty
postrotate
kill -USR1 $(cat /run/bext.pid)
endscript
}
The USR1 signal tells bext to reopen log files after rotation.
Image cache growth#
Set a maximum size for the image optimization cache:
[image]
cache_max_size_mb = 2048 # evict LRU entries above 2GB
Getting Help#
If these steps do not resolve your issue:
- Run
bext-server check --fail-on-warningsand include the output - Set log level to
debugand capture the relevant log lines - Check the GitHub Issues for known problems
- Open a new issue with your bext version (
bext-server version), OS, config file (redact secrets), and the full error output
bext-server check --fail-on-warnings is the fastest first step before filing an issue. It validates config, checks port availability, TLS paths, and permissions, and often surfaces the exact misconfiguration causing the symptom.
Related#
- Error Codes — stable
E_codes with per-code prose, examples, and next steps - Logging — log level configuration and how to narrow down module-level noise
- Monitoring — Prometheus metrics for detecting pool exhaustion, error rate spikes, and cache degradation
- Invalidation & Restarts — per-site and per-cache surfaces to resolve stale content without a full restart
- Guides: Caching — ISR, stale-while-revalidate, and stampede guard mechanics