GitHub·7 min read·

GitHub Pages Deploys from Actions: Static Sites

Automate static site builds and GitHub Pages deploys with configure-pages, upload-pages-artifact and deploy-pages actions in a single workflow.

NB

Netbay Cloud Team

Netbay Engineering

On this page

Publishing a static site from GitHub Actions is a two-job pipeline: build the site, then upload and deploy it to GitHub Pages. The platform even provides dedicated actions — configure-pages, upload-pages-artifact, and deploy-pages — that handle the fiddly bits like the artifact format and the environment configuration. This post builds a clean Pages deploy workflow and covers the common gotchas.

The Pages deployment model

GitHub Pages deploys from a single artifact that you produce and hand to GitHub. The recommended path routes through the pages actions rather than pushing to a branch. The flow is: configure-pages sets up the Pages environment and learns your static-site generator settings, you build the site into a folder, upload that folder as an artifact with upload-pages-artifact, and then deploy-pages sends the artifact live. The deploy uses a dedicated Pages environment with its own deployment tracking and protection options.

Pages accounts for the deployment with an environment named github-pages that you can see in the repo settings, with protection rules and usage stats.

A minimal static site deploy workflow

Here is a workflow that builds a static site (using a simple build command) and deploys it to Pages:

yaml
name: Deploy Static Site to Pages
on:
  push:
    branches: [main]
  workflow_dispatch:

permissions:
  contents: read
  pages: write
  id-token: write

concurrency:
  group: pages
  cancel-in-progress: true

jobs:
  build:
    runs-on: ubuntu-22.04
    steps:
      - uses: actions/checkout@v4
      - uses: actions/configure-pages@v5
      - name: Build static site
        run: |
          npm ci
          npm run build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: out
  deploy:
    needs: build
    runs-on: ubuntu-22.04
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

What each action does

The permissions block matters: pages: write is what lets you create the Pages deployment, and id-token: write is required by deploy-pages to mint the OIDC token GitHub verifies for the environment. The concurrency block prevents two Pages builds from racing — with cancel-in-progress true, a newer push cancels an older pending deploy.

configure-pages reads your repository Pages settings and sets up the artifact expectations; you just make sure the build output lands in the path you tell upload-pages-artifact (here out). The deploy job declares the github-pages environment and captures the final URL from the deployment step output so it is easy to verify.

Adding a custom build step

Most static sites need a specific generator. Replace the placeholder build with your real command — for example, Jekyll, Hugo, or a Node-based framework. The pattern is identical: build into a folder, point upload-pages-artifact at it. If you use a framework that outputs to a different folder (like build/ instead of out), update only the path argument.

For sites that need environment variables at build time, set them in an env block before the build step:

yaml
- name: Build site
  env:
    SITE_BASE_URL: https://octo-org.github.io/my-site/
  run: |
    npm ci
    npm run build

Gotchas: branch, base, and caching

Three things trip people up. First, ensure Pages serves from GitHub Actions in the repository Settings — either your repo's Pages source is set to GitHub Actions, or the deploy can bounce. Second, base-path handling: if your site is served from a sub-path, set it in the build config (for example, a base tag or equivalents for the framework); otherwise assets 404. Third, cache your package manager installs so the build job is fast — this build job re-runs on every push, so a dependency cache makes a real difference.

When Pages is and is not the right choice

GitHub Pages is a free, low-friction home for public static sites and project documentation. It is great for docs, landing pages, and demos. It is not for dynamic backends, and free public hosting may not be appropriate for sensitive private content. For a public static site it is a zero-cost CDN-backed host you can automate fully. For dynamic apps or private deployments you are better served by a VPS you fully control.

push to main build site upload artifact deploy-pages Pages served permissions: pages:write environment: github-pages public static site - not for dynamic backends

Takeaway

With configure-pages, upload-pages-artifact and deploy-pages you can build and publish a static site entirely from Actions, gated by the github-pages environment. It is a clean, zero-cost host for public docs and demos, but a dynamic site needs real compute you control — spin up an Ubuntu VPS on Netbay in under a minute at netbayhosts.in.

Keep reading

Follow along on a real VPS

Deploy Linux in under 60 seconds

These guides are written against Ubuntu, Debian, and RHEL-family images — the same ones on NetBay.

Deploy an instance