Article · 2025-04-02

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:

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:

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:

![示意图](/images/blog/my-post/image.jpg)

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:

  1. Install the deployment tool:

    npm install --save-dev gh-pages
    

    This package provides the CLI tool to publish files to your repository's gh-pages branch.

  2. Configure astro.config.mjs: Set the base path 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).

  1. 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.

  1. 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.

  1. Verify GitHub Pages deployment: Log into GitHub, open your repository, and go to Settings → Pages. GitHub typically uses the gh-pages branch 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-pages reports "branch already exists," add --force to 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:

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:

  1. Personal Access Token (PAT): Generate a token in GitHub's Developer Settings (select classic type, enable repo scope). 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.

  2. 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:

  1. 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.

  2. Add a CNAME file to public/: Create a plain text file named CNAME (no extension) in your public/ 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:// or https:// in the CNAME file, and remove any trailing whitespace. GitHub is strict about format.

  1. Configure DNS: Log into your domain registrar and add a DNS record:

    • Type: CNAME
    • Name: The subdomain prefix, e.g., blog for blog.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.com with GitHub user geyuxu, create a CNAME record pointing blog to geyuxu.github.io.

  2. 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:

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):

Legacy URL examples

© 2026 Yuxu Ge ·