Skip to content

Deploying Docusaurus

Docusaurus is a modern, powerful static site generator built for documentation websites. It features Markdown support with JSX components, automatic versioning, multi-language support, a rich plugin ecosystem, full-text search capabilities, and a beautiful default theme. Docusaurus is ideal for open-source projects, product documentation, and knowledge bases requiring professional presentation and easy maintenance.

This comprehensive guide walks through deploying a Docusaurus documentation site to Klutch.sh using either Nixpacks (automatic zero-configuration deployment) or a Dockerfile (manual container control). You’ll learn how to scaffold a Docusaurus project, structure documentation content, customize branding and styling, configure search and versioning, set up multi-language support, implement security best practices, set up monitoring, deploy custom domains, and troubleshoot common issues. By the end of this guide, you’ll have a production-ready Docusaurus documentation site running on Klutch.sh’s global infrastructure with automatic HTTPS and optimized performance.

Prerequisites

  • Node.js & npm (version 16+) – Download Node.js
  • Git installed locally and a GitHub account (Klutch.sh uses GitHub as the only git source)
  • Klutch.sh account with access to the dashboard at klutch.sh/app
  • Basic knowledge of Markdown, JavaScript/JSX, and the Node.js ecosystem

Getting Started: Create a Docusaurus Site

1. Create a New Docusaurus Project

Use the Docusaurus scaffolding tool to create a new project:

Terminal window
npx create-docusaurus@latest my-docusaurus-site classic
cd my-docusaurus-site
npm install

2. Project Structure

A typical Docusaurus project structure looks like:

my-docusaurus-site/
├── blog/
│ ├── 2024-01-01-hello-world.md
│ └── authors.yml
├── docs/
│ ├── getting-started/
│ │ ├── intro.md
│ │ └── installation.md
│ ├── guides/
│ │ ├── api-reference.md
│ │ └── configuration.md
│ ├── api/
│ │ ├── functions.md
│ │ └── classes.md
│ ├── intro.md
│ └── _category_.json
├── src/
│ ├── components/
│ │ ├── HomepageFeatures.js
│ │ └── CustomHeader.js
│ ├── css/
│ │ └── custom.css
│ ├── pages/
│ │ ├── index.js
│ │ └── contact.js
│ └── theme/
├── static/
│ ├── img/
│ └── favicon.ico
├── docusaurus.config.js
├── package.json
├── sidebars.js
├── Dockerfile
└── README.md

3. Run the Development Server

Start the Docusaurus development server:

Terminal window
npm run start

Navigate to http://localhost:3000 in your browser. The site will hot-reload as you make changes.

4. Sample Documentation Page

Create a sample documentation page:

---
id: getting-started
title: Getting Started
description: Learn how to get started with our product
sidebar_position: 1
---
# Getting Started
Welcome to our documentation! This guide will help you get up and running quickly.
## Installation
Install the package using npm:
```bash
npm install @mycompany/mypackage

Basic Usage

Here’s a simple example:

import { MyComponent } from '@mycompany/mypackage';
export default function App() {
return <MyComponent />;
}

Next Steps

### 5. Custom React Component
Create a custom React component for your docs:
```jsx
// src/components/FeatureCard.js
import React from 'react';
export default function FeatureCard({ icon, title, description }) {
return (
<div style={{
padding: '20px',
borderRadius: '8px',
border: '1px solid #eee',
marginBottom: '20px'
}}>
<h3 style={{ marginTop: 0 }}>{icon} {title}</h3>
<p>{description}</p>
</div>
);
}

Use it in your documentation:

import FeatureCard from '@site/src/components/FeatureCard';
<FeatureCard
icon="🚀"
title="Fast"
description="Lightning fast performance with optimized builds"
/>
<FeatureCard
icon="🎨"
title="Customizable"
description="Fully customizable theme and components"
/>

6. Configure Docusaurus

Update docusaurus.config.js with your site information:

