Skip to content

Deploy a VitePress site

Publish a VitePress site on your own domain, from your computer or from GitHub Actions.

Build command npm run docs:build
Output folder docs/.vitepress/dist (or .vitepress/dist if your site is at the project root)
Settings you need None; sitemap.hostname if you want a sitemap
Routing The default links end in .html and work as they are. With cleanUrls: true, deploy with --clean-urls

These steps assume the layout npx vitepress init creates: the site in docs/, and the scripts docs:dev, docs:build and docs:preview in package.json.

You need:

  • A VitePress site that builds on your computer with npm run docs:build.
  • An Evolving Edge account that can deploy workloads. See Getting Started.
  • A project in the portal, and its project ID (it starts with proj_).
  • A deploy token from the portal, under Workload → Deploy Tokens, with workload:write. Scope it to the project. See Deploy tokens.
  • The domain you want to serve the site on, for example docs.example.com, and access to its DNS settings.
  • Node.js 22.12 or later, on Windows (x64), macOS, or Linux (x64 or arm64).

Nothing is required: the site is served from the root of the domain, so leave base at its default (/).

Two optional settings:

docs/.vitepress/config.mjs
export default {
cleanUrls: true, // links without .html; deploy with --clean-urls
sitemap: { hostname: 'https://docs.example.com' }, // writes sitemap.xml with absolute URLs
};
  • cleanUrls. By default VitePress writes guide/intro.html and links to /guide/intro.html, and nothing more is needed. With cleanUrls: true it still writes guide/intro.html but links to /guide/intro. Deploy with --clean-urls so those links find their pages.
  • sitemap.hostname. Without it, VitePress writes no sitemap.

Only the project that claimed a domain can deploy to it, so claim it before your first deploy. In the portal, or from the command line:

Terminal window
export EE_CDN_TOKEN=<your deploy token>
npx -p @evolving-edge/ee-cdn-cli ee-domain claim docs.example.com --project proj_abc123 --wait

It prints a TXT record and a CNAME record to add at your DNS provider, then waits until the domain is active. See Custom domains for the portal steps, root domains and Cloudflare.

Terminal window
npm run docs:build
npx @evolving-edge/ee-cdn-cli docs/.vitepress/dist --domain docs.example.com --project proj_abc123
  • Add --clean-urls if you set cleanUrls: true.
  • ee-deploy finds docs/.vitepress/dist by itself if you leave the folder out.
  • Each deploy replaces the whole site on that domain. Allow up to about 90 seconds for a new deploy to reach every node.

In your repository settings, add:

  • a secret EE_CDN_TOKEN holding your deploy token
  • variables EE_DOMAIN (for example docs.example.com) and EE_CDN_PROJECT_ID

Then create .github/workflows/deploy.yml:

name: Deploy to Evolving Edge
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ee-deploy
cancel-in-progress: false
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # only needed if lastUpdated is on
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
- name: Cache VitePress
uses: actions/cache@v4
with:
path: docs/.vitepress/cache
key: ${{ runner.os }}-vitepress-${{ hashFiles('package-lock.json') }}
restore-keys: ${{ runner.os }}-vitepress-
- run: npm ci
- run: npm run docs:build
- name: Deploy
env:
EE_CDN_TOKEN: ${{ secrets.EE_CDN_TOKEN }}
EE_CDN_PROJECT_ID: ${{ vars.EE_CDN_PROJECT_ID }}
# Add --clean-urls if you set cleanUrls: true.
run: npx --yes @evolving-edge/ee-cdn-cli docs/.vitepress/dist --domain ${{ vars.EE_DOMAIN }}

VitePress copies everything in docs/public/ to the site root, so that’s where _redirects and _headers go.

Redirects. Add a _redirects file at docs/public/_redirects:

/guide/old-page.html /guide/new-page.html 301
/v1/* /guide/:splat 301

Rules can be 301 or 302, and * and :splat wildcards work. Redirects apply to public (Level 0) sites.

404 page. VitePress always builds a 404.html, and Evolving Edge serves it with a 404 status for any address that doesn’t exist. To change it, set notFound in the theme config or add a NotFound layout. See Custom 404 pages.

Caching. HTML pages are checked on every request, so a new deploy shows as soon as it reaches the nodes. Other files are cached for an hour. VitePress names everything in /assets/ by content hash, so you can cache that folder for a year with docs/public/_headers:

/assets/*
Cache-Control: public, max-age=31536000, immutable

Check first that nothing in docs/public/assets/ is copied in without a hash. See Caching hashed assets for a year.

  • Subfolder deploys. A site owns its whole domain, so base stays /.
  • Deploy previews for each pull request. To preview, deploy to a second domain.
What you see Why What to do
Links work in the menu, but a refreshed page is a 404 cleanUrls: true without --clean-urls Deploy with --clean-urls
Styles and scripts are 404s base is set to a subfolder Remove base, or set it to /
No sitemap.xml sitemap.hostname isn’t set Add it to the config
The deploy is refused for your domain The domain isn’t claimed for this project Claim it first
No deploy token EE_CDN_TOKEN isn’t set Export it, or add the repository secret
An authorisation error on upload The token is scoped to a project and no project was given Pass --project (or set EE_CDN_PROJECT_ID)

More in the command line troubleshooting.