Multi-App

Run dozens of applications on a single bext binary using hostname-based routing. Each app gets its own configuration, cache namespace, and deploy lifecycle while sharing the same process, TLS certificates, and port.

Configuration

Define apps in the [apps] table of bext.config.toml:

# bext.config.toml
[server]
listen = "0.0.0.0:443"

[apps.marketing]
hostname = "example.com"
root = "./apps/marketing"
type = "static"

[apps.dashboard]
hostname = "app.example.com"
root = "./apps/dashboard"
type = "ssr"
workers = 4

[apps.api]
hostname = "api.example.com"
root = "./apps/api"
type = "proxy"
upstream = "http://127.0.0.1:8080"

[apps.blog]
hostname = "blog.example.com"
root = "./apps/blog"
type = "ssr"

Routing Behavior

When a request arrives, bext resolves the app by matching the Host header against configured hostnames:

1. Exact matchapp.example.com matches hostname = "app.example.com" 2. Wildcard match*.example.com matches any subdomain 3. Default app — if no hostname matches, the [apps.default] app handles it (or 404)

[apps.catch_all]
hostname = "*.example.com"
root = "./apps/fallback"
type = "static"

App Types

Type Behavior
static Serves files from root with compression and caching
ssr Runs the V8 render pool for server-side rendering
proxy Reverse proxies to upstream with health checks and circuit breaking
spa Serves index.html for all routes (single-page app)
php Runs the embedded PHP runtime (Enterprise)

Per-App Configuration

Each app can override global settings:

[apps.dashboard]
hostname = "app.example.com"
root = "./apps/dashboard"
type = "ssr"
workers = 4

[apps.dashboard.cache]
max_entries = 5000
default_ttl_ms = 60000

[apps.dashboard.tls]
cert = "/etc/certs/app.example.com/fullchain.pem"
key = "/etc/certs/app.example.com/privkey.pem"

[apps.dashboard.headers]
X-Frame-Options = "DENY"
Content-Security-Policy = "default-src 'self'"

Isolated Caching

Each app gets its own cache namespace. Cache tags, invalidation, and ISR operate independently:

# Invalidate only the blog's cache
bext cache purge --app blog --tag "posts"

# View per-app cache stats
bext cache stats
# marketing: 1,234 entries (12 MB)
# dashboard: 567 entries (8 MB)
# blog:      89 entries (2 MB)

Deploys

Deploy apps independently with zero downtime:

# Deploy only the marketing site
bext deploy ./apps/marketing --app marketing

# Deploy all apps
bext deploy --all

# Rollback a specific app
bext rollback --app dashboard

Each app has its own deploy history and rollback stack.

Multi-Tenant Mode

For SaaS platforms, combine multi-app with tenant isolation:

[apps.saas]
hostname = "*.myplatform.com"
root = "./apps/saas"
type = "ssr"
tenant_resolution = "subdomain"   # extract tenant from subdomain
tenant_cache_isolation = true     # separate cache per tenant

The {tenant_id} is available in your application via the X-Bext-Tenant-Id header.

Limits

- No hard limit on the number of apps per instance

- Each SSR app consumes [render] workers threads from the pool

- TLS certificates are shared by default (use wildcard certs) or per-app

- The admin dashboard at /__bext/admin/ shows stats for all apps