Deployment
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 buildThis executes six steps in sequence:
- Design tokens —
build-tokens.jsconvertsdesign-tokens.jsonintoapp/tokens.css(a@themeblock for Tailwind CSS) - Search index —
build-search-index.jsscanscontent/docs/and generatespublic/searchIndex.json - FAQ index —
build-faq-index.jsextracts FAQ questions fromcontent/docs/faq/intopublic/faqIndex.json - API index —
build-api-index.jsregisters OpenAPI specs for the API documentation plugin - Next.js build — compiles the app and exports static HTML to
out/ - Link check —
check-links.jscrawls every HTML file inout/and fails the build if any internal link resolves to a missing page
During development, only the token step runs automatically. Use npm run dev to start the dev server on http://localhost:3000.
Link checking
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-linksTo check without failing the build — useful in CI environments where you want to report but not block:
node scripts/check-links.js --warn-onlyThe 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 pageAll 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 deployThis 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:
| Flag | Description |
|---|---|
--skip-build | Push 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 deployGitHub Actions alternative
For automated deploys on every push, use GitHub Actions with the newer Pages API (no gh-pages branch needed):
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@v4With this approach, set your repo Settings → Pages → Source to "GitHub Actions" instead of "Deploy from a branch".
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:
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: --prodSet 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:
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:
[
{
"from": "/old-getting-started/",
"to": "/getting-started/"
},
{
"from": "/docs/setup/",
"to": "/getting-started/",
"type": 301
},
{
"from": "/external-redirect/",
"to": "https://example.com/new-location"
}
]| Field | Required | Default | Description |
|---|---|---|---|
from | Yes | — | The old URL path to redirect from |
to | Yes | — | The destination URL (relative path or absolute URL) |
type | No | 301 | HTTP redirect type (used for documentation; the actual redirect uses meta-refresh) |
How it works
- Reads redirect definitions from
redirects.json(or a file specified in config). - Generates an HTML file for each redirect source at build time.
- Embeds a
<meta http-equiv="refresh">tag and a JavaScript fallback in each HTML file. - Creates both
path.htmlandpath/index.htmlto handle trailing slash variations.
Plugin configuration
['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— ensurestoURLs end with/trailingSlash: false— strips trailing slashes fromtoURLstrailingSlash: undefined— usestoURLs as-is
Auto-detection
If redirectsFile is not specified, the plugin automatically checks these locations:
redirects.json(project root)config/redirects.jsonsrc/redirects.json
The first valid file found is used.
Configuration reference
Key settings in next.config.mjs that affect deployment:
| Setting | Value | Purpose |
|---|---|---|
output | 'export' | Enables static HTML export |
trailingSlash | true | Generates /page/index.html instead of /page.html |
images.unoptimized | true | Required for static export (no server-side image optimization) |
basePath | undefined | Set this if deploying to a subpath |