Deployment

Last updated: 03/04/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.

Hosting

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

Vercel

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

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

Netlify

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

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

GitHub Pages

Push the out/ directory to a gh-pages branch, or use a GitHub Actions workflow:

name: Deploy to GitHub Pages

on:
  push:
    branches: [main]

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

      - uses: actions/setup-node@v4
        with:
          node-version: 20

      - run: npm ci
      - run: npm run build

      - uses: actions/upload-pages-artifact@v3
        with:
          path: out

      - uses: actions/deploy-pages@v4
Caution

If your site is served from a subpath (e.g. https://org.github.io/repo/), set basePath in next.config.mjs:

basePath: '/repo',

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?