Skip to main content

Deploy to Cloudflare Workers

On Cloudflare the whole site runs as one Worker, with no server for you to look after. This page takes you from the download to a live site. It is for whoever has a Cloudflare account and is comfortable with a terminal. The free plan is enough for a choir-sized site.

You need:

  • a Cloudflare account, and a domain whose DNS zone is on that account if you want your own address;
  • Node.js 22.13 or newer on your own computer, to run npx wrangler (Wrangler is Cloudflare's command-line tool; npx fetches it for you, so there is nothing to install);
  • the download and a licence key.
warning

Work from the download, unpacked. Never run npm run deploy:cloudflare or any build command: those need the product's source code, which the download does not contain. The download's website is already built, and npx wrangler deploy uploads it as it is.

1. Unpack​

mkdir choir-worker
tar -xzf choirmaster-cms-1.6.12.tar.gz -C choir-worker
cd choir-worker

2. Log in to Cloudflare​

npx wrangler login

A browser window opens to let Wrangler use your account. To find your account's id for later:

npx wrangler whoami

3. Create the resources​

npx wrangler d1 create choir-db
npx wrangler r2 bucket create choir-public
npx wrangler r2 bucket create choir-private
npx wrangler kv namespace create SESSIONS
ResourceWhat it is for
D1 database choir-dbAll the site's records.
R2 bucket choir-publicPublic files: gallery, posters, uploaded images.
R2 bucket choir-privateMember files and rehearsal tracks. Never give this one a public address.
KV namespace SESSIONSLogin sessions. Optional: skip it and use the database for sessions (see below).

The d1 create command prints a database_id, and kv namespace create prints an id. Copy both. If Cloudflare says R2 is not enabled, enable it for your account in the Cloudflare dashboard first.

4. Fill in wrangler.toml​

cp wrangler.toml.example wrangler.toml

Edit wrangler.toml:

  • uncomment account_id and set your account id;
  • set database_id under [[d1_databases]] to the one printed in step 3;
  • set id under [[kv_namespaces]] to the KV id;
  • uncomment routes and set your domain: routes = [{ pattern = "choir.example.org", custom_domain = true }];
  • in [vars], set SITE_URL to https://choir.example.org, and EMAIL_PROVIDER and EMAIL_FROM for your email service.

Leave workers_dev = false and preview_urls = false alone: without them a deploy also publishes the site, backed by your real data, at a workers.dev address. Leave the [assets] section as it is too: its binding = "ASSETS" and run_worker_first lines are what put your choir's name into the page for link previews and search engines, and they only work as a pair. See Your choir's name in the page.

If you skipped KV, delete the [[kv_namespaces]] block and add SESSION_STORE = "database" to [vars].

Every line is explained in wrangler.toml explained, and the custom domain in Cloudflare: your domain and public files. smtp is the one email provider that does not work on Workers.

5. Create the database tables​

The schema is never applied automatically on Cloudflare. Apply it yourself:

npx wrangler d1 execute choir-db --remote --file=./server/db/schema/sqlite.sql

It only creates what is missing, so it is safe to run again. You will run it again for each update.

6. Set the secrets​

Secrets are kept by Cloudflare, not in your files:

npx wrangler secret put ADMIN_USERNAME
npx wrangler secret put ADMIN_PASSWORD
npx wrangler secret put LICENSE_KEY

Wrangler asks you to type each value. Then the secrets for your email service, for example Amazon SES:

npx wrangler secret put AWS_ACCESS_KEY_ID
npx wrangler secret put AWS_SECRET_ACCESS_KEY

or RESEND_API_KEY, SENDGRID_API_KEY and the like: see Email: what the site sends and how to choose a provider.

SecretWhen
ADMIN_USERNAME, ADMIN_PASSWORDAlways, unless you use ADMIN_AUTH = "table" and add admins another way.
LICENSE_KEYTo be told about new versions.
Your email service's keysAlways, with a real email service.
CAPTCHA_SECRET_KEYIf CAPTCHA_PROVIDER is set.
ANTHROPIC_API_KEYOnly for the optional writing assistant in the guided setup.
S3_ACCESS_KEY_ID, S3_SECRET_ACCESS_KEYOnly if you use STORAGE_PROVIDER = "s3".

If Wrangler says there is no Worker of that name yet and asks whether to create one, say yes. The deploy in the next step then fills it in.

7. Deploy​

npx wrangler deploy

Wrangler bundles the server, uploads the built website in dist/ as the Worker's static assets, and attaches your domain. With a custom_domain route, Cloudflare creates the DNS record for you.

8. Log in​

Open https://choir.example.org/admin/login and log in with the username and password you set as secrets. The first time, the site takes you through the guided setup: see Log in to the admin panel and Set up your site step by step. To give each admin their own login instead, see Add the first admin, and get back in when locked out.

Then work through After installing: first login and go-live checklist.

What is different on Workers​

On Cloudflare
Database schemaYou apply it with wrangler d1 execute, now and after each update.
UpdatesAlways by hand. The admin panel shows the steps. See Update by hand.
EmailEverything except SMTP.
Secretswrangler secret put and [vars] only. NAME_FILE settings and secrets managers do not apply.
DatabaseD1 only.
FilesR2, or any S3-compatible store with STORAGE_PROVIDER = "s3". Not local disk.
LimitsSee Cloudflare: limits and logs.
Your wrangler.tomlYours. An update never overwrites it, because the download only has wrangler.toml.example. When a release needs something new in it, its release notes say so: see Update by hand.
PagesEvery page view runs the Worker and reads the site's settings from D1 once, so that the page carries your choir's name. See Your choir's name in the page.

If something goes wrong​

What you seeWhat to do
PUBLIC_BUCKET and PRIVATE_BUCKET R2 bindings are required (or set STORAGE_PROVIDER=s3)The R2 blocks are missing or misnamed in wrangler.toml. The binding names must be exactly PUBLIC_BUCKET and PRIVATE_BUCKET.
Errors mentioning a missing tableStep 5 was not run, or was run without --remote.
You cannot log inThe ADMIN_USERNAME and ADMIN_PASSWORD secrets are not set. Check with npx wrangler secret list.
[config] linesSee Cloudflare: limits and logs for how to read the log.
Every page shows {"success":false,"error":"Not Found"}The [assets] section has the new run_worker_first but no binding = "ASSETS". Copy the section from wrangler.toml.example and deploy again.
A shared link to the site is titled "Choir"The [assets] section is an older one. See Your choir's name in the page.
The pages appear but are empty of content, and nobody can log inThe Worker could not start, so pages are served as built and the API is down. npx wrangler tail says why.