Skip to content



Our documentation is powered by mike and MkDocs. MkDocs is powered by Python-Markdown. These are immensely configurable and extensible. You can see our MkDocs configuration in docs/mkdocs.yml. Following are some of the choices we have made.

  • The MkDocs theme is Material for MkDocs.
  • MkDocs plugin awesome-pages for greater control over how navigation links are shown.
  • MkDocs plugin macros.
  • Our own slightly improved vintage of the include-markdown MkDocs plugin, allowing the source to be factored into re-used files.
  • Python-Markdown extension SuperFences, supporting fenced code blocks that play nice with other markdown features.
  • Python-Markdown extension Highlight, for syntax highlighting of fenced code.
  • Pygments for even fancier code highlighting.
  • MkDocs plugin mkdocs-static-i18n to support multiple languages. We currently only have documentation in English. If you're interested in contributing translations, please let us know!

Serving up documents locally#

You can view and modify our documentation in your local development environment. Simply checkout one of our branches.

git clone
cd kubestellar/docs
git checkout main

You can view and modify our documentation in the branch you have checked out by using mkdocs serve from mkdocs. We have a Python requirements file in requirements.txt, and a Makefile target that builds a Python virtual environment and installs the requirements there. You can either install those requirements into your global Python environment or use the Makefile target. To install those requirements into your global Python environment, do the following usual thing.

pip install -r requirements.txt

Alternatively, use the following commands to use the Makefile target to construct an adequate virtual environment and enter it.

( cd ..; make venv )
. venv/bin/activate

Then, using your chosen environment with the requirements installed, build and serve the documents with the following command.

mkdocs serve
Then open a browser to http://localhost:8000/

Another way to view (not modify - this method reflects what has been deployed to the gh-pages branch of our repo) all branches/versions of our documentation locally using 'mike' mike for mkdocs:

git clone
cd kubestellar
git checkout main
cd docs
mike set-default main
cd ..
make serve-docs
Then open a browser to http://localhost:8000/

Jinja templating#

