PhysChen.com
Home Blog
Physics
Popular Science Research
Teaching
IB Programmes
Programming
DevOps Notes
Photography
Shenzhen Portrait Cats Others Wuhan Japan
About
Home
Blog
Physics
Popular Science Research
Teaching
IB Programmes
Programming
DevOps Notes
Photography
Shenzhen Portrait Cats Others Wuhan Japan
About
On this page
    Automate Astro Deployment to a Personal Server via GitHub Actions PhysChen Lab

    Article Metadata

    • Title: Automate Astro Deployment to a Personal Server via GitHub Actions
    • Published: Apr 18, 2026
    • Source: https://physchen.com/en/programming/notes/astro-github-actions-auto-deploy-server/
    • Description: Local Astro commands, build output, and deployment to a personal server via GitHub Actions and rsync.

    Table of Contents

      Automate Astro Deployment to a Personal Server via GitHub Actions

      Apr 18, 2026 · Chinese version
      • astro
      • github-actions
      • ci-cd

      This document covers the full path from local Astro development to automated deployment. The pipeline is: build locally or in CI → produce dist/ → sync to a server directory over SSH with rsync → serve static files with Nginx or similar.

      1. Requirements

      ItemVersion or note
      Node.js22 (match CI)
      Package managerpnpm (packageManager in package.json can pin the version)
      Astro5.x
      ServerSSH (port 22) open; Nginx or another static file server installed

      Create a new project:

      pnpm create astro@latest
      cd <project-name>
      pnpm install

      2. Astro commands

      2.1 Project scripts (package.json)

      Typical definitions:

      {
        "scripts": {
          "dev": "astro dev",
          "build": "astro build",
          "preview": "astro preview",
          "astro": "astro"
        }
      }

      Run them with:

      pnpm dev       # dev server, default http://localhost:4321
      pnpm build     # production build → dist/
      pnpm preview   # serve dist/ locally

      Some projects chain extra steps in build, e.g. pnpm assets:sync && astro build && pagefind --site dist. In CI, run the same pnpm run build as production—not bare astro build—unless you have verified they are equivalent.

      2.2 Astro CLI

      Invoke via pnpm astro:

      pnpm astro dev [--host 0.0.0.0] [--port 4321]
      pnpm astro build
      pnpm astro preview [--host 0.0.0.0] [--port 4321]
      pnpm astro check          # types and templates with @astrojs/check
      pnpm astro sync           # generate .astro/types.d.ts
      pnpm astro add <integration>

      Host and port can be set in astro.config.mjs under server, or overridden with HOST / PORT (or ASTRO_HOST / ASTRO_PORT).

      2.3 Build output

      With output: 'static', astro build writes all static assets to dist/ at the project root. Deployment uploads the contents of dist/, not the source tree.

      Verify locally:

      pnpm build
      pnpm preview

      3. Key configuration

      Deployment-related fields in astro.config.mjs:

      import { defineConfig } from 'astro/config';
      
      export default defineConfig({
        output: 'static',
        site: 'https://example.com',
      });
      FieldPurpose
      output: 'static'Pure static HTML/CSS/JS; suitable for rsync deploy
      siteCanonical base URL for sitemap, RSS, etc.; set to your domain

      4. Server: SSH key

      On the server, generate a key pair for GitHub Actions:

      ssh-keygen -m PEM -t rsa -b 4096 -C "github-actions-deploy" -f ~/.ssh/github_actions

      Leave the passphrase empty (Actions cannot prompt for it).

      Add the public key and fix permissions:

      cat ~/.ssh/github_actions.pub >> ~/.ssh/authorized_keys
      chmod 700 ~/.ssh
      chmod 600 ~/.ssh/authorized_keys

      Incorrect permissions cause sshd to reject key authentication.

      Export the private key for GitHub Secrets:

      cat ~/.ssh/github_actions

      The output must include the -----BEGIN ... PRIVATE KEY----- and -----END ... PRIVATE KEY----- lines.

      5. GitHub Secrets

      Under Settings → Secrets and variables → Actions, create:

      NameValue
      SERVER_HOSTServer IP or hostname
      SERVER_USERSSH username
      SERVER_SSH_KEYFull private key from section 4

      Do not commit keys or passwords to the repo; reference them only as ${{ secrets.* }} in the workflow.

      6. GitHub Actions workflow

      Create .github/workflows/deploy.yml:

      name: Build and Deploy Astro
      
      on:
        push:
          branches:
            - main
      
      jobs:
        build-and-deploy:
          runs-on: ubuntu-latest
          concurrency:
            group: deploy-${{ github.ref }}
            cancel-in-progress: true
      
          steps:
            - name: Checkout Code
              uses: actions/checkout@v5
      
            - name: Setup pnpm
              uses: pnpm/action-setup@v6
              with:
                version: 10.28.0
      
            - name: Setup Node.js
              uses: actions/setup-node@v5
              with:
                node-version: '22'
                cache: 'pnpm'
      
            - name: Install Dependencies
              run: pnpm install --frozen-lockfile
      
            - name: Restore Image Caches
              uses: actions/cache@v5
              with:
                path: |
                  node_modules/.astro/assets
                  public/generated-previews
                key: ${{ runner.os }}-image-cache-${{ hashFiles('src/assets/images/**/*.avif', 'src/assets/images/config.json', 'src/assets/photos/**/*.avif', 'src/assets/photos/**/*.json', 'src/utils/asset-image.ts', 'src/utils/public-image.ts', 'src/utils/photography.ts', 'src/utils/hero-image.ts', 'astro.config.mjs', 'package.json', 'pnpm-lock.yaml') }}
                restore-keys: |
                  ${{ runner.os }}-image-cache-
      
            - name: Build Astro Site
              run: pnpm run build
      
            - name: Check Deploy Host Connectivity
              env:
                REMOTE_HOST: ${{ secrets.SERVER_HOST }}
              run: |
                set -eu
                getent ahosts "$REMOTE_HOST"
                timeout 10 bash -c 'cat < /dev/null > /dev/tcp/'"$REMOTE_HOST"'/22'
      
            - name: Deploy to Server
              uses: easingthemes/ssh-deploy@main
              env:
                SSH_PRIVATE_KEY: ${{ secrets.SERVER_SSH_KEY }}
                ARGS: "-rl --delete --info=progress2,stats2 --human-readable -i --exclude=files/images-originals/*** --exclude=files/photos-originals/***"
                SOURCE: "dist/"
                REMOTE_HOST: ${{ secrets.SERVER_HOST }}
                REMOTE_USER: ${{ secrets.SERVER_USER }}
                TARGET: "/var/www/your-site"

      6.1 Step reference

      StepPurpose
      concurrencyCancels an in-progress deploy when a new push arrives on the same ref
      pnpm install --frozen-lockfileInstall exactly what the lockfile specifies
      Restore Image CachesCache Astro image pipeline output; adjust hashFiles paths for your project, or remove if unused
      pnpm run buildRun the full package.json build script
      Check Deploy Host ConnectivityDNS + TCP port 22 check before rsync; optional
      easingthemes/ssh-deployrsync from SOURCE to TARGET over SSH

      6.2 rsync arguments

      FlagMeaning
      -rRecursive
      -lPreserve symlinks
      --deleteRemove files on the remote that are not in the source
      -iTransport over SSH
      --exclude=...Skip matching paths

      SOURCE: "dist/" — the trailing slash syncs the inside of dist/ onto TARGET, without an extra dist/ directory on the remote.

      Set TARGET to the actual web root on the server.

      7. Web server configuration

      Example Nginx block pointing at the deploy directory:

      server {
          listen 80;
          server_name example.com;
          root /var/www/your-site;
          index index.html;
      
          location / {
              try_files $uri $uri/ $uri.html =404;
          }
      }

      Configure HTTPS separately (e.g. Let’s Encrypt). root must match workflow TARGET.

      Paths maintained only on the server (e.g. uploaded originals) should be listed in rsync --exclude so --delete does not remove them.

      8. Deployment procedure

      1. Change code locally; run pnpm build to confirm the build succeeds.
      2. Commit and push to main.
      3. GitHub Actions runs the workflow automatically.
      4. Open the repo Actions tab; expand failed steps to read logs.

      For a one-off manual deploy:

      pnpm build
      rsync -rl --delete -e ssh dist/ user@host:/var/www/your-site/

      9. Troubleshooting

      Key authentication fails

      • ~/.ssh should be 700, authorized_keys should be 600.
      • Private key in the Secret must be complete, with correct line breaks.
      • SERVER_USER must match the account on the server.

      Build succeeds but the site is unchanged

      • TARGET and Nginx root must be the same directory.
      • Check Actions logs for rsync permission errors.

      --delete removed extra files on the server

      • The remote is forced to match dist/. Paths that must survive on the server but are not in the build need --exclude.

      Dev port already in use

      PORT=4322 pnpm dev

      Or change server.port in astro.config.mjs.

      Previous Math Chords: Customizable LaTeX Math Shortcuts for Obsidian Jun 19, 2026 Next Git Command Reference Feb 1, 2026
      Xiaohongshu Streamline Icon: https://streamlinehq.com Xiaohongshu
      闽ICP备2026003335号
      © 2026 CHEN Hua All rights reserved
      © Hua Chen / PhysChen.com