Skip to content
Back to blog
Lancartech Team 1 min read

CI/CD for Astro / Next.js with GitHub Actions

A complete workflow: lint → test → build → deploy to in-house servers. With caching and safe rollback.

CI/CD for Astro / Next.js with GitHub Actions

Goals

  1. Push to main → auto-deploy to production
  2. PR → run lint + test + preview build
  3. Cheap deploys: cache deps, don’t reinstall everything
  4. Safe rollback if a deploy goes wrong

.github/workflows/deploy.yml

name: Build & Deploy

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: 22
          cache: 'npm'

      - run: npm ci

      - name: Type check
        run: npx astro check

      - name: Build
        run: npm run build

      - name: Pack dist
        run: tar --exclude='._*' -czf dist.tar.gz -C dist .

      - uses: actions/upload-artifact@v4
        with:
          name: dist
          path: dist.tar.gz
          retention-days: 7

  deploy:
    needs: build
    if: github.event_name == 'push' && github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/download-artifact@v4
        with:
          name: dist

      - name: Deploy via SSH
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          source: dist.tar.gz
          target: /tmp/

      - name: Atomic swap
        uses: appleboy/[email protected]
        with:
          host: ${{ secrets.DEPLOY_HOST }}
          username: ${{ secrets.DEPLOY_USER }}
          key: ${{ secrets.DEPLOY_SSH_KEY }}
          script: |
            set -euo pipefail
            STAGE=/var/www/site/staging
            mkdir -p "$STAGE"
            find "$STAGE" -mindepth 1 -delete 2>/dev/null
            tar -xzf /tmp/dist.tar.gz -C "$STAGE"
            mv /var/www/site/dist /var/www/site/dist.old
            mv "$STAGE" /var/www/site/dist
            chown -R www-data:www-data /var/www/site/dist
            find /var/www/site/dist.old -mindepth 1 -delete
            rmdir /var/www/site/dist.old
            rm /tmp/dist.tar.gz

Set up secrets

Repo Settings → Secrets and variables → Actions:

  • DEPLOY_HOST — server IP
  • DEPLOY_USER — typically root or deploy
  • DEPLOY_SSH_KEY — content of ~/.ssh/id_ed25519 (private key)

Generate a deploy-only key:

ssh-keygen -t ed25519 -f deploy_key -C "github-deploy"
ssh-copy-id -i deploy_key.pub root@your-server
cat deploy_key  # paste into the DEPLOY_SSH_KEY secret

Tips to keep builds fast

  1. Cache ~/.npmcache: 'npm' on setup-node.
  2. Cache the Astro .astro folderactions/cache@v4 keyed by astro.config.mjs hash.
  3. Split jobs — build and deploy can run in parallel for multi-env.
  4. Skip CI for doc-only commits with [skip ci] in the message.

Rollback strategy

dist.old on the server is the previous version. If a fresh deploy is bad:

ssh server
mv /var/www/site/dist /var/www/site/dist.broken
mv /var/www/site/dist.old /var/www/site/dist

Add a one-liner rollback.sh:

#!/bin/bash
set -e
mv /var/www/site/dist /var/www/site/dist.broken
mv /var/www/site/dist.old /var/www/site/dist
echo "Rolled back."

Set up CI/CD for your team.

Lancartech Team · · 1 min read

Ready to start your next project?

Initial consultation is free — let's design a digital strategy that fits your business.