Our documentation stack includes Jinja. The Jinja constructs --- {\% ... \%} for statements, {{ ... }} for expressions, and {# ... #} for comments --- can appear in the markdown sources.

File structure#

All documentation-related items live in docs (with the small exception of various make targets and some helper scripts in hack).

The structure of docs is as follows:

Path Description
config/$language/mkdocs.yml Language-specific mkdocs configuration.
content/$language Language-specific website content.
generated/branch All generated content for all languages for the current version.
generated/branch/$language Generated content for a single language. Never added to git.
generated/branch/index.html Minimal index for the current version that redirects to the default language (en)
overrides Global (not language-specific) content.
Dockerfile Builds the kubestellar-docs image containing mkdocs + associated tooling.
mkdocs.yml Minimal mkdocs configuration for mike for multi-version support.
requirements.txt List of Python modules used to build the site.

Global Variables#

There are many global variables defined in the docs/mkdocs.yml. The following are some very common variables you are encouraged to use in our documentation. Use of these variables/macros allows our documentation to have github branch context and take advantage of our evolution without breaking

- site_name: KubeStellar
- repo_url:
- site_url:
- repo_default_file_path: kubestellar
- repo_short_name: kubestellar/kubestellar
- docs_url:
- repo_raw_url:
- edit_uri: edit/main/docs/content/
- ks_branch: main
- ks_tag: latest

to use a variables/macro in your documentation reference like this:

{{ config.<var_name> }}

and in context that can look something like this:

bash <(curl -s {{ config.repo_raw_url }}/{{ config.ks_branch }}/bootstrap/ --kubestellar-version {{ config.ks_tag }}

    - A more extensive and detailed list is located at mkdocs information
    - We also check for broken links as part of our PR pipeline. For more information check out our Broken Links Crawler

Page variables#

A markdown source file can contribute additional variables by defining them in name: value lines at the start of the file, set off by lines of triple dashes. For example, suppose a markdown file begins with the following.

short_name: example1
manifest_name: 'docs/content/Coding Milestones/PoC2023q1/'

These variables can be referenced as {{ page.meta.short_name }} and {{ page.meta.manifest_name }}.

Including external markdown#

We make extensive use of 'include-markdown' to help us keep our documentation modular and up-to-date. To use 'include-markdown' you must add a block in your document that refers to a block in your external document content:

In your original markdown document, add a block that refers to the external markdown you want to include:

Include Markdown

In the document you want to include, add the start and end tags you configured in the include-markdown block in your original document:

Included Markdown

for more information on the 'include-markdown' plugin for mkdocs look here

Supported aliases for our documentation#

We currently support 3 aliases for our documentation:

- from the release major.minor branch:
    - [](../../../
- from the main branch:
    - [](../../../
    - [](../../../

Shortcut URLs#

We have a few shortcut urls that come in handy when referring others to our project:

note: You need to join our mailing list first to get access to some of the links that follow (

and.. the very important… - - our 'stable' quickstart


mkdocs has some very helpful ways to include blocks of code in a style that makes it clear to our readers that console interaction is necessary in the documentation. There are options to include a plain codeblock (```), shell (shell), console (console - no used in our documentation), language or format-specific (yaml, etc.), and others. For more detailed information, checkout the mkdocs information on codeblocks.

NOTE: the docs-ecutable technology does not apply Jinja, at any stage; Jinja source inside executed code blocks will not be expanded by Jinja but rather seen directly by bash.

Here are some examples of how we use codeblocks.

Seen and executed#

For a codeblock that can be 'tested' (and seen by the reader) as part of our CI, use the shell block:

mkdocs serve
as seen by reader:
mkdocs serve

Executed but not seen#

(Think hard before hiding stuff from your reader.)

For a codeblock that should be 'tested', BUT not seen by the reader, use the .bash with the plain codeblock, and the '.hide-me' style (great for hiding a sleep command that user does not need to run, but CI does):

``` {.bash .hide-me}
sleep 10
as seen by reader:

Seen but not executed#

(To avoid confusing readers of the HTML, this should be used only for output seen in a shell session.)

For a codeblock that should not be 'tested' as part of our CI, use the .bash with the plain codeblock, and without the '.hide-me' style:

``` {.bash}
mkdocs server
as seen by reader:
mkdocs server

Seen but not executed and no copy button#

For a codeblock that should not be 'tested', be seen by the reader, and not include a 'copy' icon (great for output-only instances), use the .bash codeblock without the '.no-copy' style:

``` {.bash .no-copy}
I0412 15:15:57.867837   94634 shared_informer.go:282] Waiting for caches to sync for placement-translator
I0412 15:15:57.969533   94634 shared_informer.go:289] Caches are synced for placement-translator
I0412 15:15:57.970003   94634 shared_informer.go:282] Waiting for caches to sync for what-resolver
as seen by reader:
I0412 15:15:57.867837   94634 shared_informer.go:282] Waiting for caches to sync for placement-translator
I0412 15:15:57.969533   94634 shared_informer.go:289] Caches are synced for placement-translator
I0412 15:15:57.970003   94634 shared_informer.go:282] Waiting for caches to sync for what-resolver

Other language-specific highlighting#

For other language-specific highlighting (yaml, etc.), use the yaml codeblock

  - Home:
  - QuickStart: Getting-Started/
  - Contributing: 
      - Guidelines: Contribution guidelines/
as seen by reader:
  - Home:
  - QuickStart: Getting-Started/
  - Contributing: 
      - Guidelines: Contribution guidelines/

Codeblock with a title#

For a codeblock that has a title, and will not be tested, use the 'title' parameter in conjunction with the plain codeblock (greater for showing or prescribing contents of files):

``` title=""
echo hello KubeStellar
as seen by reader:
echo hello KubeStellar

(other variations are possible, PR an update to the kubestellar.css file and, once approved, use the style on the plain codeblock in your documentation.)

Testing/Running Docs#

How do we ensure that our documented examples work? Simple, we 'execute' our documentation in our CI. We built automation called 'docs-ecutable' which can be invoked to test any markdown (.md) file in our repository. You could use it in your project as well - afterall it is opensource.

The way it works:#

  • create your .md file as you normally would
  • add codeblocks that can be tested, tested but hidden, or not tested at all:
    • use 'shell' to indicate code you want to be tested
    • use '.bash' with the plain codeblock, and the '.hide-md' style for code you want to be tested, but hidden from the reader (some like this, but its not cool if you want others to run your instructions without hiccups)
    • use plain codeblock (```) if you want to show sample output that is not to be tested
  • you can use 'include-markdown' blocks, and they will also be executed (or not), depending on the codeblock style you use in the included markdown files.

The GitHub Workflow:#

The original secret sauce:#

  • The original code that made all this possible is at
    • This code parses the .md file you give it to pull out all the 'shell' and '.bash .hide-me' blocks
    • The code is smart enough to traverse the include-markdown blocks and include the 'shell' and '.bash .hide-me' blocks in them
    • The Jinja constructs are not expanded by this code.
    • It then creates a file called '' which is then run at the end of the docs-ecutable execution.

All of this is invoke in a target in our Makefile

.PHONY: docs-ecutable
    MANIFEST=$(MANIFEST) docs/scripts/