docusaurus.config.js
const config = {
title: 'My Documentation Site',
tagline: 'Comprehensive documentation powered by Docusaurus',
url: 'https://example-app.klutch.sh',
baseUrl: '/',
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'warn',
favicon: 'img/favicon.ico',
organizationName: 'myorg',
projectName: 'my-docs',
presets: [
[
'classic',
{
docs: {
sidebarPath: require.resolve('./sidebars.js'),
editUrl: 'https://github.com/myorg/my-docs/edit/main/',
},
blog: {
showReadingTime: true,
editUrl: 'https://github.com/myorg/my-docs/edit/main/',
},
theme: {
customCss: require.resolve('./src/css/custom.css'),
},
},
],
],
plugins: [
[
'@docusaurus/plugin-search-local',
{
language: 'en',
hashed: true,
}
]
],
themeConfig: {
navbar: {
title: 'My Docs',
logo: {
alt: 'My Docs Logo',
src: 'img/logo.svg',
},
items: [
{
type: 'doc',
docId: 'intro',
position: 'left',
label: 'Docs',
},
{
to: '/blog',
label: 'Blog',
position: 'left'
},
{
href: 'https://github.com/myorg/my-docs',
label: 'GitHub',
position: 'right',
},
],
},
footer: {
style: 'dark',
links: [
{
title: 'Docs',
items: [
{
label: 'Getting Started',
to: '/docs/getting-started',
},
],
},
{
title: 'Community',
items: [
{
label: 'Discord',
href: 'https://discord.gg/myserver',
},
],
},
],
copyright: `Copyright © ${new Date().getFullYear()} My Company.`,
},
},
};
module.exports = config;

7. Configure Sidebars

Organize your documentation structure in sidebars.js:

sidebars.js
module.exports = {
documentationSidebar: [
'intro',
{
label: 'Getting Started',
items: ['getting-started/installation', 'getting-started/quick-start'],
},
{
label: 'Guides',
items: [
'guides/configuration',
'guides/customization',
'guides/deployment',
],
},
{
label: 'API Reference',
items: ['api/functions', 'api/classes', 'api/hooks'],
},
],
};

8. Setup Multi-Language Support

Enable internationalization in docusaurus.config.js:

const config = {
i18n: {
defaultLocale: 'en',
locales: ['en', 'es', 'fr'],
localeConfigs: {
en: {
label: 'English',
},
es: {
label: 'Español',
},
fr: {
label: 'Français',
},
},
},
// ... rest of config
};

Create translated documentation:

docs/
├── intro.md (English)
├── es/
│ └── intro.md (Español)
└── fr/
└── intro.md (Français)

9. Package Scripts

Ensure your package.json has the necessary scripts:

{
"scripts": {
"docusaurus": "docusaurus",
"start": "docusaurus start",
"build": "docusaurus build",
"swizzle": "docusaurus swizzle",
"deploy": "docusaurus deploy",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-translations": "docusaurus write-translations",
"write-heading-ids": "docusaurus write-heading-ids"
}
}

10. Production Server Setup

For serving the built Docusaurus site in production, create a simple Node.js server:

server.js
const express = require('express');
const path = require('path');
const app = express();
const PORT = process.env.PORT || 3000;
// Serve static files from the build directory
app.use(express.static(path.join(__dirname, 'build')));
// Handle client-side routing
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Update package.json to support production serving:

{
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"serve": "node server.js"
}
}

Local Production Build Test

Before deploying, test the production build locally:

Terminal window
npm install express
npm run build
npm run serve

Visit http://localhost:3000 to verify that your documentation site renders correctly in production mode.


Deploying with Nixpacks

Nixpacks automatically detects your Node.js/Docusaurus application and configures build and runtime environments without requiring a Dockerfile. This is the simplest deployment method for Docusaurus sites.

Prerequisites for Nixpacks Deployment

  • Your Docusaurus project pushed to a GitHub repository
  • Valid package.json with build and start scripts
  • No Dockerfile in the repository root (if one exists, Klutch.sh will use Docker instead)

Steps to Deploy with Nixpacks

  1. Push Your Docusaurus Project to GitHub

    Initialize and push your project to GitHub if you haven’t already:

    Terminal window
    git init
    git add .
    git commit -m "Initial Docusaurus site"
    git branch -M main
    git remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.git
    git push -u origin main
  2. Log In to Klutch.sh Dashboard

    Go to klutch.sh/app and sign in with your GitHub account.

  3. Create a Project

    Navigate to the Projects section and create a new project for your Docusaurus site.

  4. Create an App

    Click “Create App” and select your GitHub repository.

  5. Select the Branch

    Choose the branch you want to deploy (typically main).

  6. Configure Traffic Type

    Select HTTP as the traffic type for Docusaurus (a static site generator serving HTML/assets).

  7. Set the Internal Port

    Set the internal port to 3000 – this is the port where your Node.js production server listens.

  8. Add Environment Variables (Optional)

    Add any environment variables your Docusaurus site requires:

    NODE_ENV=production
    SITE_URL=https://example-app.klutch.sh

    If you need to customize the Nixpacks build or start command, use these environment variables:

    • BUILD_COMMAND: Override the default build command (e.g., npm run build)
    • START_COMMAND: Override the default start command (e.g., npm run serve)
  9. Configure Compute Resources

    Select your region, compute size, and number of instances based on expected traffic.

  10. Deploy

    Click “Create” to start the deployment. Nixpacks will automatically build and deploy your Docusaurus site. Your site will be available at a URL like https://example-app.klutch.sh.


