Deployment

Last updated: 04/02/2026Edit this page

Trellis Docs uses Next.js static export to generate a fully static site. The output is a plain out/ directory that can be deployed anywhere — no Node.js server required.

Build pipeline

Run the full build with:

npm run build

This executes six steps in sequence:

  1. Design tokensbuild-tokens.js converts design-tokens.json into app/tokens.css (a @theme block for Tailwind CSS)
  2. Search indexbuild-search-index.js scans content/docs/ and generates public/searchIndex.json
  3. FAQ indexbuild-faq-index.js extracts FAQ questions from content/docs/faq/ into public/faqIndex.json
  4. API indexbuild-api-index.js registers OpenAPI specs for the API documentation plugin
  5. Next.js build — compiles the app and exports static HTML to out/
  6. Link checkcheck-links.js crawls every HTML file in out/ and fails the build if any internal link resolves to a missing page
Tip

During development, only the token step runs automatically. Use npm run dev to start the dev server on http://localhost:3000.

The link check runs automatically as the final build step. If any internal link points to a missing page, the build exits with an error and lists the broken URLs:

Error: 2 broken link(s) found (14 occurrence(s)):

  /guides/old-page/
    in: guides/content-authoring/index.html
    in: overview/index.html
    ... and 12 more page(s)

To run the link check independently (for example, after a content-only change):

npm run check-links

To check without failing the build — useful in CI environments where you want to report but not block:

node scripts/check-links.js --warn-only

The link checker only validates internal links. External URLs (those starting with http:// or https://) are not checked during the build to keep it fast. To check external links before a release, run npm run check-links which checks everything.

Output structure

After building, the out/ directory contains everything needed to serve the site:

out/
├── _next/              # JS, CSS, and static assets
├── img/                # Images from public/img/
├── blog/               # Blog pages
├── release-notes/      # Release notes pages
├── overview/           # Docs pages (one directory per route)
├── guides/
├── theme/
├── searchIndex.json    # Client-side search data
├── faqIndex.json       # FAQ index data
└── index.html          # Home page

All URLs use trailing slashes (configured in next.config.mjs), so /guides/deployment/ resolves to out/guides/deployment/index.html.

Quick deploy

If you selected a deployment target during create-trellis-docs setup, your project already has a .github/workflows/deploy.yml configured. Push to main and your site deploys automatically.

You can also set up deployment later — see the provider-specific sections below.

Hosting

Upload or serve the contents of out/ with any static hosting provider.

GitHub Pages

Trellis Docs includes a one-command deploy, similar to Docusaurus:

npm run deploy

This builds the site, auto-detects the basePath from your GitHub remote URL, and pushes the out/ directory to a gh-pages branch. On first deploy, go to your repo Settings → Pages and set the source to "Deploy from a branch" → gh-pages.

Options:

FlagDescription
--skip-buildPush the existing out/ directory without rebuilding
--branch <name>Use a custom branch (default: gh-pages)
--remote <name>Push to a different remote (default: origin)

You can also override the subpath prefix with an environment variable:

PAGES_BASE_PATH=/my-docs npm run deploy

GitHub Actions alternative

For automated deploys on every push, use GitHub Actions with the newer Pages API (no gh-pages branch needed):

.github/workflows/deploy.yml
name: Deploy to GitHub 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-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build
        env:
          PAGES_BASE_PATH: /your-repo-name
      - uses: actions/upload-pages-artifact@v3
        with:
          path: out

  deploy:
    needs: build
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deployment.outputs.page_url }}
    steps:
      - id: deployment
        uses: actions/deploy-pages@v4

With this approach, set your repo Settings → Pages → Source to "GitHub Actions" instead of "Deploy from a branch".

Caution

