Skip to content

MkDocs + Vercel

I wasn't satisfied with any of the existing guides for deploying an MkDocs site to Vercel, so I wrote my own.

This method uses GitHub Actions to build and deploy your site via the Vercel CLI.

Prerequisites

Configuring your project

First and foremost, you'll need to install the Vercel CLI on your own machine.

npm i -g vercel
yarn global add vercel
pnpm i -g vercel
bun add -g vercel

Connect it to your Vercel account:

vercel login

Then switch into your project's directory and link it to Vercel:

vercel link

After following the interactive prompts, a .vercel folder will appear in your project's directory. Inside that folder is a project.json file with a projectId property and an orgId property. Add the values of these properties as secrets in your project's GitHub repository under the names VERCEL_PROJECT_ID and VERCEL_ORG_ID, respectively.

You can do this on GitHub.com or with the GitHub CLI:

gh secret set VERCEL_PROJECT_ID
gh secret set VERCEL_ORG_ID # (1)!
  1. You'll be interactively prompted for the respective secret after running each command.

Getting a token

You'll need to generate a Vercel account token so the Vercel CLI can access your account from within GitHub Actions. Head over to your Vercel account settings, choose a name, appropriate scope, and expiration date for your token, and click Create. Copy the token and set it as a repository secret under the name VERCEL_TOKEN.

Configuring vercel.json

It's optional, but strongly recommended, that you put a vercel.json file in your docs folder with at least the following contents:

{
  "trailingSlash": true
}
Why?

Relative internal links on your site will break if the request URL does not have a trailing slash. This isn't a problem on MkDocs' de facto standard hosting platform, GitHub Pages, which automatically enforces trailing slashes, so the overwhelming majority of public MkDocs sites do not exhibit this issue. Yours will, though, if you deploy it on Vercel without configuring vercel.json to enforce trailing slashes.

Writing the workflow

Finally, you need to write a GitHub Actions workflow that builds your documentation and deploys it to Vercel. There's no one-size-fits-all solution here, but here are some basic examples for commonly-used package managers.

name: Deploy Documentation

on:
  push:
    paths:
      - docs/**
      - mkdocs.yml
      - requirements.txt
      - .github/workflows/docs.yml

  workflow_dispatch:

jobs:
  deploy:
    name: Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Set Up Python
        uses: actions/setup-python@v5

      - name: Install Dependencies
        run: pip install -r requirements.txt

      - name: Build Documentation
        run: mkdocs build

      - name: Deploy to Vercel
        uses: npx vercel --token ${{ secrets.VERCEL_TOKEN }} # (1)!
        working-directory: site
        env:  # (2)!
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
  1. To create a production deployment, add the --prod flag:

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} --prod
    

    To emulate Vercel's branch-dependent creation of preview and production deployments, you can do something like this (replacing main with your production branch if necessary):

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} ${{ github.ref_name == 'main' && '--prod' ||'' }}
    
  2. If you don't want the Vercel CLI to send telemetry, you can add VERCEL_DISABLE_TELEMETRY: 1 to the env: key:

    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
      VERCEL_DISABLE_TELEMETRY: 1
    
name: Deploy Documentation

on:
  push:
    paths:
      - docs/**
      - mkdocs.yml
      - pyproject.toml
      - .github/workflows/docs.yml

  workflow_dispatch:

jobs:
  deploy:
    name: Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Set Up uv
        uses: astral-sh/setup-uv@v3

      - name: Install Dependencies
        run: uv sync # (2)!

      - name: Build Documentation
        run: uv run mkdocs build

      - name: Deploy to Vercel
        uses: npx vercel --token ${{ secrets.VERCEL_TOKEN }} # (1)!
        working-directory: site
        env:  # (3)!
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
  1. To create a production deployment, add the --prod flag:

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} --prod
    

    To emulate Vercel's branch-dependent creation of preview and production deployments, you can do something like this (replacing main with your production branch if necessary):

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} ${{ github.ref_name == 'main' && '--prod' ||'' }}
    
  2. If you have a seperate dependency group for your documentation, you might instead do something like:

    uv sync --only-group docs
    
  3. If you don't want the Vercel CLI to send telemetry, you can add VERCEL_DISABLE_TELEMETRY: 1 to the env: key:

    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
      VERCEL_DISABLE_TELEMETRY: 1
    
name: Deploy Documentation

on:
  push:
    paths:
      - docs/**
      - mkdocs.yml
      - pyproject.toml
      - .github/workflows/docs.yml

  workflow_dispatch:

jobs:
  deploy:
    name: Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Set Up Python
        uses: actions/setup-python@v5

      - name: Install Poetry
        run: pipx install poetry

      - name: Install Dependencies
        run: poetry install # (2)!

      - name: Build Documentation
        run: poetry run mkdocs build

      - name: Deploy to Vercel
        uses: npx vercel --token ${{ secrets.VERCEL_TOKEN }} # (1)!
        working-directory: site
        env:  # (3)!
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
  1. To create a production deployment, add the --prod flag:

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} --prod
    

    To emulate Vercel's branch-dependent creation of preview and production deployments, you can do something like this (replacing main with your production branch if necessary):

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} ${{ github.ref_name == 'main' && '--prod' ||'' }}
    
  2. If you have a seperate dependency group for your documentation, you might instead do something like:

    poetry install --only docs
    
  3. If you don't want the Vercel CLI to send telemetry, you can add VERCEL_DISABLE_TELEMETRY: 1 to the env: key:

    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
      VERCEL_DISABLE_TELEMETRY: 1
    
name: Deploy Documentation

on:
  push:
    paths:
      - docs/**
      - mkdocs.yml
      - pyproject.toml
      - .github/workflows/docs.yml

  workflow_dispatch:

jobs:
  deploy:
    name: Deploy Documentation
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4

      - name: Set Up PDM
        uses: pdm-project/setup-pdm@v4

      - name: Install Dependencies
        run: pdm sync # (2)!

      - name: Build Documentation
        run: pdm run mkdocs build

      - name: Deploy to Vercel
        uses: npx vercel --token ${{ secrets.VERCEL_TOKEN }} # (1)!
        working-directory: site
        env:  # (3)!
          VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
          VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
  1. To create a production deployment, add the --prod flag:

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} --prod
    

    To emulate Vercel's branch-dependent creation of preview and production deployments, you can do something like this (replacing main with your production branch if necessary):

    npx vercel --token ${{ secrets.VERCEL_TOKEN }} ${{ github.ref_name == 'main' && '--prod' ||'' }}
    
  2. If you have a seperate dependency group for your documentation, you might instead do something like:

    pdm sync --group docs
    
  3. If you don't want the Vercel CLI to send telemetry, you can add VERCEL_DISABLE_TELEMETRY: 1 to the env: key:

    env:
      VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }}
      VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }}
      VERCEL_DISABLE_TELEMETRY: 1
    

You can also check out the workflow that deploys this very website.