Deploying with Docker

For more control over your deployment environment, you can use a Dockerfile. Klutch.sh automatically detects a Dockerfile in your repository root and uses it for deployment.

Creating a Dockerfile for Docusaurus

Create a Dockerfile in the root of your Docusaurus project:

# === Build stage ===
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# === Runtime stage ===
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install --only=production
COPY --from=builder /app/build ./build
COPY --from=builder /app/server.js ./server.js
ENV NODE_ENV=production
ENV PORT=3000
EXPOSE 3000
CMD ["npm", "run", "serve"]

Alternative Dockerfile for Static Hosting (Nginx)

For a lighter-weight deployment using Nginx:

# === Build stage ===
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# === Runtime stage (Nginx) ===
FROM nginx:alpine
COPY --from=builder /app/build /usr/share/nginx/html
# Add nginx configuration for SPA routing
RUN echo 'server { \
listen 80; \
location / { \
root /usr/share/nginx/html; \
try_files $uri $uri/ /index.html; \
} \
}' > /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

Dockerfile Notes

  • Builder stage: Installs dependencies and builds your Docusaurus site for production.
  • Runtime stage: Uses a lightweight Node.js Alpine image with the Express server (or Nginx alternative).
  • Port: The PORT environment variable is set to 3000 for Node.js or 80 for Nginx.
  • Multi-stage build: Reduces final image size by excluding build tools and dev dependencies from the runtime container.

Steps to Deploy with Docker

  1. Create a Dockerfile

    Add the Dockerfile (shown above) to the root of your Docusaurus repository.

  2. Test Locally (Optional)

    Build and test the Docker image locally:

    Terminal window
    docker build -t docusaurus-app:latest .
    docker run -p 3000:3000 docusaurus-app:latest

    Visit http://localhost:3000 to verify.

  3. Push to GitHub

    Commit and push the Dockerfile and your code:

    Terminal window
    git add Dockerfile
    git commit -m "Add Dockerfile for production deployment"
    git push origin main
  4. Create an App in Klutch.sh

    Go to klutch.sh/app, navigate to “Create App”, and select your repository.

  5. Configure the App
    • Traffic Type: Select HTTP
    • Internal Port: Set to 3000 (or 80 if using the Nginx Dockerfile)
    • Environment Variables: Add any required runtime variables
  6. Deploy

    Klutch.sh automatically detects the Dockerfile and uses it to build and deploy your site. Your site will be available at https://example-app.klutch.sh.


Environment Variables

Define all environment variables in the Klutch.sh dashboard. Here’s a recommended set for production:

NODE_ENV=production
SITE_URL=https://example-app.klutch.sh
ANALYTICS_ID=your-analytics-id
GTM_ID=your-gtm-id

Accessing Environment Variables in Docusaurus

Access environment variables in your docusaurus.config.js:

const config = {
title: 'My Documentation Site',
url: process.env.SITE_URL || 'http://localhost:3000',
// ... rest of config
};

For client-side access in React components, use webpack’s environment variables:

src/components/Analytics.js
export default function Analytics() {
const analyticsId = process.env.REACT_APP_ANALYTICS_ID;
return (
<script>
{`window.analyticsId = '${analyticsId}';`}
</script>
);
}

Persistent Storage

If your Docusaurus site needs to store files, user uploads, or generated content, you can use persistent volumes in Klutch.sh.

Adding Persistent Volumes

  1. In the Klutch.sh dashboard, go to your app’s Volumes section.
  2. Click Add Volume.
  3. Set the mount path (e.g., /data, /uploads, or /var/www/uploads).
  4. Set the size (e.g., 1 GiB, 5 GiB).
  5. Save and redeploy your site.

Example: Storing Generated Content

// server.js with persistent storage
const express = require('express');
const path = require('path');
const fs = require('fs');
const app = express();
const PORT = process.env.PORT || 3000;
const STORAGE_PATH = '/data'; // Mount point
// Ensure storage directory exists
if (!fs.existsSync(STORAGE_PATH)) {
fs.mkdirSync(STORAGE_PATH, { recursive: true });
}
// Serve static files from the build directory
app.use(express.static(path.join(__dirname, 'build')));
// API endpoint for storing data
app.post('/api/save', express.json(), (req, res) => {
const { filename, data } = req.body;
const filepath = path.join(STORAGE_PATH, filename);
fs.writeFile(filepath, JSON.stringify(data), (err) => {
if (err) {
res.status(500).json({ error: 'Failed to save' });
} else {
res.json({ success: true });
}
});
});
// Handle client-side routing
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});

