Skip to content

Deploy Astro Starlight on Docker

This guide documents the deployment of this documentation site at docs.smilepowered.org using Astro Starlight, Docker, and Traefik.

  • Debian server with Docker and Docker Compose installed
  • Traefik reverse proxy running
  • DNS A record pointing to your server IP
┌─────────────────┐
│ Traefik │ Port 80/443
│ (reverse │ SSL via Let's Encrypt
│ proxy) │
└────────┬────────┘
┌─────────────────┐
│ nginx:alpine │ Serves static files
│ (docs) │ from /dist
└─────────────────┘
/opt/docker/docs/
├── compose.yaml # nginx serving dist/
├── build.sh # docker run node:22-alpine build
├── package.json
├── astro.config.mjs
├── src/
│ ├── content.config.ts
│ └── content/docs/
│ └── *.mdx
├── public/
│ └── downloads/
└── dist/ # generated static site

Add an A record pointing to your server:

Type: A
Name: docs
Value: <your_server_ip>

Verify DNS propagation:

Terminal window
dig +short docs.<your_domain>.com A
Terminal window
mkdir -p /opt/docker/docs/src/content/docs
mkdir -p /opt/docker/docs/public/downloads

Create /opt/docker/docs/package.json:

{
"name": "astro-docs-debian",
"type": "module",
"version": "0.0.1",
"scripts": {
"dev": "astro dev",
"start": "astro dev",
"build": "astro build",
"preview": "astro preview",
"astro": "astro"
},
"dependencies": {
"astro": "^6.0.0",
"@astrojs/starlight": "^0.38.0"
}
}

Create /opt/docker/docs/astro.config.mjs:

import { defineConfig } from 'astro/config';
import starlight from '@astrojs/starlight';
export default defineConfig({
site: 'https://docs.<your_domain>.com',
integrations: [
starlight({
title: 'Astro docs on Debian',
}),
],
});

Create /opt/docker/docs/src/content.config.ts:

import { defineCollection } from 'astro:content';
import { docsLoader } from '@astrojs/starlight/loaders';
import { schema } from '@astrojs/starlight/schema';
export const collections = {
docs: defineCollection({ loader: docsLoader(), schema }),
};

Create /opt/docker/docs/build.sh:

#!/bin/sh
docker run --rm \
-v "$(dirname "$0")":/app \
-w /app \
node:22-alpine \
sh -c "npm install && npm run build"

Make it executable:

Terminal window
chmod +x /opt/docker/docs/build.sh

Create /opt/docker/docs/src/content/docs/index.mdx:

---
title: Your Site Title
description: Site description
---
Welcome to the documentation.
Terminal window
cd /opt/docker/docs
./build.sh

This pulls node:22-alpine and generates the dist/ folder.

Create /opt/docker/docs/compose.yaml:

services:
docs:
image: nginx:alpine
container_name: docs
restart: unless-stopped
volumes:
- ./dist:/usr/share/nginx/html:ro
labels:
- "traefik.enable=true"
- "traefik.http.routers.docs.rule=Host(`docs.<your_domain>.com`)"
- "traefik.http.routers.docs.entrypoints=websecure"
- "traefik.http.routers.docs.tls.certresolver=letsencrypt"
- "traefik.http.services.docs.loadbalancer.server.port=80"
networks:
- proxy
deploy:
resources:
limits:
memory: 64M
networks:
proxy:
external: true
Terminal window
cd /opt/docker/docs
docker compose up -d

Visit https://docs.<your_domain>.com in your browser.

  1. Create new .mdx file in src/content/docs/
  2. Run ./build.sh
  3. nginx serves updated content automatically
  1. Copy files to public/downloads/
  2. Run ./build.sh
  3. Link to files: /downloads/filename.ext

Astro 6 requires src/content.config.ts (not src/content/config.ts). Ensure you’re using the new format with docsLoader().

Verify dist/ folder exists and contains index.html.

Check Traefik logs:

Terminal window
docker logs traefik

Ensure DNS is properly configured and propagating.