Go live
A ZeroDirs site is static files plus a small Worker. Going live is one command for the
mechanical part and a short list for the parts that need your account. When this page is done
you have a site on your domain, a database behind /submit/ and /admin/, and a printed list of
what is still switched off.
Before you start
Section titled “Before you start”Three things, each with a command that proves it:
| Check | Command | You should see |
|---|---|---|
| wrangler is signed in to the right account | pnpm exec wrangler whoami |
your account name and id. If it says you are not logged in: pnpm exec wrangler login opens a browser |
site.url is your real domain |
pnpm check:config |
site.config.ts OK — <your site name> (https://your-domain.com). https://, no trailing slash |
| the domain is on Cloudflare | Cloudflare dashboard → your account → the zone is listed | if it is not, add it now, or deploy to *.workers.dev first with --workers-dev and come back |
site.url matters more than it looks: canonicals, the sitemap, the feeds and the OG images bake
it in at build time. pnpm cf:setup refuses to run while it still says demo.zerodirs.com or
example.com.
-
See what will happen. Nothing changes yet.
Terminal pnpm cf:setup --dry-runThe plan names the Worker, the route, the D1 database and the R2 bucket it is about to create or reuse, then stops with
Nothing changed.:Plan:Worker name acme-directory (renamed from the template)Route acme.com (custom domain) (changes wrangler.jsonc)D1 database acme-directory (create)R2 bucket acme-directory-media (create if missing)Then migrations → build → deploy → ADMIN_SECRET / TOKEN_SECRETNothing changed. -
Run it.
Terminal pnpm cf:setupIt asks once, then does every step in order, printing a
✔per step and skipping what is already done, so you can run it again later. In order: it renames the Worker after your site (only while it still carries the template’s name), pointswrangler.jsoncatsite.urlas a custom domain, creates the D1 database and writes its id intowrangler.jsonc, creates the R2 bucket, leaves thesend_emailbinding out when your account cannot send mail yet, regeneratesworker-configuration.d.ts, applies the migrations to the production database, builds, deploys, and setsADMIN_SECRETandTOKEN_SECRETon the Worker. -
Store the admin password.
If
.dev.varsstill held the placeholder secrets, they are replaced by generated ones, written back into.dev.varsso local and production agree, and the/admin/password is printed once:Your /admin/ password (generated, also saved to .dev.vars — store it somewhere safe):<the value>Put it in your password manager now. It is also
ADMIN_SECRETin.dev.vars, which is gitignored and stays on this machine. -
Read “What is left”.
The command ends with the URL of the site and a list filtered to your configuration:
Done. The site is at https://acme.comWhat is left:• Approved submissions live in D1; the site you just deployed was built from src/content/listings. …• providers.rebuild is "deploy-hook": connect the repository to Workers Builds, create a Deploy Hook …• Paid plans are on: pnpm cf:secrets STRIPE_SECRET_KEY STRIPE_WEBHOOK_SECRET, …• Open /admin/login/ with the ADMIN_SECRET from .dev.vars, and /admin/ will list anything this configuration still needs.Run `pnpm cf:setup` again any time; every step above skips what is already done.Each line is one of the pages under What is left below.
Without a domain yet
Section titled “Without a domain yet”pnpm cf:setup --workers-devThe site goes to <worker-name>.<your-subdomain>.workers.dev and wrangler.jsonc keeps no route.
When the domain is on Cloudflare, set site.url to it and run pnpm cf:setup again without the
flag: it rewrites the route and redeploys.
If it stops
Section titled “If it stops”The message says why. The three usual reasons:
| Message | Fix |
|---|---|
site.url is still … |
Set site.url in site.config.ts to your domain, then run it again |
| R2 could not create the bucket | R2 needs a one-time sign-up in the dashboard (R2 → Get started). Do that, run it again |
| the domain does not answer after a few minutes | The zone is not on this account. Add the domain to Cloudflare, then pnpm cf:setup again |
Verify
Section titled “Verify”| Command or URL | You should see | |
|---|---|---|
| The site | https://<your domain>/ |
your home page, your name in the footer |
| The admin | https://<your domain>/admin/login/ |
the login form accepts the password from step 3 |
| What is still needed | https://<your domain>/admin/ |
the configuration report at the top lists only what you deliberately left for later |
| What the Worker holds | pnpm cf:secrets |
a list of the secrets the deployed Worker has and which ones your site.config.ts still needs |
What is left
Section titled “What is left”cf:setup deliberately stops at the edge of your account: it signs nobody in, creates no API
tokens and touches no dashboard. These are the steps it hands to you, in the order they pay off.
| Step | Why | Page |
|---|---|---|
| Build the site from the database | Approved submissions live in D1, but the site you just deployed was built from src/content/listings/ |
Listings from D1 |
| Deploy on every push, and after every approval | A Deploy Hook lets an approval trigger a build | Deploy on git push, then Publishing on approval |
| Send mail | Receipts, approvals and your own notifications wait in the outbox until a provider can send | |
| Take money | Paid plans ship on and need two Stripe secrets and a webhook | Plans and payments |
| Let submitters sign in with Google | Accounts work with emailed links already; Google is optional | Submitter accounts |
Every secret on that list goes in with pnpm cf:secrets, which prompts for one key at a time and
never prints or stores a value. Environment and secrets is the full map.
By hand
Section titled “By hand”The same result without the script, for a copy where you would rather see each command:
pnpm exec wrangler loginpnpm exec wrangler d1 create acme-directory # paste the id into wrangler.jsonc → d1_databases[0].database_idpnpm exec wrangler r2 bucket create acme-directory-mediapnpm cf:typegen # worker-configuration.d.ts follows the bindingspnpm db:migrate:remotepnpm check:configpnpm deploy # = astro build && wrangler deploypnpm cf:secrets ADMIN_SECRET TOKEN_SECRETSet name and routes in wrangler.jsonc yourself first. cf:setup is idempotent; running it
after a manual setup only fills what is missing.
To publish the pages and nothing else — no database, no submissions — the deploy is:
pnpm check:configpnpm deploy/submit/, /admin/ and the checkout routes answer 404 until a database exists, and the rest of
the site is unaffected. This is also the whole deploy of the free edition.
Directoryscripts/
- cloudflare-setup.ts the command itself, step by step, with
--dry-run,--workers-devand--yes - dev-setup.ts what
pnpm devruns first:.envand.dev.varsfrom their examples, local migrations
- cloudflare-setup.ts the command itself, step by step, with
- wrangler.jsonc
name,routes, the D1 id and the R2 bucket are written here - worker-configuration.d.ts regenerated from the bindings;
pnpm checktype-errors without it - .dev.vars gitignored;
ADMIN_SECRETandTOKEN_SECRETend up here as well as on the Worker