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:
npx create-docusaurus@latest my-docusaurus-site classiccd my-docusaurus-sitenpm install2. 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.md3. Run the Development Server
Start the Docusaurus development server:
npm run startNavigate 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-startedtitle: Getting Starteddescription: Learn how to get started with our productsidebar_position: 1---
# Getting Started
Welcome to our documentation! This guide will help you get up and running quickly.
## Installation
Install the package using npm:
```bashnpm install @mycompany/mypackageBasic 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.jsimport 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:
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:
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:
const express = require('express');const path = require('path');
const app = express();const PORT = process.env.PORT || 3000;
// Serve static files from the build directoryapp.use(express.static(path.join(__dirname, 'build')));
// Handle client-side routingapp.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:
npm install expressnpm run buildnpm run serveVisit 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.jsonwith build and start scripts - No
Dockerfilein the repository root (if one exists, Klutch.sh will use Docker instead)
Steps to Deploy with Nixpacks
-
Push Your Docusaurus Project to GitHub
Initialize and push your project to GitHub if you haven’t already:
Terminal window git initgit add .git commit -m "Initial Docusaurus site"git branch -M maingit remote add origin git@github.com:YOUR_USERNAME/YOUR_REPO.gitgit push -u origin main -
Log In to Klutch.sh Dashboard
Go to klutch.sh/app and sign in with your GitHub account.
-
Create a Project
Navigate to the Projects section and create a new project for your Docusaurus site.
-
Create an App
Click “Create App” and select your GitHub repository.
-
Select the Branch
Choose the branch you want to deploy (typically
main). -
Configure Traffic Type
Select HTTP as the traffic type for Docusaurus (a static site generator serving HTML/assets).
-
Set the Internal Port
Set the internal port to
3000– this is the port where your Node.js production server listens. -
Add Environment Variables (Optional)
Add any environment variables your Docusaurus site requires:
NODE_ENV=productionSITE_URL=https://example-app.klutch.shIf 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)
-
Configure Compute Resources
Select your region, compute size, and number of instances based on expected traffic.
-
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 ./buildCOPY --from=builder /app/server.js ./server.js
ENV NODE_ENV=productionENV PORT=3000EXPOSE 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 routingRUN 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
PORTenvironment variable is set to3000for Node.js or80for Nginx. - Multi-stage build: Reduces final image size by excluding build tools and dev dependencies from the runtime container.
Steps to Deploy with Docker
-
Create a Dockerfile
Add the Dockerfile (shown above) to the root of your Docusaurus repository.
-
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:latestVisit http://localhost:3000 to verify.
-
Push to GitHub
Commit and push the Dockerfile and your code:
Terminal window git add Dockerfilegit commit -m "Add Dockerfile for production deployment"git push origin main -
Create an App in Klutch.sh
Go to klutch.sh/app, navigate to “Create App”, and select your repository.
-
Configure the App
- Traffic Type: Select HTTP
- Internal Port: Set to
3000(or80if using the Nginx Dockerfile) - Environment Variables: Add any required runtime variables
-
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=productionSITE_URL=https://example-app.klutch.shANALYTICS_ID=your-analytics-idGTM_ID=your-gtm-idAccessing 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:
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
- In the Klutch.sh dashboard, go to your app’s Volumes section.
- Click Add Volume.
- Set the mount path (e.g.,
/data,/uploads, or/var/www/uploads). - Set the size (e.g.,
1 GiB,5 GiB). - Save and redeploy your site.
Example: Storing Generated Content
// server.js with persistent storageconst 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 existsif (!fs.existsSync(STORAGE_PATH)) { fs.mkdirSync(STORAGE_PATH, { recursive: true });}
// Serve static files from the build directoryapp.use(express.static(path.join(__dirname, 'build')));
// API endpoint for storing dataapp.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 routingapp.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:
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:
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-keyDATABASE_URL=postgresql://...ANALYTICS_SECRET=your-secret4. Dependency Security
Regularly audit and update dependencies:
npm auditnpm audit fixnpm update5. Input Validation
If your Docusaurus site accepts user input, validate and sanitize it:
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:
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:
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:
app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime() });});Structured Logging
Implement structured logging for production:
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:
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.sh3. 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:
nslookup docs.example.com# ordig docs.example.comOnce 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_COMMANDin 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
baseUrlindocusaurus.config.jsmatches 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.pngin 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.md2. Use Admonitions
Add emphasis to important information:
:::noteThis is a note admonition.:::
:::tipThis is a helpful tip.:::
:::warningThis is an important warning.:::
:::dangerThis requires caution.:::3. Implement Version Management
Setup versioned documentation:
npm run docusaurus docs:version 1.0.0This 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’;
5. Enable Analytics
Track documentation usage with analytics:
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: welcometitle: Welcome to Our Documentationauthors: [you]tags: [announcement]---
Welcome to our new documentation site! We're excited to share...7. Setup Algolia Search
Replace local search with Algolia for better performance:
algolia: { appId: 'YOUR_APP_ID', apiKey: 'YOUR_SEARCH_KEY', indexName: 'your_index', contextualSearch: true,}8. Keep Dependencies Updated
Regularly update Docusaurus and related packages:
npm updatenpm audit fixnpx docusaurus --version9. Test Documentation Locally
Always test changes before deployment:
npm run buildnpm run serve# Visit http://localhost:3000 and verify10. Document Your Documentation
Create a guide for contributors:
# Contributing Guide
## How to Add Documentation
1. Create a new .md file in the appropriate section2. Add frontmatter with title and description3. Write content using Markdown4. Test locally with `npm run start`5. Submit a pull requestVerifying Your Deployment
After deployment completes:
- Check the Site URL: Visit your site at
https://example-app.klutch.shor your custom domain. - Verify All Pages Load: Navigate through your documentation to ensure all pages render.
- Check Search Functionality: Verify the search feature works correctly.
- Test Navigation: Ensure all links and navigation work as expected.
- Check Performance: Use Google PageSpeed Insights to verify performance.
- Review Browser Console: Open the browser console (F12) and verify no errors are present.
- 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
- Official Docusaurus Documentation
- Markdown Features Guide
- Styling and Layout Guide
- Klutch.sh Official Website
- Node.js Documentation
- Express.js Documentation
- Web Security Documentation
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.