Stand it up in an account with your name on it.
Six commands create the services, two put the Worker up, and a one-time screen makes you the owner. Nothing in the sequence talks to us, and no part of it needs our permission. Below is the whole thing in order, plus the four choices that decide whether your speakers can sign in.
Four things get created, and you own them.
A Cloudflare account on the paid Workers plan, a hostname whose DNS Cloudflare already answers for, and Node. That is the entire prerequisite list.
| What | What lives in it |
|---|---|
| A database | Every proposal, score, decision, session, speaker and sponsor row you will ever hold. It is in your account, and a CSV export of it needs nobody's permission. |
| A key-value namespace | The published schedule snapshot your website reads, and the replay cache that turns a retried write into the same answer instead of a second booking. |
| A bucket | Headshots, slide decks, signed agreements. An upload carries a short-lived signature and streams straight into the bucket instead of being buffered, so a 200 MB deck is not a timed-out request. |
| Three queues | One carries what happened. One carries what goes out: mail, webhooks, spreadsheet pushes. The third keeps whatever failed six attempts, so a bad afternoon is a queue you can read rather than mail nobody can account for. |
Create them once. The names are not decorative: the configuration in the repository refers to them, so a bucket called something else is a deploy that fails.
npx wrangler d1 create podium
npx wrangler kv namespace create CACHE
npx wrangler r2 bucket create podium-assets
npx wrangler queues create podium-events
npx wrangler queues create podium-delivery
npx wrangler queues create podium-dlqThe first two print an id each. Keep both; the next section wants them. Two durable objects and a quarter-hourly schedule come with the deploy itself, so there is nothing to create for the reminders, the sweeps or the deadline expiries.
Set three variables, then run two commands.
There are no account ids anywhere in the repository you cloned. Production configuration is generated at deploy time from what you export, which is why your first deploy cannot land in somebody else's database.
Put the three values somewhere your shell can read
The hostname you will serve on, and the two ids the create commands printed. In a file, not your shell history. The deploy script reads them from the environment and writes a config that is never committed.
PODIUM_HOSTNAME=cfp.yourconference.com CF_D1_DATABASE_ID=… CF_KV_CACHE_ID=…Set the one secret the application cannot start without
It signs the unsubscribe links, so a speaker can leave a mailing list without signing in. Generating or checking one of those links throws without it.
npm run deploy:config npx wrangler secret put UNSUBSCRIBE_SECRET -c wrangler.production.jsoncMigrate, then ship
In that order, always. Migrations are append-only, so the new schema fits the code already running. The reverse order gives you new code reading columns that do not exist yet.
npm run deploy:migrate npm run deploy
The hostname list replaces, it does not merge. Deploying publishes the hostnames you named as the Worker's complete set. A hostname currently served by a different Worker is taken from it, silently. Name every hostname you want served, every time.
Open the setup screen before anyone else does.
Nothing seeds the organization. A freshly migrated deployment has no accounts in it at all, and every path sends you to the same place until one exists.
The screen asks for the conference-running body, a default timezone, a contact address, and your own name, email and password. Submitting it creates the organization, your person record and your ownership of it as one write, then signs you in.
Then it is gone. Every method on that path is refused the instant an organization exists, checked against the database on each request rather than cached. It is a screen that runs with nobody signed in and makes an administrator.
There is a lock under it as well. The first row it claims can hold exactly one value, so two people racing to set up the same empty deployment cannot both win.
Import your speakers after that, not before. One CSV brings people and their history across from the tool you are leaving.
Four choices you make once and live with.
Two of them have already gone wrong on a real deployment of this software, on dates you can go and read. They are here so they do not go wrong on yours.
Sign-ins die on the free plan
A correctly sized password hash costs far more than the ten milliseconds of processor time a free Worker gets per request, so attempts are killed mid-hash and sign-in fails intermittently. This deployment ran that way for months, on a hash deliberately weakened to fit, and moving to the paid plan is what let that be undone. Start on Workers Paid.
Installing an email provider turns password sign-in off
Password sign-in is on wherever no email provider is active, and off the moment one is. That is deliberate: a deployment that can neither send mail nor take a password cannot exist. It also means the afternoon you connect Resend, everyone signing in with a password stops. Tick the box on the settings screen first, or send everybody an invitation link.
The first-pass review is off, and stays off
Nothing switches it on for you. Switched on, its scores sit beside your reviewers' rather than inside them, never count toward quorum, and are always overridable. The evaluator in the box calls no external model at all — it scores structure and says so.
Moving off D1 costs you one file
The database sits behind 388 lines, and no query outside them knows it is talking to D1. Postgres behind Hyperdrive means rewriting that file and nothing above it. The rules do not move, and neither does anything that reads them.

Five checks, in the order they fail.
The sign-in page answers
On a deployment nobody has set up, it redirects to the setup screen, which is the correct answer and not a fault. After setup it renders. This is the same check the build runs against our own deploy.
Create an event and read what it made for you
A new event arrives with days, rooms, formats, review rubrics, about a dozen speaker onboarding tasks and a draft call. The common mistake is building a second set of tracks beside the ones already there.
Send yourself something and look in the outbox
With no provider installed, every message is composed, rendered, recorded and never sent. That is a legitimate state to run in, and it is how you rehearse an announcement to four hundred speakers.
Publish a schedule and embed it
Publishing writes an immutable snapshot. Your marketing site reads that, a CDN caches it, and rolling back is pointing at the previous one. Nothing public touches the live programme tables.
Mint a read-only key and try to read something with it
A key reaches exactly what its scopes name. Give one only the schedule and confirm for yourself that it cannot see a review or a speaker's email address.
Nobody will do the upgrades. Pulling a release and redeploying is a five-minute job, and it is your job. There is no status page to check and no vendor to email at nine on a Monday. If that is a dealbreaker, it is better to find out today than in April.
What this costs, itemised, and what it costs that is not money →
Store the key, then name the key.
Email, chat and spreadsheet sync all install the same way, and the field that trips people up is the same field every time.
The install form asks for a secret reference, not a secret. Put the provider's key in your Worker's secrets under a name you choose, then type that name into the form. A key pasted into that field is a leak, not a configuration, and the application will not find it there anyway.
npx wrangler secret put RESEND_API_KEY -c wrangler.production.jsoncThen install Resend and put RESEND_API_KEY in the reference field. Health check it from the same screen; a misconfigured provider says what went wrong there rather than in a bounced acceptance three weeks later.
Then go back to the settings screen and decide about passwords, because you have just changed that answer.
One fixed line, and the rest is metered.
The software is free and the licence cannot be revoked. Workers Paid starts at five dollars a month on Cloudflare's own pricing page. That is the only source that binds; it is not our price to promise. Storage, database reads and queue messages are metered and small next to the included allowances, but your event is not our event.
There is no monthly total on this page. Every one on a page like this is a guess you find out was wrong on your own invoice.
