For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
Book a demoLog inStart for free
  • Getting started
    • Overview
    • How it works
    • Quickstart
    • Project structure
    • Customer showcase
    • Changelog
  • Configuration
    • Overview
    • Site-level settings
    • Page-level settings
  • Writing content
    • Markdown basics
    • Rich media in Markdown
    • Fern Editor
    • Reusable snippets
  • AI features
    • Overview
    • Fern Writer
    • AI-generated examples
    • Markdown access
      • Overview
      • Customize LLM output
      • Agent directives
      • Analytics and integration
    • MCP server
    • API catalog discovery
      • Overview
      • Set up self-hosted documentation
      • Authentication
      • Previews
      • Health check endpoints
      • Releases
  • Public API
    • GETJWT from Fern API key
    • GETAlgolia search credentials
    • GETCurrent user information
  • Fern Writer API
    • GETGet Fern Writer Install Link
Checking status...
SOC2Soc 2 Type II
© 2026 Fern • Birch Solutions, Inc., a Postman company

Documentation

SDKsDocsAsk FernCLI Reference

API Definitions

OpenAPIAsyncAPIOpenRPCgRPC

Resources

BlogSupportPricing

Company

Brand KitPrivacy PolicyTerms of Service
LogoLogo
Book a demoLog inStart for free
On this page
  • Choosing an approach
  • GitHub Actions workflows
Self-hosted

Previews

Beta
||View as Markdown|
Was this page helpful?
Edit this page
Previous

Authentication

Next

Health check endpoints

Enterprise feature

This feature is available only for the Enterprise plan. To get started, reach out to support@buildwithfern.com.

There are two ways to set up preview environments for your self-hosted docs:

  • Container-based — deploys the same self-hosted Docker container you use in production, giving you a full-fidelity preview with search, API Explorer, and authentication.
  • Static export — renders your docs to static HTML and assets that can be served from any object store (S3, GCS, R2), with no running containers required.

Both approaches render content exactly as it would appear in production.

Choosing an approach

For most teams, static export is the best starting point because of its minimal infrastructure and setup. Choose container-based if you need search, API Explorer, or authentication in your previews.

Container-basedStatic export Beta
Content renderingIdentical to productionIdentical to production
SearchYesNo
API Explorer (Try it)YesNo
AuthenticationYesNo
InfrastructureRequires container hosting, load balancer, DNSServes from any object store (S3, GCS, R2)
ScalingOne container per previewThousands of previews with no servers
CostHigherLow
CleanupMust tear down containers and resourcesSimple — delete static files

GitHub Actions workflows

The following workflows can be added to your repository to automatically build and deploy preview environments on every pull request.

Container-based preview workflow

This workflow builds the Docker image on each pull request and deploys it to your container hosting platform.

.github/workflows/preview-docs-container.yml
1name: preview-docs-container
2
3on:
4 pull_request:
5
6jobs:
7 preview-docs:
8 runs-on: ubuntu-latest
9 permissions: write-all
10 steps:
11 - name: Checkout repository
12 uses: actions/checkout@v4
13
14 - name: Log in to Docker Hub
15 uses: docker/login-action@v3
16 with:
17 username: ${{ secrets.DOCKERHUB_USERNAME }}
18 password: ${{ secrets.DOCKERHUB_TOKEN }}
19
20 - name: Build docs container
21 run: docker build -t self-hosted-docs:${{ github.sha }} .
22
23 - name: Push container to registry
24 run: |
25 docker tag self-hosted-docs:${{ github.sha }} ${{ secrets.REGISTRY }}/self-hosted-docs:${{ github.sha }}
26 docker push ${{ secrets.REGISTRY }}/self-hosted-docs:${{ github.sha }}
27
28 - name: Deploy preview
29 run: |
30 # Deploy the container to your hosting platform
31 # (e.g. ECS, Cloud Run, Kubernetes, etc.)
32 # and retrieve the preview URL
33 echo "Deploy self-hosted-docs:${{ github.sha }} to your platform"
Static export preview workflow

This workflow builds the container, runs the static export, and uploads the output to S3. Adapt the upload step for your object store.

.github/workflows/preview-docs-static.yml
1name: preview-docs-static
2
3on:
4 pull_request:
5
6jobs:
7 preview-docs:
8 runs-on: ubuntu-latest
9 permissions: write-all
10 steps:
11 - name: Checkout repository
12 uses: actions/checkout@v4
13
14 - name: Log in to Docker Hub
15 uses: docker/login-action@v3
16 with:
17 username: ${{ secrets.DOCKERHUB_USERNAME }}
18 password: ${{ secrets.DOCKERHUB_TOKEN }}
19
20 - name: Build docs container
21 run: docker build -t self-hosted-docs .
22
23 - name: Start container
24 run: |
25 docker run -d \
26 --name docs-container \
27 -e WARMUP=true \
28 self-hosted-docs
29
30 - name: Wait for container to be healthy
31 run: |
32 echo "Waiting for container to be healthy..."
33 MAX_ATTEMPTS=60
34 ATTEMPT=0
35 while [ "$ATTEMPT" -lt "$MAX_ATTEMPTS" ]; do
36 ATTEMPT=$((ATTEMPT + 1))
37 if docker exec docs-container sh -c \
38 'TOKEN=$(cat /tmp/.cache-admin-token 2>/dev/null); \
39 curl -f -s --max-time 5 \
40 -H "Authorization: Bearer $TOKEN" \
41 http://localhost:3000/__cache/stats' > /dev/null 2>&1; then
42 echo "Container is healthy."
43 break
44 fi
45 echo " Not ready yet... ($ATTEMPT/$MAX_ATTEMPTS)"
46 sleep 5
47 done
48 if [ "$ATTEMPT" -ge "$MAX_ATTEMPTS" ]; then
49 echo "Error: container did not become healthy."
50 exit 1
51 fi
52
53 - name: Export static site
54 run: |
55 docker exec docs-container /scripts/export.sh
56 docker cp docs-container:/tmp/fern-static-export.tar.gz ./export.tar.gz
57
58 - name: Extract static files
59 run: |
60 mkdir -p ./site
61 tar -xzf export.tar.gz -C ./site
62
63 - name: Upload to S3
64 uses: jakejarvis/s3-sync-action@v0.5.1
65 with:
66 args: --delete
67 env:
68 SOURCE_DIR: ./site
69 AWS_S3_BUCKET: ${{ secrets.AWS_S3_BUCKET }}
70 AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
71 AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
72 AWS_REGION: ${{ secrets.AWS_REGION }}
73 DEST_DIR: pr-${{ github.event.pull_request.number }}
74
75 - name: Comment preview URL on PR
76 if: github.event_name == 'pull_request'
77 uses: thollander/actions-comment-pull-request@v3
78 with:
79 message: |
80 Preview: http://${{ secrets.AWS_S3_BUCKET }}.s3-website.${{ secrets.AWS_REGION }}.amazonaws.com/pr-${{ github.event.pull_request.number }}
81 pr-number: ${{ github.event.pull_request.number }}
82
83 - name: Stop container
84 if: always()
85 run: docker rm -f docs-container