Handbook Development

Development environment and processes for maintaining the handbook

Note: You can edit this page in the docsy-gitlab theme project. Changes require bumping up the theme version and then pushing it (automation is tracked in an open issue).

The handbook site uses Hugo for static page generation from Markdown.

The handbook uses a base theme called Docsy. A custom theme override is located in the Docsy GitLab Theme project and automatically included in the handbook setup.


For help and support with the development environment, please reach out in the public #handbook Slack channel.

If you encounter a problem or bug, please open an issue or MR in the respective handbook projects.

Edit the handbook in your browser

Follow the editing the handbook documentation.

Run the handbook locally for edits

Supported methods:

  1. Docker or compatible runtime (all software preinstalled in a container image)
  2. Source installation (experienced users, more software dependencies, can break)


  • Command line: git, wget (for syncing data). Additional requirements for source installation, see below.
  • Docker or compatible runtime (for running the Hugo environment in a container)
    • On macOS: Docker Desktop, Rancher Desktop, etc.
    • On Linux: Docker engine, Podman, etc.

Clone the handbook Git repository

Cloning the repository allows you to manually edit the handbook locally. If you prefer to use the Web IDE, please continue reading the editing the handbook documentation.

We recommend using git to clone the repository and then editing the handbook with a text editor such as Visual Studio Code, Typora, Nova or Sublime to name a few.

Clone the repo with HTTPS or SSH.

Public handbook:

git clone https://gitlab.com/gitlab-com/content-sites/handbook.git

git clone git@gitlab.com:gitlab-com/content-sites/handbook.git

Internal handbook:

git clone https://gitlab.com/gitlab-com/content-sites/internal-handbook.git

git clone git@gitlab.com:gitlab-com/content-sites/internal-handbook.git

Docsy GitLab theme:

git clone https://gitlab.com/gitlab-com/content-sites/docsy-gitlab.git

git clone git@gitlab.com:gitlab-com/content-sites/docsy-gitlab.git

Set up the repository

After cloning the repository, sync the required data files from the data file location (currently the www-gitlab-com repository). Without this step, the handbook cannot be run locally.

Open a terminal, navigate into the cloned handbook repository path, and run the sync-data.sh script.

Example for the public handbook:

cd handbook


Running Hugo

Hugo needs to be run to generate the static files from the handbook markdown content.

Running Hugo in Docker

You can use the Hugo container to start a locally running instance of the handbook, and verify how your changes look.

The following command starts the Hugo server, using the hugomods/hugo container image. The exts container image tag is important, as it provides the required SASS/CSS conversion tools.

docker run --rm -v $(pwd):$(pwd) -w $(pwd) --network host hugomods/hugo:exts hugo server

This will start the Hugo server listening on http://localhost:1313. If that doesn’t work, try It may take a couple of minutes to load the first time.

You can also start a new container, and run the commands with Hugo manually.

docker run --rm -it -v $(pwd):$(pwd) -w $(pwd) --network host hugomods/hugo:exts sh

## only if you're running into: ERROR Failed to read Git log: fatal: detected dubious ownership in repository
git config --global --add safe.directory '*'

hugo server

ctrl+d # to quit
Running older Hugo versions

Sometimes, newer Hugo versions introduce breaking changes to the docsy-gitlab theme, or its dependencies. As a workaround, use a working version, for example from the .gitlab-ci.yml configuration.

The Hugomods Docker images support versioned tags:

docker run --rm -v $(pwd):$(pwd) -w $(pwd) --network host hugomods/hugo:exts-0.123.7 hugo serve
exts-0.123.7: Pulling from hugomods/hugo

Running Hugo from source


The handbook development environment requires the extended version of Hugo. The extended version is required for the Docsy GitLab theme as we use SASS for generating the CSS stylesheets for the site. This will also require the installation of Node/NPM.

  1. Install asdf. The handbook repositories provide a .tools-version configuration file for asdf.
  2. Install hugo using Homebrew (Note: It is important to install Hugo extended), by following their documentation.
  3. Install the asdf packages: NodeJS (SASS/CSS), Go (theme development), Vale (linting)
  4. Install Git, curl, wget for sync scripts.
  5. Install the GitLab CLI for linting jobs.
asdf install

brew install hugo git curl wget glab

npm install
Running the Hugo binary

# Build the static website, default output into public/

# Start a local webserver, listening on localhost:1313
hugo serve

Parameters for Hugo

The handbook is huge, and by default, the hugo server command loads everything to memory.

The following options for the hugo command can be helpful for debugging or otherwise running locally:

  • --environment=production: generate a production build (asset minification, checksums, etc)
    • Note: hugo v0.x.x+extended version must be installed, use hugo version to check
    • Note: npm i must be run as a prerequisite to install postcss and autoprefixer dependencies
  • --renderToDisk: slower but requires less memory to run. Useful if you have less than 16GB allocated to docker machine
  • --verbose: enables verbose logging output
  • --templateMetrics and --templateMetricsHints: prints metrics related to how frequently templates are invoked and how much time is being spent evaluating them
  • --printMemoryUsage: periodically prints memory usage while the site is building

Build static files

To render the entire site to disk (and inspect the output in ${PWD}/public), purge the generated files first, and then run Hugo.

make clean

docker run --rm -v $(pwd):$(pwd) -w $(pwd) hugomods/hugo:exts hugo
# or


For some pages to render you may need a personal access token. Generate one and export it as an environment variable prior to running hugo commands:

hugo ...

If you need to make changes to the underlying theme you’ll need to make changes in the Gitlab-Docsy module.