Security Best Practices

1. HTTPS/SSL Enforcement

Klutch.sh automatically provides HTTPS for all deployed sites. Ensure your Docusaurus site redirects HTTP to HTTPS:

server.js
app.use((req, res, next) => {
if (process.env.NODE_ENV === 'production' && !req.secure) {
return res.redirect(`https://${req.get('host')}${req.url}`);
}
next();
});

2. Content Security Policy

Implement CSP headers for enhanced security:

server.js
app.use((req, res, next) => {
res.setHeader(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline' cdn.jsdelivr.net; style-src 'self' 'unsafe-inline';"
);
next();
});

3. Environment Variable Protection

Never commit sensitive data to version control. Use Klutch.sh environment variables for:

API_KEY=your-secret-key
DATABASE_URL=postgresql://...
ANALYTICS_SECRET=your-secret

4. Dependency Security

Regularly audit and update dependencies:

Terminal window
npm audit
npm audit fix
npm update

5. Input Validation

If your Docusaurus site accepts user input, validate and sanitize it:

server.js
const validator = require('validator');
app.post('/api/contact', express.json(), (req, res) => {
const { email, message } = req.body;
if (!validator.isEmail(email)) {
return res.status(400).json({ error: 'Invalid email' });
}
if (message.length > 5000) {
return res.status(400).json({ error: 'Message too long' });
}
// Process message
res.json({ success: true });
});

6. Rate Limiting

Protect API endpoints with rate limiting:

server.js
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100 // limit each IP to 100 requests per windowMs
});
app.post('/api/contact', limiter, (req, res) => {
// Handle contact form
});

7. Security Headers

Add security headers to all responses:

server.js
app.use((req, res, next) => {
res.setHeader('X-Content-Type-Options', 'nosniff');
res.setHeader('X-Frame-Options', 'DENY');
res.setHeader('X-XSS-Protection', '1; mode=block');
res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
next();
});

Monitoring and Logging

Health Check Endpoint

Implement a health check endpoint:

server.js
app.get('/health', (req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});

Structured Logging

Implement structured logging for production:

server.js
function log(level, message, data = {}) {
const entry = {
timestamp: new Date().toISOString(),
level,
message,
data
};
console.log(JSON.stringify(entry));
}
app.use((req, res, next) => {
log('info', 'Request', {
method: req.method,
path: req.path,
ip: req.ip
});
next();
});
process.on('unhandledRejection', (reason, promise) => {
log('error', 'Unhandled Rejection', { reason, promise });
});

Error Handling

Implement comprehensive error handling:

server.js
app.use((err, req, res, next) => {
log('error', 'Server Error', {
message: err.message,
stack: err.stack,
path: req.path
});
res.status(500).json({
error: 'Internal Server Error',
requestId: req.id
});
});

Custom Domains

To use a custom domain with your Klutch.sh-deployed Docusaurus site:

1. Add the Domain in Klutch.sh

In the Klutch.sh dashboard, go to your app’s settings and add your custom domain (e.g., docs.example.com).

2. Update Your DNS Provider

Update your DNS records with the CNAME provided by Klutch.sh:

CNAME: docs.example.com → example-app.klutch.sh

3. Update Docusaurus Config

Update your docusaurus.config.js with the correct URL:

const config = {
url: 'https://docs.example.com',
// ... rest of config
};

4. Wait for DNS Propagation

DNS changes can take up to 48 hours to propagate. Use tools to verify:

Terminal window
nslookup docs.example.com
# or
dig docs.example.com

Once propagated, your Docusaurus site will be accessible at your custom domain with automatic HTTPS.


Troubleshooting

Issue 1: Build Fails with “Out of Memory”

Error: JavaScript heap out of memory during build

Solutions:

  • Increase Node memory: NODE_OPTIONS=--max-old-space-size=4096 npm run build
  • Set BUILD_COMMAND in Klutch.sh: NODE_OPTIONS=--max-old-space-size=4096 npm run build
  • Reduce documentation size or split into smaller sections
  • Review Klutch.sh build logs for detailed error messages

Issue 2: Site Shows Blank Page in Production

Error: Documentation site loads but shows no content

Solutions:

  • Verify baseUrl in docusaurus.config.js matches your deployment
  • Ensure build/ directory is generated and included in deployment
  • Check browser console for JavaScript errors
  • Verify static assets are being served correctly

