VitePress is the static site generator behind the Vue and Vite documentation sites, and it has quietly become my default recommendation for project docs: Markdown in, fast static site out, with a default theme that looks professional before you touch a line of CSS. Deploying it is simple — but there are three or four specifics (the build output path, dead-link failures, clean URLs) that account for basically every failed first deploy I've seen. Let's do it properly.
Scaffolding the site
VitePress needs Node 18 or newer. In a new or existing repo:
npm add -D vitepress
npx vitepress initThe init wizard asks where to scaffold. The common convention — and the one I'd use — is ./docs, which keeps documentation alongside your code without polluting the repo root. The wizard writes a config file and adds scripts to your package.json:
{
"scripts": {
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:preview": "vitepress preview docs"
}
}Run npm run docs:dev and you have a hot-reloading docs site locally. Write your Markdown under docs/, structure the sidebar in the config, and you're most of the way there.
The build — and where the output actually goes
This is the number-one deploy mistake with VitePress, so let's be precise:
npm run docs:buildThe output lands in docs/.vitepress/dist — inside a hidden directory, inside your docs folder. Not dist/ at the repo root, not docs/dist, not build/. If you scaffolded into the repo root instead of docs/, it's .vitepress/dist. Every static host asks you for a "publish directory," and pointing it at the wrong one gives you either a 404 site or a site serving your raw Markdown.
Verify the production build locally before you ever push:
npm run docs:previewThis serves the built output (not the dev server) on localhost so you see exactly what will ship.
The gotchas worth knowing up front
The build fails on dead links — on purpose
vitepress build errors out if any internal link points at a page that doesn't exist. This surprises people whose dev server worked fine, because the dev server doesn't enforce it. It's a feature: broken links in docs erode trust faster than almost anything else. Fix the links. If you're migrating a big legacy docs set and need to ship incrementally, you can temporarily set this in docs/.vitepress/config.mts:
export default defineConfig({
ignoreDeadLinks: true // temporary migration crutch — remove it
})But treat that as debt, not configuration.
base — almost certainly leave it alone
The base option exists for hosting under a subpath, like https://user.github.io/repo/. If you're deploying to a root domain or subdomain — docs.yourproject.com — the default / is correct and setting base will break every asset URL. Only touch it if your site genuinely lives under a path prefix.
Clean URLs
By default VitePress generates getting-started.html and links to it with the .html extension. Setting cleanUrls: true generates extensionless links (/getting-started) — but that requires the host to serve /getting-started from getting-started.html. Most modern static hosts handle this; if you enable it and see 404s on navigation-by-URL, that's why. Test with docs:preview and then verify once deployed.
lastUpdated needs git history
lastUpdated: true shows a "last updated" timestamp per page, derived from git commit times. If a CI environment builds from a shallow clone, those timestamps come out missing or wrong. If your dates look off in production but fine locally, shallow clone depth is the first thing to check.
Two free upgrades: search and sitemap
Two config additions that cost nothing and make the site feel finished:
export default defineConfig({
title: 'My Project',
sitemap: {
hostname: 'https://docs.yourproject.com'
},
themeConfig: {
search: {
provider: 'local'
}
}
})search.provider: 'local' gives you full-text search built entirely at build time — no external search service, no API keys, no backend. The index ships as static assets. For the large majority of docs sites this is all the search you need. The sitemap option emits sitemap.xml at the root, which you want before submitting the docs to Google Search Console.
Deploying on PandaStack
VitePress compiles to plain static files, so this deploys as a static site — no container, no server process, nothing to keep warm.
- 1Connect the repo from the dashboard as a static site. PandaStack auto-detects the framework and build commands — VitePress is one of the generators it's verified against — but always sanity-check the two values that matter:
- Build command: npm run docs:build
- Output directory: docs/.vitepress/dist
- 1Pick your package manager if it isn't npm. The install command is overridable, and npm, yarn, pnpm, and bun are all supported — if your lockfile is
pnpm-lock.yaml, set the install command to match rather than letting a mismatched installer produce a subtly different dependency tree. - 2Push. The build runs in an isolated microVM, and the build logs stream live — if the dead-link check fails, you'll see exactly which link and which file in the log output rather than a generic "build failed."
- 3Add your domain. Point
docs.yourproject.comat the site in the app settings; SSL certificates are provisioned automatically.
From here the workflow is just git. Merge a docs PR, the site rebuilds and goes live. Deployment history is kept (10 days on the free tier, longer on paid plans), so if someone merges a docs change that breaks the sidebar, rolling back is a click rather than a revert-commit fire drill.
On sizing: the free tier includes 5 static websites, 100 GB of bandwidth, and 300 build-pipeline minutes a month. A VitePress build is fast — even a few hundred pages typically builds in well under a couple of minutes — so 300 minutes covers a lot of docs pushes. Pro ($15/mo) lifts you to unlimited static sites and 1000 build minutes if you're hosting docs for several projects.
No database is involved in any of this, which is exactly how docs should be. That said, if the project the docs describe is an app with a Postgres behind it, it's convenient that the same platform runs container apps with the database auto-wired via DATABASE_URL — docs and app in one place, one deploy workflow.
References
- VitePress deployment guide: https://vitepress.dev/guide/deploy
- Site config reference: https://vitepress.dev/reference/site-config
- Default theme search: https://vitepress.dev/reference/default-theme-search
Scaffold the site, get the output directory right, and the rest is Markdown. If you want the hosting side done in one push, try it at https://pandastack.io.