Zero to Published in 30 Minutes
Astro Blog: Zero to Published in 30 Minutes
A no-nonsense walkthrough that gets you from “I have nothing” to “my blog is live on the internet” using Astro and Cloudflare Pages. Designed for a front-end dev who has never touched a static site generator.
What you’ll have at the end
A live blog at a real URL (e.g., your-blog.pages.dev or your own domain), with one published post, hot-reload local development, and automatic deploys whenever you push to GitHub.
Prerequisites (verify these in 2 minutes)
Open Terminal and run these to check what you already have:
node --version # need 18.20.8+ or 20.3.0+ (anything 22+ is fine)
npm --version # any recent version
git --version # any recent version
If Node is missing or too old, install the LTS from nodejs.org or use Homebrew: brew install node. Everything else you need (npm, npx) comes bundled with Node.
You’ll also need a GitHub account (you have one) and a free Cloudflare account (cloudflare.com, takes 60 seconds to create if you don’t have one).
Step 1: Create the Astro project (5 minutes)
In Terminal, navigate wherever you keep your projects, then:
npm create astro@latest
It’ll ask you a series of questions. Recommended answers for your first time:
| Question | Answer |
|---|---|
| Where should we create your new project? | ./my-blog (or whatever name you want) |
| How would you like to start your new project? | Use blog template ← important |
| Install dependencies? | Yes |
| Do you plan to write TypeScript? | No (keep it simple) |
| Initialize a new git repository? | Yes |
Wait about a minute while it installs. You’ll get a folder with a fully working blog, including styled pages, RSS feed, sample posts, and an “About” page.
Step 2: Run it locally (2 minutes)
cd my-blog
npm run dev
You’ll see something like:
🚀 astro v4.x.x started in 234ms
┃ Local http://localhost:4321/
┃ Network use --host to expose
Open that URL in your browser. You’re looking at a working blog. Take 60 seconds to click around — there’s a homepage, a blog index, individual post pages, and an About page.
Leave this terminal running. The server hot-reloads when you change files, just like any modern front-end build tool.
Step 3: Look around the file structure (5 minutes)
In a new Terminal tab (or your editor), here’s the lay of the land:
my-blog/
├── src/
│ ├── content/
│ │ └── blog/ ← your posts go here as .md files
│ │ ├── first-post.md
│ │ ├── second-post.md
│ │ └── ...
│ ├── pages/ ← URLs of your site
│ │ ├── index.astro ← homepage
│ │ ├── about.astro
│ │ └── blog/ ← /blog routes
│ ├── layouts/ ← page templates
│ ├── components/ ← reusable HTML components
│ └── consts.ts ← site title, description, etc.
├── public/ ← static files served as-is (favicon, images)
├── astro.config.mjs ← config (you probably won't touch this)
└── package.json
The two folders you’ll spend 95% of your time in: src/content/blog/ (writing posts) and src/pages/ (page templates and routing).
Step 4: Make it yours (8 minutes)
Change site title and description
Open src/consts.ts. It looks something like this:
export const SITE_TITLE = 'Astro Blog';
export const SITE_DESCRIPTION = 'Welcome to my website!';
Change those to your actual blog title and description. Save. Watch the browser auto-refresh.
Write your first real post
In src/content/blog/, create a new file called hello-world.md:
---
title: 'Hello World'
description: 'The first post on my new Astro blog'
pubDate: 'May 9 2026'
heroImage: '/blog-placeholder-1.jpg'
---
This is my first post on Astro. The Markdown just works.
## A subheading
Regular paragraph text. **Bold** and *italic* and [links](https://example.com) all work like you'd expect.
- Bullet points
- Also work
- Naturally
That's the whole format.
The block at the top between --- markers is called frontmatter — it’s just metadata Astro uses to build the post. Below it is plain Markdown.
Save the file. Refresh localhost:4321/blog — your post is there.
Delete the sample posts
When you’re ready, just delete the first-post.md, second-post.md, etc. files in src/content/blog/. Astro automatically updates.
(Optional) Tweak the styling
The CSS lives in src/styles/global.css. Edit anything you want. Hot-reload applies instantly.
Step 5: Push to GitHub (5 minutes)
Go to github.com/new and create a new repository:
- Name:
my-blog(or whatever) - Public or Private — your choice (Cloudflare Pages works with both)
- Don’t initialize with README, .gitignore, or license — Astro already created those
GitHub will show you a “push an existing repository” snippet. The commands look like this (use the ones GitHub shows you with your actual username):
# In your my-blog folder, in Terminal:
git add .
git commit -m "Initial commit"
git branch -M main
git remote add origin https://github.com/YOUR-USERNAME/my-blog.git
git push -u origin main
Refresh GitHub — your code is up.
Step 6: Deploy to Cloudflare Pages (5 minutes)
- Log into dash.cloudflare.com.
- In the left sidebar, click Workers & Pages.
- Click Create → Pages tab → Connect to Git.
- Authorize Cloudflare to access your GitHub (one-time setup).
- Select your
my-blogrepository. - On the build settings screen:
- Framework preset: Select Astro from the dropdown
- Build command:
npm run build(auto-filled) - Build output directory:
dist(auto-filled) - Leave everything else default
- Click Save and Deploy.
Wait 1–2 minutes while Cloudflare builds your site. When it’s done, you’ll get a URL like my-blog-x7y.pages.dev. Click it.
Your blog is live on the internet.
What just happened (the mental model)
From this point on, your workflow is:
- Edit Markdown files locally
git push- Cloudflare automatically rebuilds and redeploys, usually in under 90 seconds
There’s no server. No database. No PHP. No plugins to update. Your site is just static HTML files served from Cloudflare’s CDN — which is why it’ll load in under 100ms anywhere in the world.
Hooking up a custom domain (5 minutes, optional)
In your Cloudflare Pages project: Custom domains tab → Set up a custom domain → enter your domain → follow the prompts. If your domain is already on Cloudflare, it’s literally one click. If it’s at another registrar, you’ll add a CNAME record they’ll tell you exactly how to format.
Common gotchas
“npm create astro@latest” fails with permission errors. You may need to clear npm’s cache: npm cache clean --force, then retry.
Hot reload doesn’t work. Make sure you’re editing files inside src/, not public/. Files in public/ are copied as-is at build time and don’t trigger reloads.
Cloudflare build fails with “Node version” error. Add an environment variable in your Cloudflare Pages project settings: NODE_VERSION = 20 (or 22).
You changed src/consts.ts but the browser title didn’t update. Some pages cache the title from a layout — try a hard refresh (Cmd+Shift+R).
Markdown post doesn’t appear in the blog index. Check your frontmatter — the pubDate field needs to be a real parseable date, and all required fields must be present. Look at the sample posts for the exact format.
What to learn next (in order of usefulness)
- Frontmatter fields: Open
src/content.config.ts(orsrc/content/config.ts) to see exactly which fields are required for blog posts. You can add your own fields here (tags, author, draft flag, etc.). - Components: Look at
src/components/andsrc/layouts/. Astro components (.astrofiles) are HTML with a script tag on top. Familiar territory for a front-end dev. - Adding pages: Drop a new
.astroor.mdfile insrc/pages/and it becomes a URL. That’s the entire routing system. - RSS feed: Already configured at
/rss.xml. Substack subscribers can plug your URL straight into their reader. - Comments: Add Giscus (free, uses GitHub Discussions) when you want it. ~30 minutes.
- CMS for non-technical writers: If you ever want a UI-based editor for clients, look at Decap CMS or TinaCMS as a layer on top of this exact setup.
Total time check
| Step | Estimate |
|---|---|
| Prerequisites | 2 min |
| Create project | 5 min |
| Run locally | 2 min |
| Explore structure | 5 min |
| Customize | 8 min |
| Push to GitHub | 5 min |
| Deploy to Cloudflare | 5 min |
| Total | 32 min |
Close enough. Welcome to the indie web.