You give the path from that follows the '' path, and name of the .md file you want to 'execute'/'test' as the value for the MANIFEST variable:

How to 'make' our docs-ecutable target
make MANIFEST="'docs/content/Getting-Started/'" docs-ecutable

note: there are single and double-quotes used here to avoid issues with 'spaces' used in files names or directories. Use the single and double-quotes as specified in the quickstart example here.

The new and improved secret sauce:#

  • The newer code for executing bash snippets in documentation is at
    • This code parses the HTML generated by MkDocs to extract all the fenced code blocks tagged for the "shell" language.
    • This HTML scraping is relatively easy because it does not have to work on general HTML but only the HTML generated by our stack from our sources. The use of the option setting pygments_lang_class: true for the Python-Markdown extension pymdownx.highlight plays a critical role, getting the source language into the generated HTML.
    • Because it reads the generated HTML, invisible code blocks are not extracted.
    • Because it reads the generated HTML, the Jinja constructs have their usual effects.
    • This script is given the name of the HTML file to read and the current working directory to establish at the start of the extracted bash.
    • It then creates a file called '' which is then run.

All of this is invoked in a target in our Makefile

.PHONY: execute-html
execute-html: venv
    . $(VENV)/activate; \
    cd docs; \
    mkdocs build; \
    scripts/ "$$PWD/.." "generated/$(MANIFEST)/index.html"

The make target requires the variable MANIFEST to be set to the directory that contains the generated index.html file, relative to ''. This is the name of the markdown source file, relative to '' and with the .md extension dropped.

How to 'make' a docs-ecutable target
make MANIFEST="Coding Milestones/PoC2023q1/example1" execute-html

note: this target has no special needs for quoting --- which is not to deny the quoting that your shell needs.

Important files in our gh-pages branch#

index.html and home.html#

In the 'gh-pages' branch there are two(2) important files that redirect the github docs url to our KubeStellar doc site hosted with

both files have content similar to:

index.html and home.html
<!DOCTYPE html>
<meta http-equiv="content-type" content="text/html; charset=utf-8" >
<meta http-equiv="refresh" content="0; URL=" />

Do not remove these files!


The CNAME file has to be in the gh-pages root to allow github to recognize the url tls cert served by our hosting provider. Do not remove this file!

the CNAME file must have the following content in it:



The versions.json file contains the version and alias information required by 'mike' to properly serve our doc site. This file is maintained by the 'mike' environment and should not be edited by hand.

[{"version": "release-0.2", "title": "release-0.2", "aliases": ["stable"]}, {"version": "main", "title": "main", "aliases": ["latest", "unstable"]}]

In case of emergency#

If you find yourself in a jam and the pages are not showing up at or, check the following 1) Is the index.html, home.html, CNAME, and versions.json file in the gh-pages branch alongside the folders for the compiled documents? If not, then recreate those files as indicated above (except for versions.json which is programmatically created by 'mike'). 2) Is GitHub settings for 'Pages' for the domain pointing at the url? If not, paste it in and check off 'enforce https'. This can happen if the CNAME file goes missing from the gh-pages branch.

How to recreate the gh-pages branch#

To recreate the gh-pages branch, do the following: - checkout the gh-pages branch to your local system

git clone -b gh-pages KubeStellar
cd KubeStellar
- delete all files in the branch and push it to GitHub
rm -rf *
git add; git commit -m "removing all gh-pages files"; git push -u origin gh-pages
-- switch to the 'main' branch
git checkout main
git pull
- switch to /docs and run 'mike deploy' for the main branch for alias 'unstable' and 'latest'
cd docs
mike deploy --push --rebase --update-aliases main unstable
mike deploy --push --rebase --update-aliases main latest
- switch to the 'release' branch and 'mike deploy' for the release branch for alias 'stable' (your release name will vary)
git checkout release-0.2
git pull
mike deploy --push --rebase --update-aliases release-0.2 stable
- switch back to the gh-pages branch and recreate the home.html, index.html, and CNAME files as needed (make sure you back out of the docs path first before switching to gh-pages because that path does not exist in that branch)
cd ..
git checkout gh-pages
git pull
vi index.html
vi home.html
- push the new files into gh-pages
git add .;git commit -m "add index, home, and CNAME files";git push -u origin gh-pages
- go into the GitHub UI and go to the settings for the project and click on 'Pages' to add as the domain and check the box to enforce https.

  • if the above did not work, then you might have an issue with the GoDaddy domain (expired, files missing, etc.)

Publishing Workflow#

All documentation building and publishing is done using GitHub Actions in docs-gen-and-push.yaml. The overall sequence is: