Building and Deploying a Personal Blog with Astro to GitHub Pages
Astro stands out among static site generators like Hexo, Hugo, and 11ty for solid reasons:
- Lightning-fast loading: Astro defaults to static rendering, producing pure HTML with minimal frontend overhead. Your blog loads instantly.
- Framework-agnostic: You're not locked into one framework. Mix React, Vue, Svelte, or any framework you prefer within the same project. This matters if you already know these tools.
- Native Markdown and MDX support: Write in clean Markdown while optionally embedding JSX components for interactive sections.
- Straightforward deployment and active ecosystem: The community is vibrant, documentation is solid, and official guides cover multiple hosting platforms. Maintenance headaches are rare.
Astro feels more modern and approachable than older generators, and it aligns with how frontend development has actually moved. If you're considering a new tech stack for your blog, it's worth trying.
Creating an Astro Blog Project
Ensure Node.js ≥ 18 is installed. Create a project with the official scaffold:
npm create astro@latest
Astro's interactive setup will guide you through templates. Select the Blog template for a quickstart with example posts and layouts. The tool installs dependencies automatically; if not, run npm install after entering the project directory.
Start the development server:
npm run dev
Astro runs on port 4321 by default. Open your browser to http://localhost:4321 to preview the site locally. You should see a working blog immediately.
Note: If port 4321 is in use, Astro tries the next available port. The terminal shows the actual address—trust the terminal output.
Your Astro project is running. Next comes customization.
Customizing Pages and Content
The blog template includes basic pages and sample content. Typical customizations include:
- Homepage: Edit
src/pages/index.astroto change what appears on the front page. - About page: Edit
src/pages/about.astro(or rename it per section 3.2 below) to personalize your bio. - Blog posts: Add Markdown or MDX files to
src/content/blog/. Astro converts these to static pages at build time.
Posts use YAML frontmatter for metadata (title, date, tags) followed by Markdown content. Follow the included examples as templates for your own articles.
About Page Example
<Layout
title="About Me" <!-- 页面标题,将会显示在页面头部或标签页标题 -->
description="Software Engineer & AI Explorer" <!-- 页面描述,有利于SEO -->
pubDate="2025-04-02" <!-- 发布日期 -->
heroImage="/blog-placeholder-about.jpg" <!-- 页眉背景图(从 public/ 文件夹引用) -->
>
<p>
Hi! I’m Yuxu Ge, a software engineer and AI enthusiast with 10+ years of backend experience.
I'm currently pursuing an MSc in AI, exploring LLMs, RAG, agents and virtual intelligence.
</p>
</Layout>
This Astro component uses the <Layout> helper from the blog template, passing title, description, date, and a hero image. Update the text, replace "Yuxu Ge" with your name, and point heroImage to your own image file in public/. Astro maps /public to the site root, so /blog-placeholder-about.jpg works if that file exists in your public folder.
Generating /about.html Instead of /about/index.html
By default, Astro generates pages as directories: about.astro becomes dist/about/index.html (accessed as /about/). If you prefer a file-based path like /about.html, rename the file:
src/pages/about.html.astro
The .html in the filename tells Astro to output a standalone HTML file instead of a directory structure. Both approaches work; this is purely a URL style preference.
After customizing your content, move on to handling images correctly—this is a common failure point during deployment.
Handling Images in Markdown Correctly
Technical blogs need images. In Astro, incorrect image paths cause a frequent problem: images display locally but vanish after deployment. The fix is straightforward:
- Place images in
public/: For example,public/images/blog/my-post/image.jpg. - Reference with absolute paths in Markdown:

