Last updated July 11, 2026

Deployment

2 minutes read

folio.md produces plain HTML, CSS, and JS in dist/, ready to serve from GitHub Pages or GitLab Pages.

Build

If you installed folio as a dependency via folio init:

bash
bun run folio:build    # bun
npm run folio:build    # npm
yarn folio:build       # yarn

If you cloned the folio.md repository directly:

bash
bun run build    # bun
npm run build    # npm
yarn build       # yarn

The link validator runs automatically before every build. When you install folio.md as a dependency, it runs through folio:build. When you work in the repository directly, it runs through bun run build. It fails the build at exit code 1 on broken internal links and dead anchor fragments. Dead external URLs emit a warning but don’t fail the build.

If you cloned the folio.md repository directly, you can also run the link validator without a full build:

bash
bun run validate-links

When you install folio.md as a dependency, this standalone command isn’t available. Link validation runs only as part of folio:build.

Deploying to GitHub pages

Add a workflow at .github/workflows/deploy.yml. Choose the tab for your package manager:

Bun

yaml.github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
  push:
    branches: [master]
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0        # needed for "last updated" git dates
      - uses: oven-sh/setup-bun@v2
      - run: bun install
      - run: bun run build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/deploy-pages@v4
        id: deployment

npm

yaml.github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
  push:
    branches: [master]
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm install
      - run: npm run build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/deploy-pages@v4
        id: deployment

yarn

yaml.github/workflows/deploy.yml
name: Deploy to GitHub Pages
on:
  push:
    branches: [master]
permissions:
  contents: read
  pages: write
  id-token: write
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: yarn
      - run: yarn build
      - uses: actions/upload-pages-artifact@v3
        with:
          path: dist
  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - uses: actions/deploy-pages@v4
        id: deployment

Deploying to GitLab pages

Add a pipeline at .gitlab-ci.yml:

Bun

yaml.gitlab-ci.yml
pages:
  image: oven/bun:latest
  script:
    - bun install
    - bun run build
    - mv dist public   # GitLab Pages expects output in public/
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "master"

npm / yarn

yaml.gitlab-ci.yml
pages:
  image: node:20
  script:
    - npm install          # or: yarn
    - npm run build        # or: yarn build
    - mv dist public
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == "master"

Custom server

Copy dist/ to any static file host. With nginx:

nginxnginx.conf
server {
    listen 80;
    root /var/www/my-docs/dist;
    index index.html;
    location / {
        try_files $uri $uri/ $uri.html =404;
    }
}