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.
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@v4If 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:
[
{
"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 |