bext.dev
DocsOperations
Operations5 min read
On this page

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:

bash
# 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:

bash
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:

  1. 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).
bash
# Check if port 80 is open
ss -tlnp | grep :80
  1. DNS not pointing to this server. The domain must resolve to the server's public IP. Verify with:
bash
dig +short domain.com
curl -I http://domain.com/.well-known/acme-challenge/test
  1. 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:

bash
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:

toml
[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:

toml
[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 getServerSideProps or server actions)
  • Infinite loops in rendering logic
  • Very large component trees

Increase the timeout as a stopgap, then fix the underlying issue:

toml
[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:

toml
[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:

bash
# 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:

bash
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:

toml
[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:

toml
[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:

toml
[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:

bash
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:

toml
[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:

bash
# 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:

toml
[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:

toml
[logging]
rotate = true
rotate_max_size_mb = 100
rotate_max_files = 10
rotate_compress = true

Or use logrotate externally:

code
/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:

toml
[image]
cache_max_size_mb = 2048    # evict LRU entries above 2GB

Getting Help#

If these steps do not resolve your issue:

  1. Run bext-server check --fail-on-warnings and include the output
  2. Set log level to debug and capture the relevant log lines
  3. Check the GitHub Issues for known problems
  4. Open a new issue with your bext version (bext-server version), OS, config file (redact secrets), and the full error output
Tip

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.

  • 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
Edit this page ↗Need a hand? ↗
FIND YOUR NEXT STEP

Start with a topic, a command, or a question.