Deploy
Your laptop is not a server — here is how a folder on your machine becomes a URL a stranger can open, and the one step you still have to click.
put a working site on the public internet with a custom domain, HTTPS, production secrets and a rollback you have already practised
Right now your sign-up page exists on exactly one computer on Earth: yours. Close the lid and it is gone. Deploying means copying it onto machines that never sleep and giving it a name people can type. On this stack that is four commands — plus one thing you cannot do from the terminal.
Working on your machine is not done. Deployed is done. This page gets you from the first sentence to the second without losing a secret or a weekend.
Hosting is someone else’s computer that never turns off
A host is a machine with a public address that stays on. You hand it your files; it answers when a browser asks. That is the whole idea.
What changed is the delivery. Modern hosts watch your repository and deploy on every git push: pull the code, build it, publish it in a minute or two, with a preview URL for every branch. Nothing is uploaded by hand, no web server is configured, and no bill arrives until you are genuinely popular.
Static or server-rendered decides almost everything else
Static means the site is built once, into a folder of plain HTML, CSS and JavaScript, and that folder is copied to machines around the world. Nothing runs per visitor. It is the cheapest, fastest and hardest-to-break option. The site you are reading is static: npm run build turns the source into dist/, and dist/ is the website.
Server-rendered means a program runs for each request: it checks who you are, reads the database, calls a paid API. That code lives on the server, out of the user’s sight, which is the only place secrets can live. It comes in two shapes — one long-running server, or small serverless functions (Cloudflare Workers, Vercel Functions) that wake per request and go back to sleep.
The rule for a first project: keep as much static as you can and push the dynamic part into the smallest possible function. Free tiers are counted in function invocations and CPU time, not in static files — Cloudflare’s free Workers tier gives 100,000 requests a day and 10 ms of CPU per invocation, while Pages lists no bandwidth cap at all for static assets. Serving a static page costs you nothing. A function spends budget every time someone loads it.
Go deeperWhere a reserved seat actually gets stored
A sign-up form needs a database, and all three sensible free options have a catch worth knowing before you choose.
- Cloudflare D1 (SQLite): 5M rows read and 100k rows written per day, 5 GB total. Same account as your Pages project, no card.
- Neon (Postgres): 0.5 GB storage per project, 100 CU-hours per project per month, mandatory scale-to-zero after 5 minutes — which means the first query after a quiet hour is slow.
- Supabase (Postgres plus auth): 500 MB database, 50,000 monthly active users, 2 active projects — and the project is paused after one week of inactivity, which is exactly how long a demo sits between the workshop and the follow-up meeting.
Sources: Cloudflare, Neon, Supabase.
Five free tiers, and what each one does when you hit the wall
These are the real numbers as of September 2026. The column that matters is the last one.
| Host | Free tier | What happens when you exceed it |
|---|---|---|
| Cloudflare Pages | 500 builds/month, 1 concurrent build, 20-min build timeout, 20,000 files, 25 MiB per file, 100 custom domains per project. No bandwidth cap listed for static assets | You run out of builds, not bandwidth, and only one build runs at a time. Cloudflare’s paid Workers plan is $5/month |
| Vercel Hobby | 100 GB data transfer, 1M edge requests, 1M function invocations, 100 deployments/day, runtime logs kept 1 hour | Usually you wait 30 days. Hobby is non-commercial, personal use only; Pro is $20/user/month |
| Netlify Free | 300 credits/month, hard limit, no rollover, cannot buy more. A production deploy costs 15 credits; 1 GB of bandwidth costs 20 | At zero credits every project is paused and shows “Site not available” until the next cycle |
| Railway | One-time $5 trial credit for up to 30 days, then $1 of credit per month that does not roll over; 0.5 GB RAM, 1 vCPU, 0.5 GB volume | $1 a month is the entire budget and it does not accumulate. Hobby is $5/month with $5 of usage included |
| Render | Free web service spins down after 15 min idle (about a minute to wake), 750 instance-hours/month, no persistent disk. Free Postgres is 1 GB with no backups | The free Postgres expires 30 days after creation and is gone |
For a student in Kazakhstan the choice is nearly made for you: a public GitHub repo, Cloudflare Pages or Workers for the site, and D1, Neon or Supabase for data. None of those asks for a card. Netlify’s free tier is the trap — twenty deploys and the month is gone, and you will deploy twenty times in one evening. Firebase is the other one: Cloud Functions need the pay-as-you-go plan, and that plan needs a billing account with a payment method on it.
Free tiers are prototype infrastructure, and the fine print says so. A free Render web service spins down after 15 minutes of silence and needs about a minute to wake up. A free Render Postgres is deleted 30 days after you create it. A free Supabase project is paused after a week of inactivity. Netlify pauses every project you own the moment the 300 credits are gone.
Deployed means reachable. Production means someone is relying on it — and that needs a tier that will not switch itself off while you sleep. The upgrade runs from $5 a month on Cloudflare or Railway Hobby to $20–$25 for Vercel Pro or Supabase Pro, which is the cheapest part of the whole story. Pay it the day a real person depends on the thing, not before and not after.
A domain is a rented name, and DNS is the phone book
Your site already has an address the moment it deploys — something like qairu-event.pages.dev. A custom domain is a nicer name pointed at the same place.
DNS is the internet’s directory: it translates a human-readable name into the address of the machine that answers for it. When you attach a domain you add a record, and the one you will meet first is a CNAME — it says “this name means that server”. Your host then issues the SSL certificate that turns http:// into https://, automatically, for free.
The part that confuses everybody: DNS answers are cached worldwide, so a change takes anywhere from minutes to hours to reach everyone. If the site works for you and not for your friend, you are almost always looking at a cached old record, not a broken deploy. Wait before you debug.
Secrets belong on the host, never in the repo and never in the browser
An environment variable is a named value handed to your program when it starts — an API key, a database address, a setting like NODE_ENV=production. It lives outside the code so the same code can behave differently on your laptop and on the server.
There are exactly three places a secret can be, and only one of them is correct:
- In a
.envfile on your laptop. Fine, as long as.envis in.gitignorebefore your first commit. - In the host’s dashboard, as an environment variable your server code reads. This is production.
- In the frontend. Never. Anything the browser downloads is public — a visitor opens developer tools and reads it in seconds.
Moving from the first place to the second is where first deploys die. Your .env never leaves your laptop, so nothing in it exists on the host until you type it there. Before you deploy, open .env, go key by key, and add each one in the host’s dashboard. When a page works locally and errors in production, the usual cause is one variable that lives in only one of those two places.
Module 8 gave your agent a deny rule on .env in .claude/settings.json. Keep it, and keep its limits in mind: it blocks Claude’s file tools and the shell readers it recognises, not a script that opens the file itself. It is a second layer, not a lock (Claude Code permissions docs).
Every branch gets its own URL, so break things where nobody is watching
A preview deploy is a full copy of your site built from one branch, published at its own address, with nothing pointing at it but the link. You send that link instead of a screenshot. On Netlify, deploy previews and branch deploys are not metered at all — it is only production deploys that cost credits, which makes previews the cheapest place to be wrong.
Previews also catch the classic “it works on my machine” failure: your host builds on Linux, and Linux cares about capital letters in filenames while Windows does not. Header.astro imported as header.astro works all day on your laptop and fails in the build log. Read the log; it tells you the filename.
The four commands that ship it, and the click at the end
Prove it builds before anyone else sees it
The repo behind this site bundles its checks into one script, so there is one thing to remember. It runs the translation check, the type check, the build and the link check, in that order, and stops at the first failure.
npm run verifyBuild the folder that is the website
npm run verifyalready ran this as its third step. Run it once on its own anyway, then opendist/and look inside: plain HTML, CSS and JavaScript. Understanding that a static site is just a folder removes most of the fear from this whole step.npm run buildPush, which is your backup and your trigger
Your commits go to GitHub. This is the history you can roll back to, and on a repo wired to CI it is also the button that starts everything else.
git push origin mainPublish the folder to the edge
Wrangler is Cloudflare’s CLI. It uploads
dist/and hands you a live URL onpages.dev, served with HTTPS from data centres worldwide, at no cost.qairu-eventis the Pages project name — use your own, and the URL that comes back is that name plus.pages.dev.npx wrangler pages deploy dist --project-name=qairu-eventAttach the domain — and this one is not a command
Be honest with yourself about this step: you cannot finish it from the terminal. You open the Cloudflare dashboard, find your Pages project, go to Custom domains, and add the name. Cloudflare creates the CNAME record and issues the SSL certificate for you. Then you wait for DNS to spread.
Deploy guides that show four tidy commands quietly skip this one. Your agent will skip it too, and then tell you the site is live when it is live only on
pages.dev.
One GitHub Action, start to finish
Continuous integration is the same commands, run by someone else’s machine on every push, so that “I forgot to run the tests” stops being possible. It lives in your repo as a file:
name: Deploy
on:
push:
branches: [main]
jobs:
ship:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm run verify
- run: npx wrangler pages deploy dist --project-name=qairu-event
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}Read it top to bottom: on a push to main, rent a Linux machine, check out the code, install Node 20, install dependencies exactly as the lock file says, run the checks — which already produce dist/ — and then publish. The token is never in the file. GitHub stores it encrypted and substitutes it at run time, which is the same “secrets live on the host” rule applied to your build machine.
The cost: GitHub Free gives 2,000 Actions minutes a month on private repositories, and standard runners are free on public repositories. A public repo plus Cloudflare Pages is a complete pipeline for zero. (GitHub billing docs)
Rollback is a skill you practise on a quiet Tuesday
Your agent’s undo does not reach production. Claude Code checkpoints cover the edits it made to your files, and explicitly not remote side effects — database writes, pushes, deploys. /rewind puts your source back and the broken version stays live the whole time. (Checkpointing docs)
So rollback is a git operation followed by a deploy. Find the last commit that was fine, undo the bad one as a new commit, push, rebuild, publish:
git log --oneline -5
git revert --no-edit <commit-hash>
git push origin main
npm run build
npx wrangler pages deploy dist --project-name=qairu-eventWhat makes this fast is small commits. If one commit holds an agent’s entire afternoon across forty files, reverting it throws the good work out with the bad, and you will hesitate at exactly the moment hesitating is expensive. Commit before each agent request, not after each work session.
Then do the part almost nobody does: run that sequence today, while the site is fine. A rollback you have never tested is a plan, not a safety net.
Plan the deployment for this project to Cloudflare Pages. Do not write
any code yet.
Context:
- This is a static site. `npm run build` outputs to dist/.
- `npm run verify` runs all checks and must pass before any deploy.
- The repo is public on GitHub.
In your plan, cover:
1. The exact commands I run locally for a first manual deploy.
2. A .github/workflows/deploy.yml that runs the checks and deploys on
push to main, reading its token from an encrypted Actions secret.
3. Which secrets I must create, and where each one is stored.
4. A rollback procedure using git revert plus a redeploy.
Then list separately, under the heading "I must do these in a browser",
every step you cannot perform from the CLI — including attaching the
custom domain. Do not claim the site is live until that list is done.Put the sign-up page on a URL you can send to someone
Build it, push it, deploy it. Get the pages.dev URL and open it on your phone — not on your laptop, where a browser tab still pointed at localhost will happily lie to you.
Then set any secret the page needs in the Cloudflare dashboard rather than in the code, and if you claimed a Student Pack domain, attach it and wait for DNS.
Done when: a person who has never touched your laptop can open your link and reserve a seat, and you can point at the commit you would roll back to if they could not.
The command sequence, the secrets check, the browser-only steps and the rollback — one page to follow when the site is down and you are tired.