Start here

Two questions, then you're on a track and you can stop making decisions.


Question 1 — What did you build?#

Look at the folder your project lives in and find the row that matches. If two seem to fit, pick the lower one.

Shape 1 — A static site#

You have: .html, .css, .js files. Maybe images. You can double-click the HTML file and it opens in your browser and works.

Also this shape: anything with a build step that produces a folder of files — React, Vue, Svelte, Vite, Astro, Eleventy, plain Tailwind. You run npm run build, you get a dist/ or build/ folder, and that folder is the whole site.

Tell-tale: nothing on your machine has to stay running for the site to work.

Time: ~20 minutes on Track A, ~90 on Track B Cost: the domain on Track A; the domain plus ~$0.50/month on Track B

Shape 2 — A site with a bit of backend#

You have: a form that saves something, a page that calls an API key you don't want in the browser, a "generate with AI" button, a login, a small database.

Tell-tale: parts of it work if you open the files directly, but the interesting bits need a server to answer.

Time: ~45 minutes on Track A, ~90 on Track B Cost: the domain on Track A; the domain plus ~$0.50/month on Track B

Shape 3 — Something that has to keep running#

You have: a Python Flask/FastAPI/Streamlit app, a Discord bot, a job that runs on a schedule, a websocket server, a game server, something holding a model in memory, or a database you administer yourself.

Tell-tale: you start it with a command, and you have to leave that command running or it stops working.

Read the honest note below before you pick a track.


Question 2 — Which track?#

Both tracks get you to the same finish line. Pick on temperament, not technology.

Take Track A (Cloudflare) if…#

→ Track A: Cloudflare

Take Track B (AWS) if…#

→ Track B: AWS

Genuinely can't decide?#

Take Track A. You can move to AWS later without buying a new domain or changing a line of code — that's the whole point of the three layers, and it's the next page.


What you need on your machine#

Install these once, now, rather than discovering each one missing halfway through a step. Both tracks use all of them.

# macOS — installs Homebrew first if you don't have it
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
brew install git gh jq awscli

# Debian / Ubuntu
sudo apt update && sudo apt install -y git jq curl zip dnsutils
# gh and awscli have their own installers — see guide/07-github.md
Tool What it's for Used in
git tracks your changes everywhere
gh GitHub from the command line 02
curl fetching a URL to check it works everywhere
dig asking DNS questions directly 01, troubleshooting
jq reading JSON that AWS commands return Track B
zip packaging a Lambda Track B, backend only
aws the AWS command line Track B

dig is already on macOS. On Ubuntu it's in dnsutils. On Windows, see below.

If you're on Windows, read this first#

This guide is written in bash, and Track B in particular uses heredocs (cat > file <<EOF) and single-quoting that do not work in PowerShell or cmd. You have two good options:

  1. WSL (recommended) — open PowerShell as administrator and run wsl --install. Reboot. You now have a real Ubuntu terminal, and every command in this guide works exactly as written. This is worth twenty minutes.
  2. Git Bash — comes with Git for Windows. Fine for Track A and for 02. Track B's heredocs mostly work, but path handling occasionally bites.

Track A works fine either way and needs far less terminal — most of it is a web dashboard. If you're on Windows and want the path of least resistance, take Track A.


Before either track: two shared steps#

Do these first regardless of track. They're short.

  1. Understand the three layers — ten minutes of reading that makes every subsequent step obvious instead of magic. Skip it and you will be copying commands you don't understand, which is exactly how people end up stuck.

  2. Get your code on GitHub — both tracks deploy from a GitHub repository. This is also where you find out whether you're about to publish an API key, so it comes before anything is public.

Already have your project on GitHub with a .gitignore you trust? Skip straight to your track. Come back to 02 at the "what counts as a secret" section before you make the repo public.


The honest note about Shape 3#

If your project has to keep running, neither track's free tier is designed for it, and a guide that pretended otherwise would waste your afternoon.

Here's the real picture:

What you built What actually fits
Python API (Flask, FastAPI) A container host — Fly.io, Railway, Render. Or AWS App Runner / Lambda with an adapter.
Streamlit / Gradio app Streamlit Community Cloud or Hugging Face Spaces — both free, both built for exactly this
Discord/Telegram bot A small always-on VM, or Fly.io. It has no web address, so it needs no domain
Scheduled job GitHub Actions on a schedule: trigger. Free, and you already have the repo
Websockets / game server Cloudflare Durable Objects (Track A, advanced) or a VM
Postgres you manage Don't. Use Supabase or Neon — the free tiers are generous and backups are somebody else's problem

You can still use this guide. Do 01 and 02, buy your domain via whichever track you prefer, and then use that track's DNS section to point the domain at whatever host from the table above you chose. The domain and DNS parts transfer completely. Only the hosting chapter changes.

And a genuinely useful question to ask first: does it have to keep running, or did it just end up that way? A surprising number of Flask apps are one form and one API call, and become Shape 2 — deployable on a free tier forever — with about twenty minutes of help from Claude. It's worth asking:

This is a Flask app. Look at what it actually does — could it be a static page plus one serverless function instead? Show me what would have to change, and be honest if the answer is no.


Next: Your machine →