-
Notifications
You must be signed in to change notification settings - Fork 0
Bare VM
A fresh Ubuntu box with bun and git runs a scaffolded app's whole gate. No Docker daemon, no
Postgres, no Redis, no NATS, no object store, no .env editing. Bun runs all four commands below;
git is what commits the scaffold, and --no-git is the form that skips it.
bunx create-ultimate demo --no-git
cd demo
bin/setup
bin/check| Command | What it needs from the box |
|---|---|
bunx create-ultimate demo --no-git |
bun, and a network the npm registry answers on. --no-git skips git init, which is the form an automated provisioner uses; without it the scaffold is committed, and that is git's only job here |
cd demo |
— |
bin/setup |
bun. Six steps: bun install, an .env.development.local touch, x db gen "initial" when packages/db/migrations holds no .sql, x db migrate, x db seed, x manifest
|
bin/check |
bun. x build --target static, then x verify — the build first, because budgets weighs .x/build-stats.json and the build is that file's only writer |
x new installs nothing, so bin/setup is not optional and cd demo && bin/check stops on
X_BUILD_FAILED naming bun install (Installation).
The scaffold's .env.development ships committed non-secret defaults, and it leaves DATABASE_URL
empty. An empty DATABASE_URL is not a hole to fill — it is the switch that selects the embedded
database, in resolveServices
(packages/cli/src/dev-services.ts),
which is the one place the three service bindings are decided:
| Binding | Unset env | Resolves to | Set it to |
|---|---|---|---|
| db | DATABASE_URL |
pglite://<app>/.x/pgdata — Postgres compiled to WASM, running inside this process
|
a postgres: URL |
| events | NATS_URL |
inproc://events — in-process fanout |
a NATS URL |
| storage | S3_ENDPOINT |
file://<app>/.x/storage |
an S3 endpoint |
The database is
packages/db/src/pglite.ts:
a real Postgres, WASM-compiled, opened on a directory under .x/, so x db migrate and x db seed
write to the same engine the app then reads — no container to wait for, no port to publish, and a
restart keeps the data. The module is an optional peer resolved at first query, never at import,
so an image that only ever talks to a managed Postgres does not carry the WASM it will never load.
@electric-sql/pglite is a devDependencies entry of the generated app, which is what puts it on
the box during step 1 of bin/setup.
Its absence is a coded refusal, never a silent skip: X_DB_UNAVAILABLE, whose fix: is
bun add @electric-sql/pglite, or set DATABASE_URL to a Postgres server and re-run — a command the
reader runs, on a box that has just told them what is wrong.
x doctor says it earlier than the first query does. Its external-database probe answers nothing
where DATABASE_URL is unset, and that silence is exactly this box, so it asks the embedded
question too: does the peer resolve from the app root — a resolve, never an import, since
loading it boots the WASM and takes the single-writer lock the next command needs. Red only where
DATABASE_URL is unset and the peer is unresolvable (CLI reference).
bin/check's live, job and e2e steps need no service of their own in a generated app: the
queue is Postgres (SELECT … FOR UPDATE SKIP LOCKED), the fanout is in-process, and both resolve
through the table above.
No logical replication. PGlite has no walsender, so there is no write-ahead log to decode and no
slot to take. A --live query still works: x dev installs the in-process bridge instead — the
same row observer the framework's own live tests run on — and the boot line says which feed you
got, live=in-process under the embedded database and live=replication under a real one
(packages/cli/src/dev-live-feed.ts). Its honest bound is that a write made by another process
is invisible to it, which holds by construction under x dev, where every role is this one process
(Realtime).
Reach for the dev compose file only when you want parity against real Postgres, NATS and MinIO (Deployment). Nothing on this page uses it.
On a WSL2 developer box — not a bare VM, and not a CI runner — on a Bun the CLI's own floor
accepts (x doctor), against real x new scaffolds, first pass, nothing waived and no fix-follow,
warm Bun cache, As of 2026-09-11:
| Measure | Default scaffold | --no-example |
|---|---|---|
| files written | 151 | 123 |
bin/setup |
6,802ms | 5,135ms |
bin/check |
5,389ms | 3,909ms |
the first bin/check's verdict |
green, 20 of 20 steps, budgets included |
green, 20 of 20 steps, budgets included |
The cache is the variable to watch, not the box: a cold first install takes the bun install
inside bin/setup to 12.0s on the same machine.
And on a free ubuntu-latest runner, which is the measurement that speaks for a provisioned
box — ci.yml's scaffold-smoke job, running these same two scripts on a scaffold written outside
the checkout, As of 2026-09-12:
| Measure | Default scaffold | --no-example |
|---|---|---|
| files written | 151 | 123 |
bin/setup |
6,665ms | 5,994ms |
bin/check |
7,279ms | 4,886ms |
the first bin/check's verdict |
green, 20 of 20 steps pass | green, 20 of 20 steps pass |
budgets is green on that first pass on every one of those runs, because bin/check builds before
it verifies — nothing is waived to get there, and no printed fix: is followed before the verdict
is taken. The job prints the table itself, one row per command and one per gate step, so the run is
its own measurement rather than a number this page asserts: read it from the job's log when you want
today's.
Ultimate — v20.1.1 As of 2026-09. Stable API, semver from here. MIT licensed. What npm serves is npm view @ultimat3/core version, never this line.
This footer is the only page that stamps a version. It renders under every wiki page, so one release bumps one line; a stamp on a second page is 46 hand-copies of one fact, and every one of them goes stale on the next tag.
Repository · Issues · Changelog · llms.txt
Edits to these pages are synced from wiki/ in the repository — change the file there, not the wiki, or the next sync overwrites it.
Start
Tutorials
- 1 · First app
- 2 · First feature
- 3 · Auth and admin
- 4 · Jobs and realtime
- 5 · Deploy free
- 6 · Growing up
Primitives
- The eight primitives
- Building your own base
- Actions
- Entities and migrations
- Policies and authz
- Queries and live queries
- Jobs and workflows
- Scheduled tasks
- Routes and render modes
Capabilities
- Realtime
- Caching and invalidation
- Batching and preloading
- N+1 detection
- PWA and offline
- MCP and AI
- Agents
- Admin dashboard
- Scraping
Cross-cutting
- I18n
- Theming
- UI components
- Interface rules
- Timezones and dates
- Money
- Resource management
- Migrations and backfills
- Testing
Reference