Running vibld yourself
Deploying your own copy
Checked against the product on
The repository’s own READMEs carry the exact commands and are the version to follow. The project’s own Self-host check follows this page on every run: it deploys a separately named copy, confirms the copy comes up and refuses a signed-out caller, signs in with the owner’s password, builds one site with a real model and opens its preview, then removes the copy. It does the same under Docker. Nobody outside the project has followed the page end to end yet.
The deploy workflows in the repository run only in the maintainers’ own repository, since a copy has none of their secrets. Deploy from your own machine with wrangler, or write your own workflow.
Your copy’s configuration
The configuration in the repository deploys vibld’s own service: its Worker names, its D1 database and R2 bucket, its Workflow, its rate-limit namespaces, its domains and its sign-in. Deployed unchanged into another account it fails. Deployed into an account that also runs vibld, it would replace vibld’s Workers and write into its database.
Write your own instead, from a settings file:
{
"prefix": "acme",
"d1DatabaseId": "<the id wrangler d1 create printed>",
"auth": "owner",
"provider": "deepseek",
"rateLimitNamespaceBase": 5001
}node scripts/self-host.mjs settings.json writes wrangler.self-host.jsonc beside each Worker’s configuration (git ignores it) with every one of those names derived from your prefix: Workers acme-web, acme-preview and acme-publish, database and bucket acme-control-plane, Workflow acme-generation, the service bindings between them, and the rate-limit namespaces. It refuses to write a file that still names anything of vibld’s. Without a builderDomain and a previewDomain the Workers are served at workers.dev, and publishing stays off: the builder is left unbound from the publish Worker, which would otherwise name sites under vibld’s own domain. Without a previewDomain the Preview pane also runs each project in the viewer’s own browser instead of in a sandbox, while the sandbox still checks each build. Rate-limit namespace ids are shared across a Cloudflare account, so rateLimitNamespaceBase is required, and the script refuses vibld’s own. The optional settings are listed at the top of the script.
auth is how people sign in: owner (one password), access (Cloudflare Access, with accessTeamDomain and accessAud) or clerk (with clerkFrontendApiUrl). See Self-hosting for which to pick.
Pass -c wrangler.self-host.jsonc to every wrangler command, and apply the migrations with wrangler d1 migrations apply acme-control-plane --remote -c wrangler.self-host.jsonc from apps/web: the builder’s own deploy and migrate scripts name vibld’s database.
Still yours to set: the alert addresses and the referral origin, if you use them, which default to vibld’s. See Settings and secrets.
The order that works
- Create the stored state first. A D1 database and an R2 bucket, both named in each Worker’s configuration. The Durable Object and the Workflow need no provisioning.
- Apply the migrations before deploying the Worker, not after. A Worker whose code expects a table that does not exist yet takes the whole thing down: two migrations once sat unapplied here for two days, and every generation failed. The builder’s
deployscript applies them first for you. - Deploy the publish Worker, then the sandbox, which binds it. The sandbox’s container image is built at deploy time, so this needs Docker running, and it needs Workers Paid. Set the three secrets:
PREVIEW_INTERNAL_SECRETon the builder and the sandbox,PUBLISH_INTERNAL_SECRETon the builder and the publish Worker, andPREVIEW_SHARE_SECRETon the sandbox alone. Three different values. - Deploy the builder last: its service bindings name the other two Workers, and Cloudflare refuses the deploy until both exist. Build it after running the script, which writes the sign-in it is built for into
apps/web/.env.production.local. Give it a provider key as a secret, and for the sign-in you chose:VIBLD_OWNER_PASSWORDforowner; foraccess, put the Access application in front of the builder’s address and setVIBLD_PLATFORM_ADMINSto the email you sign in to Access with, or nobody gets past the invite gate to invite anybody else; forclerk, build withVITE_CLERK_PUBLISHABLE_KEYand setVIBLD_PLATFORM_ADMINS.
Migrations are idempotent. Make applying them a step in your deploy.
The domain problem, with Clerk
Only a copy signed in with Clerk has this problem. A live Clerk instance is bound to a domain, and Clerk’s Frontend API refuses any request whose origin is not that domain or a subdomain of it. A deployment reachable only at a workers.dev URL will therefore not load Clerk at all, which looks like a broken sign-in rather than a configuration mismatch.
Two ways through it:
- Use a development Clerk instance while you are standing things up, and switch when you have a domain.
- Or put the Worker on a custom domain from the start. A custom domain route provisions its own DNS record on deploy, but only if the deploying token can manage DNS on that zone. A token scoped to Workers alone is not enough, and the deploy error does not say so clearly.
Adding a custom domain also disables that Worker’s workers.dev address entirely, so the old URL starts returning 404 rather than continuing to work alongside the new one.
The preview domain needs the same DNS permission, for the sandbox Worker’s wildcard route.
What to check once it is up
- Sign in. If the sign-in page never renders, it is the domain problem above, not your Clerk keys.
- Run one generation. If it is refused because the deployment is closed, you are not getting past the gate: your address is not in
VIBLD_PLATFORM_ADMINS, or your Clerk session token does not carry a verifiedemail, andVIBLD_ACCESS_MODEis notopen. If it says generation is not configured, the builder is missing a provider key,CLERK_FRONTEND_API_URL, or one of its D1, R2, Durable Object or Workflow bindings. - Run the live preview. With a
previewDomainit starts a sandbox; if that reports itself unavailable rather than failing, the service binding or the shared secret is missing on one of the two sides, or the sandbox has noPREVIEW_HOSTNAME. Without one it bundles in your browser.
Each of those refuses with a reason rather than half-working.
Where the exact commands live
In the repository, beside the code they deploy: apps/web/README.md for the builder, apps/preview/README.md for the sandbox and apps/publish/README.md for publishing.