Linting content

We use markdownlint-cli2 and Vale to enforce rules in handbook content.


Refer to the markdown guide for style guide information, and guidance on rules.

We use markdownlint-cli2 in our pipelines with a slightly customized set of rules. Before pushing any changes, you should run markdownlint-cli2 and fix any suggested changes to avoid pipeline failures.

To run markdownlint-cli2 using Docker, run:

docker run --rm -v $(pwd):$(pwd) -w $(pwd) davidanson/markdownlint-cli2 content/**/*.md

If you have markdownlint-cli2 installed locally, you can run the command in the relevant repository. This method is recommended when editing the config file (look for the markdownlint file), and testing the changes. To have markdownlint automatically fix some errors, add the fix option:

markdownlint-cli2 --fix "content/**/*.md"

Rules are configured to be close to the GitLab documentation markdownlint, without some of the stricter styling rules. The relevant rules are also noted in the markdown guide


We use Vale to warn when some rules from the Handbook Markdown Guide. are broken. Vale is not run in pipelines.

To run Vale using Docker, run:

docker run --rm -v $(pwd):$(pwd) -w $(pwd) jdkato/vale content

Docsy GitLab Theme Development

Docsy is used as a base for the internal and public handbooks. Modifications and extensions have been added on top to ensure efficient handbook usage.

All Hugo partials and shortcodes should be added to this repository, instead of duplicating them locally into the handbook projects.

The Docsy GitLab theme is integrated into the public/internal handbook repositories using a Go module in go.mod.


Documentation changes to /docs, theme updates with partials, and more require a new theme module release.

  1. Navigate into Code > Tags and create a new Git tag.
  2. Bump the semantic version (bugfixes: minor version, features: feature version, breaking changes: major version).
  3. Collect the recently merged MR titles into the description.
  4. Publish the Git tag

The new Git tag release needs to be pulled into the public/internal handbook projects as updated dependency, following the next steps.


Updating dependencies

Note: Before updating dependencies, review the go.mod file in the target project for additional notes and requirements.

Always update dependencies individually to isolate potential bugs and regressions. Never pull latest but pin the depedencies to a specific released version.

hugo mod get server.com/project-name@version

Google Docsy

hugo mod get github.com/google/docsy@v0.9.1

Do not bump Docsy versions without testing them extensively in MR review apps.

GitLab Docsy Theme update in handbook projects

  1. Ensure that the latest release is tagged in the Docsy GitLab Theme.

  2. Bump the version in the handbook projects.

  3. Navigate into the public/internal handbook project.

  4. Update the dependency version to the desired release.

    hugo mod get gitlab.com/gitlab-com/content-sites/docsy-gitlab@v0.3.15
  5. Create an MR and assign a handbook backend maintainer or codeowner for review.


You can add redirects to the layouts/index.redirects file in the relevant repository. Refer to the GitLab Pages redirect documentation for how these are formatted.

CI/CD Pipelines

The CI/CD pipelines do the following:

  1. Run linting scripts to check for content accuracy, codeowners, etc. Add an MR comment in case of linting errors.
  2. Use GitLab Pages for Review Apps to preview the changes in the same environment.
  3. Deploy to GitLab Pages when MRs are merged to main.

GitLab Pages Review Apps

The review apps are configured in the pages-review job in the .gitlab-ci.yml configuration file.

  1. Extends the Hugo build template
  2. Rules: Only run in MRs
  3. Environment: Use $CI_COMMIT_REF_SLUG as unique name
  4. Access job artifacts using $CI_JOB_ID in the URL
  5. Set the HUGO_ENVIRONMENT variable value to review.

Example for the public handbook:

  extends: [.hugo]
    name: review/$CI_COMMIT_REF_SLUG
    # There is no predefined CI variable that is formatted quite right
    # neither $CI_PAGES_URL nor $CI_JOB_URL have /-/ directly after the hostname
    url: https://gitlab-com.gitlab.io/-/content-sites/handbook/-/jobs/$CI_JOB_ID/artifacts/public/
    on_stop: stop-pages-review

The route map is configured in the .gitlab/route-map.yml.

Reviewer Roulette

The handbook projects use Reviewer Roulette feature of the danger-review CI/CD component.

In order to make Danger post message to MRs we need to setup a project token (api scope with Developer role) and a CI/CD variable called DANGER_GITLAB_API_TOKEN to contain this project token (Masked but not Protected).

For more details, follow the Danger documentation and inspect the MR adding the reviewer roulette to the public handbook.

A Zapier zap is configured with a webhook at the group level that should post to the #handbook-escalation channel when it is about to expire.

Code quality report MR commenter

The CI/CD linting jobs generate code quality report artifacts for MR widget integration. If there are errors detected, a custom CI/CD job posts a Markdown table summary as MR comment, linking to helpful handbook editing resources.

Requires a project token (api scope with Developer role) and a CI/CD variable called MR_UPDATE_TOKEN to contain this project token (Masked but not Protected).

CI/CD maintenance tasks

  1. Test and bump the Hugo image versions, aligned with the config/_default/config.yml mimimum version.
  2. Test and bump the davidanson/markdownlint-cli2 Docker image tags in the markdownlint job. We are not using the latest tag to avoid production failures pulling different latest versions.


The Markdown files get parsed by Hugo, and converted into static HTML files, including all assets (JS, CSS, images, etc.). The static files are uploaded to GitLab Pages.

Templates and Partials

Review the Hugo development documentation to learn more.

Triage bot

A triage bot exists to help triage issues and close stale merge requests. See the triage project readme for more information.