If your site is served from a subpath (e.g. https://org.github.io/repo/), set PAGES_BASE_PATH to your repo name. The deploy script auto-detects this, but for GitHub Actions you need to set it explicitly in the workflow.

Vercel

Connect your Git repository. Vercel detects Next.js automatically:

  • Build command: npm run build
  • Output directory: out
  • Framework preset: Next.js

No basePath or environment variables needed — Vercel serves from the root domain.

For CI-based deploys, add a workflow:

.github/workflows/deploy.yml
name: Deploy to Vercel

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

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build
      - uses: amondnet/vercel-action@v25
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          working-directory: out
          vercel-args: --prod

Set VERCEL_TOKEN, VERCEL_ORG_ID, and VERCEL_PROJECT_ID in your repo secrets.

Netlify

Connect your Git repository or drag-and-drop the out/ folder:

  • Build command: npm run build
  • Publish directory: out

For CI-based deploys, add a workflow:

.github/workflows/deploy.yml
name: Deploy to Netlify

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

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
          cache: npm
      - run: npm ci
      - run: npm run build
      - uses: nwtgck/actions-netlify@v3
        with:
          publish-dir: out
          production-branch: main
          production-deploy: ${{ github.ref == 'refs/heads/main' }}
          deploy-message: "Deploy from GitHub Actions"
        env:
          NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
          NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}

Set NETLIFY_AUTH_TOKEN and NETLIFY_SITE_ID in your repo secrets.

AWS S3 + CloudFront

Upload the out/ directory to an S3 bucket configured for static hosting, then front it with CloudFront for HTTPS and caching:

aws s3 sync out/ s3://your-bucket-name --delete
aws cloudfront create-invalidation --distribution-id YOUR_DIST_ID --paths "/*"

Any static host

The out/ folder is self-contained. Upload it to any web server or CDN that can serve static files — Apache, Nginx, Cloudflare Pages, Firebase Hosting, etc.

Redirects

Trellis Docs includes a redirects plugin that generates HTML pages with meta-refresh redirects at build time. This is useful when you rename or move pages and want old URLs to point to the new location.

Redirect file format

Create a redirects.json file at the project root:

redirects.json
[
  {
    "from": "/old-getting-started/",
    "to": "/getting-started/"
  },
  {
    "from": "/docs/setup/",
    "to": "/getting-started/",
    "type": 301
  },
  {
    "from": "/external-redirect/",
    "to": "https://example.com/new-location"
  }
]
FieldRequiredDefaultDescription
fromYesThe old URL path to redirect from
toYesThe destination URL (relative path or absolute URL)
typeNo301HTTP redirect type (used for documentation; the actual redirect uses meta-refresh)

How it works

  1. Reads redirect definitions from redirects.json (or a file specified in config).
  2. Generates an HTML file for each redirect source at build time.
  3. Embeds a <meta http-equiv="refresh"> tag and a JavaScript fallback in each HTML file.
  4. Creates both path.html and path/index.html to handle trailing slash variations.

Plugin configuration

config/site.ts
['redirects-plugin', {
  redirectsFile: 'redirects.json',  // Path to the redirects file
}]

You can also define inline redirects in the config:

['redirects-plugin', {
  redirectsFile: 'redirects.json',
  redirects: [
    { from: '/old-page/', to: '/new-page/' },
  ],
}]

Trailing slash handling

The plugin respects the trailingSlash setting in config/site.ts:

  • trailingSlash: true — ensures to URLs end with /
  • trailingSlash: false — strips trailing slashes from to URLs
  • trailingSlash: undefined — uses to URLs as-is

Auto-detection

If redirectsFile is not specified, the plugin automatically checks these locations:

  1. redirects.json (project root)
  2. config/redirects.json
  3. src/redirects.json

The first valid file found is used.

Configuration reference

Key settings in next.config.mjs that affect deployment:

SettingValuePurpose
output'export'Enables static HTML export
trailingSlashtrueGenerates /page/index.html instead of /page.html
images.unoptimizedtrueRequired for static export (no server-side image optimization)
basePathundefinedSet this if deploying to a subpath

Was this page helpful?