Issue 3: Search Not Working

Error: Search feature is unavailable or returns no results

Solutions:

  • Install search plugin: npm install @docusaurus/plugin-search-local
  • Clear browser cache and rebuild: npm run build
  • Verify search plugin is configured in docusaurus.config.js
  • Check that documentation files are properly indexed

Issue 4: Images Not Loading

Error: Images return 404 or fail to load

Solutions:

  • Place images in static/img/ directory
  • Reference images as /img/filename.png in Markdown
  • Ensure paths are relative to the site root
  • Check that static/ directory is included in git repository

Issue 5: Custom Domain HTTPS Not Working

Error: Custom domain shows certificate error or insecure connection

Solutions:

  • Verify DNS CNAME record is correctly configured
  • Wait for DNS propagation (up to 48 hours)
  • Check that domain is added in Klutch.sh dashboard
  • Ensure domain TLS certificate is installed (Klutch.sh handles this automatically)

Best Practices

1. Organize Documentation Logically

Structure your documentation for easy navigation:

docs/
├── intro.md
├── getting-started/
│ ├── intro.md
│ ├── installation.md
│ └── _category_.json
├── guides/
│ ├── _category_.json
│ ├── configuration.md
│ ├── customization.md
│ └── deployment.md
└── api/
├── _category_.json
└── reference.md

2. Use Admonitions

Add emphasis to important information:

:::note
This is a note admonition.
:::
:::tip
This is a helpful tip.
:::
:::warning
This is an important warning.
:::
:::danger
This requires caution.
:::

3. Implement Version Management

Setup versioned documentation:

Terminal window
npm run docusaurus docs:version 1.0.0

This creates separate documentation versions for different product releases.

4. Add Code Highlighting and Tabs

Use code highlighting and tabs for better documentation:

```javascript title="example.js"
const greeting = 'Hello, World!';
console.log(greeting);

import Tabs from ‘@theme/Tabs’; import TabsContent from ‘@theme/TabsContent’;

const greeting = 'Hello, World!'; greeting = 'Hello, World!' ```

5. Enable Analytics

Track documentation usage with analytics:

docusaurus.config.js
const config = {
scripts: [
{
src: 'https://www.googletagmanager.com/gtag/js?id=YOUR_GA_ID',
async: true,
},
],
// ... rest of config
};

6. Create a Blog

Share updates and announcements:

---
slug: welcome
title: Welcome to Our Documentation
authors: [you]
tags: [announcement]
---
Welcome to our new documentation site! We're excited to share...

Replace local search with Algolia for better performance:

docusaurus.config.js
algolia: {
appId: 'YOUR_APP_ID',
apiKey: 'YOUR_SEARCH_KEY',
indexName: 'your_index',
contextualSearch: true,
}

8. Keep Dependencies Updated

Regularly update Docusaurus and related packages:

Terminal window
npm update
npm audit fix
npx docusaurus --version

9. Test Documentation Locally

Always test changes before deployment:

Terminal window
npm run build
npm run serve
# Visit http://localhost:3000 and verify

10. Document Your Documentation

Create a guide for contributors:

# Contributing Guide
## How to Add Documentation
1. Create a new .md file in the appropriate section
2. Add frontmatter with title and description
3. Write content using Markdown
4. Test locally with `npm run start`
5. Submit a pull request

Verifying Your Deployment

After deployment completes:

  1. Check the Site URL: Visit your site at https://example-app.klutch.sh or your custom domain.
  2. Verify All Pages Load: Navigate through your documentation to ensure all pages render.
  3. Check Search Functionality: Verify the search feature works correctly.
  4. Test Navigation: Ensure all links and navigation work as expected.
  5. Check Performance: Use Google PageSpeed Insights to verify performance.
  6. Review Browser Console: Open the browser console (F12) and verify no errors are present.
  7. Review Logs: Check the Klutch.sh dashboard logs for any runtime errors.

If your site doesn’t work as expected, review the troubleshooting section and check the logs in the Klutch.sh dashboard.


External Resources


Deploying a Docusaurus documentation site to Klutch.sh is straightforward with Nixpacks for automatic deployment or Docker for fine-grained control. By following this guide, you’ve learned how to scaffold a Docusaurus project, structure documentation content, customize branding and styling, configure search and versioning, set up multi-language support, implement security best practices, set up monitoring, and troubleshoot common issues. Your Docusaurus site is now running on Klutch.sh’s global infrastructure with automatic HTTPS, custom domain support, and production-grade performance. For additional help or questions, consult the official Docusaurus documentation or contact Klutch.sh support.