|Docs

Host a Documentation Site with Astro Starlight

documentationastrostaticdeployment

Starlight is a documentation theme built on Astro. It turns a folder of Markdown files into a complete documentation site: navigation sidebar, full-text search, dark mode, mobile layout, and internationalization are all built in. The output is static HTML, so hosting it requires nothing more than a static file server.

That simplicity still leaves the documentation-specific work: configuring the sidebar, getting search working, deploying the static output to Railway, and putting the site behind your own domain. This guide covers each one; for general Astro deployment patterns (SSR, Dockerfiles, one-click templates), see the Astro guide.

Create a Starlight project

You need Node.js installed. Scaffold a new project with the Starlight template:

npm create astro@latest my-docs -- --template starlight

Accept the defaults when prompted, then start the dev server:

cd my-docs
npm run dev

Open http://localhost:4321 to see the site. Documentation pages live in src/content/docs/ as Markdown or MDX files. Each file becomes a route: src/content/docs/guides/install.md is served at /guides/install.

Every page needs a title in its frontmatter:

---
title: Installation
description: How to install the CLI.
---

Install the CLI with npm:

...

Configure the sidebar

The sidebar is defined in astro.config.mjs. You can list pages explicitly, or point a group at a directory and let Starlight generate the entries from the files it finds.

// astro.config.mjs
import { defineConfig } from "astro/config";
import starlight from "@astrojs/starlight";

export default defineConfig({
  integrations: [
    starlight({
      title: "My Product Docs",
      sidebar: [
        {
          label: "Getting Started",
          items: [
            { label: "Installation", slug: "guides/install" },
            { label: "Quick Start", slug: "guides/quick-start" },
          ],
        },
        {
          label: "Reference",
          // Generate entries from every file in src/content/docs/reference/
          items: [{ autogenerate: { directory: "reference" } }],
        },
      ],
    }),
  ],
});

Explicit items give you full control over order and labels. autogenerate trades that control for less work as the docs grow: new files under the directory appear in the sidebar automatically, ordered by filename. Most docs sites split the difference: explicit entries for the getting-started path, autogeneration for reference sections.

Search works without a server

Starlight ships with Pagefind, a static search library. When you run astro build, Pagefind indexes the rendered HTML and writes the index into the output directory. Search then runs entirely in the visitor's browser, with no search server to deploy and no per-query cost.

Two things to know:

  • Search does not appear in npm run dev. The index only exists after a build. To test search locally, run npm run build && npm run preview.
  • The index is rebuilt on every deploy, so search results match the published content with nothing to invalidate or resync.

Deploy to Railway

Starlight produces static files, so the deployed service is just a file server in front of the dist/ directory. Add the serve package to handle that:

npm install serve

Then add a start script to package.json:

{
  "scripts": {
    "dev": "astro dev",
    "build": "astro build",
    "preview": "astro preview",
    "start": "serve dist -l $PORT"
  }
}

Railway injects the PORT environment variable at runtime, and serve listens on it. serve resolves clean URLs to the folder/index.html files Starlight generates, so /guides/install works without any rewrite rules.

Push the project to GitHub, then:

  1. Go to railway.com/new and select Deploy from GitHub repo.
  2. Choose your repository. Railway detects a Node.js project, runs npm run build, and starts the service with npm start.
  3. In the service settings, under Networking, click Generate Domain to get a public *.up.railway.app URL.

Every push to the connected branch now rebuilds the site, including the search index, and deploys the new version.

Add a custom domain

Documentation usually lives at a subdomain like docs.example.com:

  1. Open your service, go to Settings, and find the Networking section.
  2. Click + Custom Domain and enter docs.example.com.
  3. Railway shows two records: a CNAME record and a TXT record that verifies domain ownership. Add both at your DNS provider; the domain will not verify with only the CNAME in place.
  4. Once both records are in place and verified, Railway provisions and renews the TLS certificate automatically.

See working with domains for provider-specific DNS details, including the Cloudflare proxying caveats.

Keep hosting costs near zero

Railway pricing is usage based, and a static file server uses very little. Two settings help for a low-traffic docs site:

  • Serverless: enable it in the service settings and Railway puts the service to sleep after 10 minutes without outbound traffic, then wakes it on the next request. A static server makes no database connections or background calls, so it sleeps reliably. The first request after sleep pays a short cold-start delay. See serverless.
  • PR environments: enable them in your project settings and every pull request gets its own preview deployment of the docs, torn down when the PR closes. Reviewers can read the rendered pages instead of raw Markdown diffs. See environments.

Next steps