This repository is responsible for building and deploying the official Rocky Linux documentation site, hosted at docs.rockylinux.org. The site is built by GitHub Actions, published as static files to an S3 bucket, and served to the public through Fastly.
Important
This repository contains the build and deployment logic only. It does not contain the documentation content itself.
- Content Source
- How the Build Process Works
- Key Files & Scripts
- Deep Dive: The
build.shScript - How to Maintain the Site
- Deployment & Operations
- Local Development & Testing
All documentation content is sourced from the rocky-linux/documentation GitHub repository. The build script in this repository clones the content repo at build time.
The pipeline is defined in .github/workflows/build-docs.yml.
-
Trigger: A build starts on any of the following:
- A push to this repository's
mainbranch. - A daily schedule (
0 4 * * *UTC), to pick up content changes inrocky-linux/documentation. - A manual
workflow_dispatchrun. - A
repository_dispatchevent of typedeploy-docs, sent by the content repository when its docs change.
Runs are serialized through the
s3-deployconcurrency group, so deployments never overlap. - A push to this repository's
-
Build: The workflow runs
./scripts/build.sh, which uses mkdocs and the mike plugin to build a versioned static HTML site into thesite/directory. -
Publish:
aws s3 sync site/ s3://$S3_BUCKET/ --deleteuploads the result, with apublic, max-age=604800cache header. The--deleteflag means the bucket is an exact mirror of the build output. -
Compress search indexes: Each
search_index.jsonis re-uploaded gzipped withContent-Encoding: gzip. These files can reach ~30MB uncompressed, which exceeds Fastly's cacheable object size limit, and S3 does not compress on the fly. Compressing them keeps the indexes cacheable at the edge and makes them roughly 6x faster to download. -
Purge: The workflow issues a Fastly soft purge for surrogate key
all, so the edge picks up the new content. This step iscontinue-on-error, since a failed purge does not invalidate an otherwise successful deploy — the cache will expire on its own. -
URL Structure: The site uses a "Root + Versioned" deployment strategy. The latest documentation (currently Rocky Linux 10) is served from the root URL (
/), while all documentation versions remain accessible via versioned paths (e.g.,/8/,/9/,/latest/).
.github/workflows/build-docs.yml: The build and deploy pipeline (build → S3 → Fastly purge).scripts/build.sh: The primary script that orchestrates the entire build. It contains all the logic for cloning, versioning, and building the documentation.requirements.txt: A standard Python file listing the dependencies required for the build, such asmkdocsandmike.configs/mkdocs.yml: The main configuration file formkdocs. The build script symlinks this tomkdocs.ymlsomikecan find it.
This script is the heart of the repository. For maintainers, understanding its structure is key.
The script creates a Python 3.12 virtual environment with uv, installs the dependencies from requirements.txt, and puts .venv/bin on the PATH so mkdocs and mike resolve directly. It then applies a small in-place patch to mkdocs-awesome-pages-plugin for i18n stability.
This function is called for each documentation version that needs to be built. It:
- Clones a specific branch (e.g.,
rocky-8) from therocky-linux/documentationrepository. - Crucially, it performs a full clone to preserve the entire git history. This is required for the
git-revision-date-localized-pluginto accurately display when a page was last updated. - It uses symlinks to make the cloned content available to
mikewhile preserving the git context.
After cloning a version, the script uses mike deploy to build the static HTML for that version. mike manages the versioning by committing the built site to a temporary gh-pages branch within the build environment. This process is repeated for all specified versions.
Once mike has built all versions into the gh-pages branch, the script extracts the complete static site into the site/ directory using git archive. This directory is the final artifact that gets synced to S3.
To ensure docs.rockylinux.org serves the latest documentation directly, the script performs a final step: it copies all content from the site/latest/ directory to the root of the site/ directory. It carefully preserves the versions.json file to ensure the version-switching dropdown menu continues to function correctly across the entire site.
Warning
To give mike a repository to work in, the script deletes and re-initializes .git in the working directory. This is safe on a throwaway CI runner, but it will destroy your local git state if you run the script directly in a checkout you care about. See Local Development & Testing.
Maintenance typically involves modifying the build script to add, update, or remove documentation versions.
- Open
scripts/build.sh. - Find the section where
build_versionis called. - Add a new line for the new version, specifying the version number and the corresponding branch name from the content repository. For example, to add Rocky Linux 11 from the
rocky-11branch:build_version "11" "rocky-11" "" ""
The default version is the one aliased to latest.
- Open
scripts/build.sh. - Modify the
build_versioncall that includes"latest"as the alias. For example, to make version 11 the new latest:# Old build_version "10" "main" "latest" "" # New build_version "11" "rocky-11" "latest" ""
- The
mike set-defaultcommand useslatest, so it does not need to be changed.
- Open
scripts/build.sh. - Find the
build_versioncall for the version you want to remove and delete or comment out the line.
Note
Because the S3 sync uses --delete, removing a version from the build script also removes it from the live site on the next deploy.
Deployments are fully automated. Pushing to main — or a content change in rocky-linux/documentation — is all that is normally required.
To start a build manually without pushing a commit:
# Requires the GitHub CLI, authenticated with access to the repo
gh workflow run build-docs.yml -R rocky-linux/docs.rockylinux.orgTo watch the run and inspect logs:
gh run list -R rocky-linux/docs.rockylinux.org --workflow=build-docs.yml --limit 5
gh run watch -R rocky-linux/docs.rockylinux.org <run-id>
gh run view -R rocky-linux/docs.rockylinux.org <run-id> --log-failedThe same workflow can also be triggered from the content repository via a repository_dispatch event of type deploy-docs.
The workflow soft-purges the whole service after every successful deploy. To purge by hand — for example if the purge step failed, or if you changed something at the Fastly layer:
curl -X POST \
"https://api.fastly.com/service/$FASTLY_SERVICE_ID/purge" \
-H "Fastly-Key: $FASTLY_API_TOKEN" \
-H "Fastly-Soft-Purge: 1" \
-H "Surrogate-Key: all" \
-H "Accept: application/json"FASTLY_SERVICE_ID is exported by .envrc (via direnv). Put your personal FASTLY_API_TOKEN in .envrc.local, which is git-ignored.
A soft purge marks content stale rather than evicting it, so the edge keeps serving the old copy until the new one is fetched. There is no hard-down window.
Configured in the repository's Settings → Secrets and variables → Actions.
| Name | Type | Purpose |
|---|---|---|
S3_BUCKET |
secret | Destination bucket name for the built site |
AWS_ACCESS_KEY_ID |
secret | Credentials for the S3 sync |
AWS_SECRET_ACCESS_KEY |
secret | Credentials for the S3 sync |
FASTLY_SERVICE_ID |
secret | Fastly delivery service fronting the bucket |
FASTLY_API_TOKEN |
secret | Token with purge rights on that service |
AWS_REGION |
variable | Region of the S3 bucket |
- Build failed: Use
gh run view <run-id> --log-failed. The most common causes are an upstream change inrocky-linux/documentationthat breaks a mkdocs plugin, or themkdocs-awesome-pages-pluginpatch in Stage 1 failing to apply after a dependency bump. - Deploy succeeded but the site looks stale: The purge step is
continue-on-error, so check whether it actually succeeded, and re-run the manual purge above if not. - A page 404s that used to work: Remember the sync runs with
--delete. If a page vanished from the build output, it is now gone from the bucket too. - Search returns nothing: Check that
search_index.jsonis being served withContent-Encoding: gzipand a 200 status, e.g.curl -sI https://docs.rockylinux.org/search/search_index.json.
You can run the full build locally to test changes.
Caution
scripts/build.sh is destructive to its working directory: it runs rm -rf .git and re-initializes a fresh repository, and it overwrites README.md with a placeholder. Do not run it directly inside your working checkout. Copy the repository to a scratch directory first:
cp -r docs.rockylinux.org /tmp/docs-build && cd /tmp/docs-build- Install
uv. The script creates its own Python 3.12 virtual environment and installsrequirements.txtinto it, so no other setup is needed. - Run the build script:
./scripts/build.sh
- The script will execute the full build process and place the output in the
site/directory. Serve it locally to verify your changes:python3 -m http.server --directory site
A full build clones three branches of the content repository with complete history. Expect it to take roughly 10 minutes and produce about 3.7GB in site/, plus the rockydocs-8, rockydocs-9 and rockydocs-10 clones and a .venv.
The many WARNING - External file: ... messages during the build are expected. They come from the privacy plugin running with assets_fetch: false and do not indicate a problem.