🚧 VeloxKit is pre-release software. APIs may change before v1.0. Get started →
Documentation
Getting Started
Docker Setup

Docker Setup

Run the VeloxKit docs site in a container for a reproducible local preview, CI builds, or a static-site deployment — without polluting your host with Node, Bun, or pnpm versions.

Docker is optional. Use it if you want hermetic builds, CI parity, or a static export you can host on any static provider. For day-to-day docs work, the local npm run dev flow in the Installation page is faster.

What's Included

FilePurpose
DockerfileMulti-stage build: depsbuilderrunner (Next.js standalone) and static (nginx)
docker-compose.ymlLocal stack with three profiles: default preview, dev (hot reload), static (nginx)
.dockerignoreExcludes .git, node_modules, .next, secrets
docker/nginx.confHardened nginx config for the static stage

Quick Start

Build and run the production preview

docker build -t veloxkit-docs .
docker run --rm -p 3000:3000 veloxkit-docs

Open http://localhost:3000 (opens in a new tab) in your browser.

Or use Compose

docker compose up docs

Same port, same image, same behaviour. Compose also defines dev and static profiles.

The Three Targets

Stage 3 — runner

A minimal Node image that runs Next.js's standalone server.

docker build --target runner -t veloxkit-docs .
docker run --rm -p 3000:3000 veloxkit-docs
  • Image size: ~180 MB
  • Cold start: ~600 ms
  • Best for: production preview, internal hosting

Build Matrix

GoalCommandImage sizeCold start
Local dev (hot reload)docker compose --profile dev up~250 MBn/a
Local prod previewdocker compose up docs~180 MB~600 ms
Static exportdocker compose --profile static up~25 MB~80 ms
CI build (Next.js standalone)docker build --target runner .~180 MB~600 ms
CI build (static)docker build --target static .~25 MB~80 ms

Switching Package Managers

The Dockerfile autodetects the lockfile at build time:

  • bun.lock / bun.lockbBun is installed globally and used.
  • pnpm-lock.yamlpnpm via corepack.
  • yarn.lockYarn via corepack.
  • Otherwise → npm.

This means the Dockerfile works whether your team uses bun, pnpm, yarn, or npm. No edits needed.

Health Check

The runner stage exposes a health endpoint at /api/health. The Compose file wires docker compose ps to show a healthy state once Nextra boots.

If you do not have a /api/health route yet, add this to pages/api/health.ts:

import type { NextApiRequest, NextApiResponse } from 'next'
 
export default function handler(_req: NextApiRequest, res: NextApiResponse) {
  res.status(200).json({ ok: true, ts: Date.now() })
}

CI Recipes

GitHub Actions — build and push to GHCR

name: docker
 
on:
  push:
    branches: [main]
 
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: actions/checkout@v4
      - uses: docker/setup-buildx-action@v3
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v6
        with:
          context: .
          target: static
          push: true
          tags: ghcr.io/${{ github.repository }}:latest
          cache-from: type=gha
          cache-to:   type=gha,mode=max

The static target is the right pick for a docs site — small, fast, deployable to any static host.

Vercel

You can keep using Vercel. The Dockerfile is for cases when you want a fully self-hosted preview or a static export to a non-Vercel provider.

If you switch to static export and host on Vercel, set output: 'export' in next.config.js and the existing Vercel flow picks it up automatically.

Troubleshooting

Port 3000 is already in use

Change the host port in docker-compose.yml or the run command:

docker run --rm -p 4000:3000 veloxkit-docs

Hot reload not picking up changes

On Linux, ensure your user can watch files inside the container. Either:

  • Use Docker Desktop with the new develop.watch (already configured in docker-compose.yml).
  • Or run the dev server on the host (npm run dev).

"Cannot find module 'next'"

The volume mount may have wiped the image's node_modules. Re-run:

docker compose --profile dev down -v
docker compose --profile dev up

Static export fails

Nextra's static export requires the site to be a pure static site — no API routes, no server components, no ISR. If the export fails, fall back to the runner target and host the Next.js server.

Operational Notes

  • The runner stage runs as the unprivileged node user that the official node:20-bookworm-slim image ships with.
  • The static stage uses the official nginx:1.27-alpine and is non-root by default.
  • NEXT_TELEMETRY_DISABLED=1 is set in every stage that runs the Next.js build, so you do not accidentally opt into Next.js telemetry.
  • The static stage adds minimal security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy) but does not enforce HTTPS — terminate TLS at your edge or load balancer.

Verifying the Build

# 1. Build should succeed
docker build -t veloxkit-docs:test .
 
# 2. Health check should return 200
docker run --rm -d --name vk-test -p 3000:3000 veloxkit-docs:test
sleep 5
docker exec vk-test node -e "require('http').get('http://127.0.0.1:3000/api/health',r=>console.log('status',r.statusCode)).on('error',e)=>console.error(e))"
docker stop vk-test

If both pass, your Dockerfile is healthy and the docs site is reproducible.