diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 5a0c5466f544a892f68d72d30e634908ef2a5871..6d8ff33238c6b2d5e20bab97b463ad991db13894 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,6 +1,10 @@
 ---
 stages:
   - tox
+  - documentation
+
+include:
+  - docs/.gitlab-ci.yml
 
 ####################################  tox   -   Testing and linting
 run-tox-pipeline:
diff --git a/docs/.gitlab-ci.yml b/docs/.gitlab-ci.yml
new file mode 100644
index 0000000000000000000000000000000000000000..91940314c2c0f01d3624ac6bfe495d8036b87974
--- /dev/null
+++ b/docs/.gitlab-ci.yml
@@ -0,0 +1,38 @@
+---
+##### Sphinx  - Generate documentation
+build-documentation:
+  stage: documentation
+  tags:
+    - docker-executor
+  image: sphinxdoc/sphinx:latest
+
+  before_script:
+    - pip install sphinx sphinx_rtd_theme myst-parser
+  script:
+    - sphinx-apidoc gso gso/app.py -o docs/source
+    - cd $CI_PROJECT_DIR/docs
+    - sphinx-build -b html docs/source docs/build
+
+  artifacts:
+    paths:
+      - docs/build/html
+
+##### Vale    - Documentation linter
+lint-documentation:
+  stage: documentation
+  image:
+    name: jdkato/vale:latest
+    entrypoint: [""]
+
+  tags:
+    - docker-executor
+  needs:
+    - job: build-documentation # Only run when documentation has been built
+      artifacts: true
+
+  before_script:
+    - cd $CI_PROJECT_DIR/docs/vale
+    - vale sync
+
+  script:
+    - vale $CI_PROJECT_DIR/docs/build/html/_sources
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..ed88099027f775942fa65dce2314f1ae9675cb36
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000000000000000000000000000000000000..c1ad4dc9ae84a3a3974eff88209f81753e50b089
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,26 @@
+# -- Project information -----------------------------------------------------
+project = 'GÉANT Service Orchestrator'
+copyright = '2023, GÉANT Vereniging'
+author = 'GÉANT Orchestration and Automation Team'
+
+# -- General configuration ---------------------------------------------------
+extensions = ['sphinx_rtd_theme', 'myst_parser']
+
+templates_path = ['templates']
+exclude_patterns = ['build', 'Thumbs.db', '.DS_Store', 'venv', 'vale']
+source_suffix = {
+    '.md': 'markdown'
+}
+
+# -- Options for Markdown support --------------------------------------------
+myst_enable_extensions = ['attrs_block', 'deflist', 'replacements', 'smartquotes', 'strikethrough']
+suppress_warnings = ['myst.strikethrough']
+
+# -- Options for HTML output -------------------------------------------------
+html_theme = 'sphinx_rtd_theme'
+html_static_path = ['static']
+html_theme_options = {
+    'style_nav_header_background': 'rgb(0 63 95)',
+}
+html_css_files = ['custom.css']
+html_logo = 'static/geant_logo_white.svg'
diff --git a/docs/glossary.md b/docs/glossary.md
new file mode 100644
index 0000000000000000000000000000000000000000..e256c6a20a27c0ccb56f05feddff5447387dd08e
--- /dev/null
+++ b/docs/glossary.md
@@ -0,0 +1,8 @@
+# Glossary of terms
+
+{.glossary}
+GSO
+: GÉANT Service Orchestrator
+
+WFO
+: <a href="https://workfloworchestrator.org/" target="_blank">Workflow Orchestrator</a>
diff --git a/docs/index.md b/docs/index.md
new file mode 100644
index 0000000000000000000000000000000000000000..d63b6e23aae1e10c22bc33609333eab59107bfe6
--- /dev/null
+++ b/docs/index.md
@@ -0,0 +1,32 @@
+# GÉANT Service Orchestrator ({term}`GSO`)
+
+Welcome to the documentation of the GÉANT Service Orchestrator, or {term}`GSO` for short. \
+
+This documentation has the following sections:
+
+```{toctree}
+:caption: Background
+:maxdepth: 2
+overview/index.md
+architecture/index.md
+```
+
+```{toctree}
+:caption: Modeling
+:maxdepth: 2
+modeling/index.md
+modeling/ports.md
+```
+
+```{toctree}
+:caption: Processes
+:maxdepth: 2
+processes/node_provisioning.md
+processes/deploy_router.md
+```
+
+```{toctree}
+:caption: —
+:maxdepth: 2
+glossary.md
+```
diff --git a/docs/static/custom.css b/docs/static/custom.css
new file mode 100644
index 0000000000000000000000000000000000000000..7297b93dc4b35f38c353d1d31917bdac92094156
--- /dev/null
+++ b/docs/static/custom.css
@@ -0,0 +1,3 @@
+.wy-menu > p > span {
+    color: rgb(237 21 86);
+}
diff --git a/docs/static/geant_logo_white.svg b/docs/static/geant_logo_white.svg
new file mode 100644
index 0000000000000000000000000000000000000000..31bfead62b052812096b5a9c79cf782ce330a87c
--- /dev/null
+++ b/docs/static/geant_logo_white.svg
@@ -0,0 +1,16 @@
+<svg width="79" height="35" viewBox="0 0 79 35" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_101_15)">
+<path d="M15.9 17.8C16.5 17.3 17 17.1 17.4 17.1C18.4 17.1 18.7 17.8 18.7 18.2C18.5 18.3 14.2 19.7 14 19.8C13.9 19.7 13.9 19.6 13.8 19.6C14 19.4 15.9 17.8 15.9 17.8Z" fill="white" fill-opacity="0.85"/>
+<path d="M0 27C0 31.8 2.1 34.3 6.3 34.3C9.1 34.3 10.8 33.1 10.8 33L10.9 32.9V26.2H5.2V28.1C5.2 28.1 8.1 28.1 8.6 28.1C8.6 28.6 8.6 31.6 8.6 31.9C8.3 32.1 7.4 32.4 6.2 32.4C3.7 32.4 2.5 30.6 2.5 27C2.5 24.9 3.1 22.4 5.9 22.4C7.8 22.4 8.5 23.5 8.5 24.5V24.8H11.1V24.5C11.1 22.1 9 20.5 5.9 20.5C2.2 20.5 0 22.9 0 27Z" fill="white" fill-opacity="0.85"/>
+<path d="M20.2 20.7H12.6V34.1H20.7V32.2C20.7 32.2 15.5 32.2 14.9 32.2C14.9 31.7 14.9 28.5 14.9 28C15.5 28 20.2 28 20.2 28V26.1C20.2 26.1 15.5 26.1 14.9 26.1C14.9 25.6 14.9 23 14.9 22.5C15.5 22.5 20.5 22.5 20.5 22.5V20.6H20.2V20.7Z" fill="white" fill-opacity="0.85"/>
+<path d="M54.5 20.7H42.9C42.9 20.7 42.9 28.7 42.9 30.6C42 29 37.2 20.7 37.2 20.7H34.5V34.1H36.8C36.8 34.1 36.8 26.1 36.8 24.2C37.7 25.8 42.5 34.1 42.5 34.1H45.2C45.2 34.1 45.2 23.2 45.2 22.6C45.7 22.6 48.4 22.6 48.9 22.6C48.9 23.2 48.9 34.1 48.9 34.1H51.3C51.3 34.1 51.3 23.2 51.3 22.6C51.8 22.6 54.9 22.6 54.9 22.6V20.7H54.5V20.7Z" fill="white" fill-opacity="0.85"/>
+<path d="M28.9 20.7H28.7H26.4L21.4 34.1H23.8C23.8 34.1 25.1 30.6 25.3 30.2C25.7 30.2 29.8 30.2 30.2 30.2C30.3 30.6 31.7 34.1 31.7 34.1H34L28.9 20.7ZM25.9 28.3C26.1 27.6 27.3 24.4 27.7 23.3C28.1 24.4 29.2 27.6 29.5 28.3C28.7 28.3 26.6 28.3 25.9 28.3Z" fill="white" fill-opacity="0.85"/>
+<path d="M77 8C68.2 -2.9 32.6 12.5 23 16.5C22.3 16.8 21.4 16.7 20.9 15.7C21.3 16.7 22.1 17.1 23.1 16.7C35.8 11.6 66.5 0.600002 74.2 10.7C77.7 15.3 76.7 20.9 72.9 28.8C72.7 29.1 72.6 29.4 72.6 29.4C72.6 29.4 72.6 29.4 72.6 29.5C72.6 29.5 72.6 29.5 72.6 29.6C72.3 30.1 71.9 30.3 71.6 30.4C72 30.4 72.5 30.2 72.9 29.6C73 29.5 73.1 29.3 73.3 29C78.7 19.5 80.7 12.5 77 8Z" fill="white" fill-opacity="0.85"/>
+<path d="M70.3 29.9C70.2 29.8 68.6 28.4 67 26.9C58.7 19 33.4 -5.3 22.4 1.1C19.3 2.9 18.8 8.2 20.7 15.2C20.7 15.3 20.8 15.4 20.8 15.5C21 16.2 21.5 16.7 22.2 16.7C21.7 16.6 21.3 16.2 21.1 15.7C21.1 15.6 21 15.5 21 15.5C21 15.4 20.9 15.3 20.9 15.1C20.9 15 20.9 14.9 20.8 14.9C19.8 9 21 5.1 23.4 3.5C32.3 -2.5 53.5 15.8 64.2 25C66.6 27.1 69.4 29.5 70.2 30.1C71.4 31 72.4 30 72.7 29.5C72.3 30.1 71.3 30.7 70.3 29.9Z" fill="white" fill-opacity="0.85"/>
+</g>
+<defs>
+<clipPath id="clip0_101_15">
+<rect width="78.9" height="34.3" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/docs/test_documentation.sh b/docs/test_documentation.sh
new file mode 100755
index 0000000000000000000000000000000000000000..dcb80d7820ec201a87bc2f714b803233925f65d3
--- /dev/null
+++ b/docs/test_documentation.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+docker run -it --rm -v $(pwd):/docs sphinxdoc/sphinx:latest /bin/bash -c \
+"pip install sphinx_rtd_theme myst-parser;cd docs;make html"
+
+if [ ! -d ./vale/styles/proselint ] || [ ! -d ./vale/styles/Microsoft ]; then
+  docker run -it --rm -v $(pwd):/gso jdkato/vale:latest --config="/gso/vale/.vale.ini" sync
+fi
+
+docker run -it --rm -v $(pwd):/gso jdkato/vale:latest --config="/gso/vale/.vale.ini" /gso/build/html/_sources/
diff --git a/docs/vale/.vale.ini b/docs/vale/.vale.ini
new file mode 100644
index 0000000000000000000000000000000000000000..3abe670e22315ca4b13a17c3702bf9d2fc9b4d84
--- /dev/null
+++ b/docs/vale/.vale.ini
@@ -0,0 +1,16 @@
+StylesPath = styles
+
+MinAlertLevel = suggestion
+
+Vocab = geant-jargon, Sphinx
+
+Packages = proselint, Microsoft
+
+[formats]
+txt = md
+
+[*]
+BasedOnStyles = Vale, proselint, Microsoft
+
+; Ignore the table of contents directive
+BlockIgnores = (?s) *(\x60\x60\x60{toctree}.*?\x60\x60\x60)
diff --git a/docs/vale/styles/Vocab/Sphinx/accept.txt b/docs/vale/styles/Vocab/Sphinx/accept.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1c26bb264fd26def95b47f76db7bd31eb9fe37a2
--- /dev/null
+++ b/docs/vale/styles/Vocab/Sphinx/accept.txt
@@ -0,0 +1 @@
+toctree
diff --git a/docs/vale/styles/Vocab/geant-jargon/accept.txt b/docs/vale/styles/Vocab/geant-jargon/accept.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1d13a809d0a79120dcb7d1f4554a0cb7eedcb933
--- /dev/null
+++ b/docs/vale/styles/Vocab/geant-jargon/accept.txt
@@ -0,0 +1 @@
+GÉANT Automation Platform
diff --git a/requirements.txt b/requirements.txt
index 2ba2124901876fa9d668c8d9f59e453c8d03771e..e02d83b26d57eb6a478ebf6f90abf11e96837d33 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -9,3 +9,5 @@ isort
 flake8
 mypy
 ruff
+sphinx
+sphinx-rtd-theme