bext.dev
DocsGuides
Guides3 min read
On this page

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:

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

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

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

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

# Rollback a specific app
bext rollback dashboard

Each app has its own deploy history and rollback stack.

Multi-Tenant Mode#

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

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

Each app's cache is isolated by namespace, so invalidating one app's cache never affects another. For cache purge mechanics and TTL configuration, see the Caching guide.

Edit this page ↗Need a hand? ↗
FIND YOUR NEXT STEP

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