Infrastructure Generators
bext ships four CLI generators that produce infrastructure config tailored to your project. They read bext.config.toml, detect the framework from ambient file markers (like astro.config.mjs, Gemfile, manage.py), and emit a sensible default that you can commit and edit.
All four generators default to stdout so you can pipe them directly to a file, or pass --out <path> to write the file yourself.
bext dockerfile gen#
Emits a multi-stage Dockerfile tailored to the detected framework.
bext dockerfile gen > Dockerfile
# or
bext dockerfile gen --out Dockerfile
The generated Dockerfile has three stages:
bext-base— the official bext runtime image (ghcr.io/bextdev/bext:latestby default; override with--bext-image)framework-build— the framework's toolchain:oven/bun:1for Astro / SvelteKit / SolidStart / Qwik / PRISM,ruby:3.3-slimfor Rails,python:3.12-slimfor Django,composer:2+php:8.3-fpm-alpinefor Laravel,elixir:1.16-alpinefor Phoenixruntime— final minimal image that copies the build output, sets aHEALTHCHECKagainstbext health, and runsbext serve
For projects where no framework is detected, the generator skips the framework-build stage and emits a minimal runtime that just copies the project tree and runs bext serve.
Flags#
| Flag | Default | Purpose |
|---|---|---|
--dir <path> |
. |
Project root to read |
--out <path> |
stdout | Write Dockerfile to this path |
--bext-image <tag> |
ghcr.io/bextdev/bext:latest |
Override the base image |
bext ci gen#
Emits a CI workflow for a supported platform: GitHub Actions (default), GitLab CI, CircleCI, Cloudflare, Fly, and Render.
bext ci gen --platform github > .github/workflows/bext.yml
# with a deploy target
bext ci gen --platform github --deploy-target self-hosted --out .github/workflows/bext.yml
# no deploy step, just build + test
bext ci gen --platform github --deploy-target none
The default GitHub workflow has:
- A
buildjob that checks out the repo, installs the Rust toolchain, installs bext via the curl-installer, and runsbext check,bext build, andbext test - A
deployjob that runs only on pushes tomain/master, SSHes intoDEPLOY_HOSTusing secrets, and runsbext upgrade+systemctl reload bext
Flags#
| Flag | Default | Purpose |
|---|---|---|
--dir <path> |
. |
Project root to read |
--platform <name> |
github |
CI platform: github, gitlab, circle, cloudflare, fly, render |
--deploy-target <kind> |
self-hosted |
self-hosted or none |
--out <path> |
stdout | Write workflow to this path |
bext systemd gen#
Emits a bext.service unit file (and optionally a companion bext.socket unit when --socket-activation is passed).
bext systemd gen > bext.service
# with socket activation
bext systemd gen --socket-activation > units.txt
# custom user + working directory
bext systemd gen --user www-data --working-dir /srv/bext
The output uses a sentinel ### FILE: bext.service header to separate each unit in the stream. Split on that header if you piped both units to a single file.
The default unit uses Type=notify, sets hardening flags (NoNewPrivileges, ProtectSystem=strict, PrivateTmp, PrivateDevices), and restarts on failure with a 2-second back-off.
With --socket-activation, the service gets Requires=bext.socket + Sockets=bext.socket and the socket unit listens on 0.0.0.0:80 + 0.0.0.0:443 with ReusePort=true. This is the shape the zero-downtime binary upgrade path expects — see Zero-Downtime Upgrades.
Flags#
| Flag | Default | Purpose |
|---|---|---|
--dir <path> |
. |
Project root to read |
--socket-activation |
off | Also emit bext.socket and wire it into the service |
--user <user> |
bext |
Unix user the service runs as |
--working-dir <path> |
/opt/bext |
WorkingDirectory for the service |
--out <path> |
stdout | Write units to this path |
bext export docker-compose#
The reverse of bext import: walk your bext.config.toml and declared plugins, then emit a docker-compose.yaml suitable for local development.
bext export docker-compose > docker-compose.yaml
The bext service is always present. Backend services are auto-added from plugin naming conventions:
| Plugin name contains | Service added | Env var wired |
|---|---|---|
-pg, -postgres, -pgsql |
postgres:16-alpine |
DATABASE_URL |
-redis |
redis:7-alpine |
REDIS_URL |
-meili, meilisearch |
getmeili/meilisearch:v1.7 |
MEILI_URL |
So a project that declares @bext/session-redis and @bext/locking-pg gets both redis and postgres services with the matching env vars wired into the bext service. Volumes and a shared bext-net network are emitted for every backend.
A richer version that reads requires_capabilities from plugin manifests is a wave-2 item; for v1 the naming convention is authoritative.
Flags#
| Flag | Default | Purpose |
|---|---|---|
--dir <path> |
. |
Project root to read |
--out <path> |
stdout | Write compose to this path |
--bext-image <tag> |
ghcr.io/bextdev/bext:latest |
Override the image used by the bext service |
Regeneration#
All four generators are pure functions of the project state — running them twice produces identical output. Re-run them after bumping bext or adding plugins and commit the diff; that's the intended review loop.
They also have no side effects beyond writing the target file. They never modify bext.config.toml, never fetch anything from the network, and never touch the plugin cache. This makes them safe to run inside CI pipelines, pre-commit hooks, or scripts.
Framework detection#
The generators recognise:
- Astro —
astro.config.* - SvelteKit —
svelte.config.* - SolidStart —
solid.config.* - Qwik —
qwik.config.* - Rails —
Gemfilewith agem 'rails'line - Django —
manage.py - Laravel —
composer.jsonwith"laravel/framework" - Phoenix —
mix.exswith:phoenix - PRISM-shaped TSX project — legacy heuristic:
package.jsonwith a"react"dependency and no Next.js marker. React may be present only for a client island; native PRISM rendering does not call React.
Detection is a thin file-marker scan, not a full project parse. If you hit a project the generators misidentify, please open an issue.
All four generators are pure functions — running them twice produces identical output. Re-run them after bumping bext or adding plugins and commit the diff. This makes them safe inside pre-commit hooks or CI without worrying about side effects.
Related#
- Zero-Downtime Upgrades — the systemd unit generated by
bext systemd gen --socket-activationis the recommended shape for the upgrade signal flow - Scaling —
bext export docker-composewires Redis and Postgres from plugin names; see the scaling page for Redis L2 cache config - Configuration: Docker — Docker-specific runtime configuration that complements the generated Dockerfile
- Configuration: bext.config.toml — the config file the generators read to tailor their output
- Frameworks: Overview — framework detection markers match the frameworks listed here