The leading / means "from the site root," which maps to public/. Astro bundles everything in public/ into dist/ unchanged, so your images appear in both development and production.
Don't mix images with Markdown files in src/content/. Astro ignores binary files there, so the build output won't include them—local editor previews show images, but browsers get 404s. Think of public/ as luggage you pack carefully; src/content/ is what you leave behind.
Lesson learned: I once deployed a post only to find all images broken. The fix: move images to
public/and use root-relative paths. It solved the problem immediately.
Static resources go in public/. This single rule prevents path issues.
Deploying to GitHub Pages with gh-pages
Deploy your generated static site to GitHub Pages by building locally and pushing dist/ to a gh-pages branch. Here's how:
Install the deployment tool:
npm install --save-dev gh-pagesThis package provides the CLI tool to publish files to your repository's
gh-pagesbranch.Configure
astro.config.mjs: Set thebasepath so Astro knows where the site will live.
For a repository named virtual-velocity, add:
import { defineConfig } from 'astro/config';
export default defineConfig({
base: '/virtual-velocity/', // 基础路径:替换为你的仓库名,加前后斜杠
});
The base property tells Astro the deployment path. If your repo is virtual-velocity, the site lives at https://用户名.github.io/virtual-velocity/, so base must be /virtual-velocity/. This path must match your repository name, or CSS, JavaScript, and images will 404 after deployment and your site will look broken. I skipped this step on my first deploy—the result was a mangled page. Adding the configuration fixed it.
For a user-level repository like username.github.io (deployed at your root domain), either skip base or set it to / (the default).
- Add a deploy script to
package.json:
// package.json 部分内容
"scripts": {
"deploy": "astro build && gh-pages -d dist --branch gh-pages" // 构建并推送到gh-pages分支
}
This script builds the site and pushes dist/ to the remote gh-pages branch in one step. You can split these commands, but a single script is more convenient.
- Run the deployment:
npm run deploy
The script builds and pushes to your repository. On first run, it may ask for GitHub credentials. GitHub no longer accepts password authentication; you need a Personal Access Token or SSH Key (covered in section 7 below). Once the command succeeds, your repository's gh-pages branch is updated.
- Verify GitHub Pages deployment: Log into GitHub, open your repository, and go to Settings → Pages. GitHub typically uses the
gh-pagesbranch automatically if it exists. If not, manually set the source to that branch. After a few seconds, your site should be live at (for a project repository):
https://<你的 GitHub 用户名>.github.io/<你的仓库名>/
Replace username and repo-name with your own. For the example virtual-velocity repository, visit https://yourname.github.io/virtual-velocity/ to see your deployed blog.
Note: If
gh-pagesreports "branch already exists," add--forceto override:gh-pages -d dist --branch gh-pages --force. Be aware that force-pushing overwrites prior branch history.
Your Astro blog is now on GitHub Pages. Updates are one command away: npm run deploy.
Common Problems and Solutions
Deployment usually works, but here are issues I've encountered and how to fix them:
- Missing
sharpmodule: If you seeError: Cannot find module 'sharp', runnpm install sharp. Alternatively, use Astro's Squoosh image processing (WebAssembly-based, no local binary dependency). - Images work locally but 404 after deployment: Images are in the wrong place. Put them in
public/and reference with absolute paths starting with/. Only files inpublic/get bundled into the published site. - Build outputs
/about/index.htmlinstead of/about.html: This is Astro's default. Rename the page file toabout.html.astro(see section 3.2) to generate a single HTML file instead. gh-pagesreports "remote branch already exists": Add--forceto the deployment command to overwrite the branch. Note that force-pushing discards prior history, so use it carefully.- SSH connection errors (port 22 blocked): Some networks (corporate, campus) block port 22. If your local repository uses SSH, deployments will fail. Switch to HTTPS:
git remote set-url origin https://github.com/yourname/yourrepo.git. HTTPS uses port 443, which is rarely blocked. - GitHub rejects your password: GitHub disabled password-based Git authentication. Use a Personal Access Token instead, or set up SSH keys (see section 7).
The last point about authentication is critical. Without proper credentials, code won't push. Let's cover authentication next.
GitHub Authentication Setup (Essential)
Since late 2021, GitHub requires tokens or SSH keys for git push. Password authentication no longer works. You have two options:
Personal Access Token (PAT): Generate a token in GitHub's Developer Settings (select classic type, enable
reposcope). Copy it and paste it when prompted for a password during push. Treat tokens like passwords—save them safely and never commit them to your repository. If leaked, revoke it immediately in GitHub settings.Tip: Think of a token as a "temporary passport" for command-line access. Generate it in GitHub settings and always keep it private.
SSH Key: If you've already created an SSH key pair locally and added the public key to your GitHub account, use SSH for password-free authentication:
git remote set-url origin [email protected]:yourname/yourrepo.git
Replace yourname/yourrepo with your GitHub username and repository name. This changes the remote URL from HTTPS to SSH format. Push commands now authenticate via SSH (assuming port 22 isn't blocked and you've added your public key to GitHub). See GitHub's documentation if you haven't generated an SSH key yet.
For most individual developers, token authentication is simpler: generate once, use for all deployments. SSH is better if you use Git frequently and want zero-password workflows. Either way, set one up before deploying, or you'll get stuck at the authentication step.
Custom Domain Setup (Optional)
After the steps above, your blog is live at GitHub's default domain (e.g., https://yourname.github.io/yourrepo/). To use your own domain like blog.geyuxu.com, follow these steps:
Set custom domain in GitHub: Open your repository's Settings → Pages. Find the "Custom domain" field, enter your domain (e.g.,
blog.geyuxu.com), and save. Check "Enforce HTTPS" to redirect HTTP traffic to HTTPS automatically.Add a CNAME file to
public/: Create a plain text file namedCNAME(no extension) in yourpublic/folder with a single line:blog.geyuxu.com
Save and commit this file. When Astro builds, it includes CNAME in the output. This file tells GitHub Pages which domain to use.
Warning: Don't include
http://orhttps://in the CNAME file, and remove any trailing whitespace. GitHub is strict about format.
Configure DNS: Log into your domain registrar and add a DNS record:
- Type: CNAME
- Name: The subdomain prefix, e.g.,
blogforblog.geyuxu.com(leave blank or use@for the root domain, but root domains also need an A record—see GitHub's docs) - Value: Your GitHub Pages address, e.g.,
yourname.github.io - TTL: Default or automatic
Example: For
blog.geyuxu.comwith GitHub usergeyuxu, create a CNAME record pointingblogtogeyuxu.github.io.Test: DNS changes take minutes to hours to propagate. Once updated, visiting your custom domain (e.g., https://blog.geyuxu.com) should show your blog.
Tips:
- After the first setup,
npm run deployautomatically includes your CNAME file, so you won't need to manually reconfigure GitHub Pages later. - For a root domain (e.g.,
geyuxu.cominstead of a subdomain), you'll need both a CNAME record and A records pointing to GitHub's IP addresses. Check GitHub's documentation for current IPs.
Summary
Astro and GitHub Pages combine to create a fast, maintainable, free personal blog. Once setup is complete, publishing is a single command: npm run deploy. The barrier to entry is low—focus on writing, not deployment infrastructure.
The workflow is straightforward: scaffold with Astro, customize your content, configure deployment, troubleshoot common issues, set up authentication, and optionally add a custom domain. Each step is manageable. You'll avoid common pitfalls by following this guide, and you'll have a solid platform for sharing your thoughts online.
Example URLs (after deployment):
- Default GitHub Pages URL: https://yourname.github.io/your-repo/ (replace yourname and your-repo)
- Custom domain example: Your own domain registered in DNS and configured above