diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 79d4434b0d4030764a849a58c0ab86b527260c0b..9265228fc78988f725997a2599e80837ebe6b12f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -17,7 +17,7 @@ build-user-manual:
 
 pages:
   image: alpine:latest
-  stage: deploy
+  stage: "Deploy"
   script:
     - mv user_manual/_sphinx_build/html public  # folder has to be named "public" for gitlab to publish it
   artifacts:
diff --git a/cmake/PrepareSFG.cmake b/cmake/PrepareSFG.cmake
index 4a5431b4a30597f145128a302d1cd028d10ae33f..1f34e3c4006f8b2ab1883a5ccd38018f06b5d7c5 100644
--- a/cmake/PrepareSFG.cmake
+++ b/cmake/PrepareSFG.cmake
@@ -36,8 +36,6 @@ if( WALBERLA_CODEGEN_USE_PRIVATE_VENV )
         set( _wlb_codegen_python_init ${_venv_python_exe} )
         mark_as_advanced(_sfg_private_venv_done)
     endif()
-
-    set(PystencilsSfg_PYTHON_PATH ${_venv_python_exe})
 else()
     #   Use the external Python environment, but check if all packages are installed
     find_package( Python COMPONENTS Interpreter REQUIRED )
@@ -58,6 +56,7 @@ else()
     set( _wlb_codegen_python_init ${Python_EXECUTABLE} )
 endif()
 
+set(PystencilsSfg_PYTHON_PATH ${_wlb_codegen_python_init})
 set(
     WALBERLA_CODEGEN_PYTHON
     ${_wlb_codegen_python_init}
diff --git a/user_manual/index.md b/user_manual/index.md
index 618f2c81a7f9c45e877ff1aa76014b9c364daa0d..473b4f1a05c50df6418adf4cad216475acf8e0dd 100644
--- a/user_manual/index.md
+++ b/user_manual/index.md
@@ -32,6 +32,13 @@ GeneratorScriptBasics/GeneratorScriptBasics
 ForceDrivenChannel/ForceDrivenChannel
 :::
 
+:::{toctree}
+:caption: Reference
+:maxdepth: 1
+
+Python Environment <reference/PythonEnvironment>
+:::
+
 
 [pystencils_2_0]: https://da15siwa.pages.i10git.cs.fau.de/dev-docs/pystencils-nbackend/ "pystencils 2.0 Documentation"
 [pystencils-sfg]: https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/index.html "pystencils-sfg Documentation"
diff --git a/user_manual/reference/PythonEnvironment.md b/user_manual/reference/PythonEnvironment.md
new file mode 100644
index 0000000000000000000000000000000000000000..614cf1fb3610fbb5e1d7e0ac58631f29e4a0a494
--- /dev/null
+++ b/user_manual/reference/PythonEnvironment.md
@@ -0,0 +1,38 @@
+# Managing the Code Generator's Python Environment
+
+On this page, you can find information on managing, customizing, and extending the Python environment
+used by the waLBerla code generation system.
+
+## Using the Default Virtual Environment
+
+By default, `sfg-walberla` creates a new Python virtual environment within the CMake build tree,
+and there installs all packages required for code generation.
+
+### Install Additional Packages
+
+For projects that require external dependencies, *sfg-walberla* exposes the CMake function
+`walberla_codegen_venv_install`, which can be used to install additional packages into the
+code generator virtual environment;
+for instance, the following invocation installs `pyyaml`:
+
+```CMake
+walberla_codegen_venv_install( pyyaml )
+```
+
+The arguments passed to `walberla_codegen_venv_install` are forwarded directly to `pip install`.
+You can therefore use any parameters that `pip` can interpret, for instance `-e` to perform an
+editable install, or `-r <requirements-file>` to install packages from a requirements file.
+
+## Using an External Virtual Environment
+
+To have even more control over your Python environment, you can configure sfg-walberla to
+forego creating a private virtual environment, and instead use the Python interpreter
+supplied from the outside.
+
+To explicitly specify a Python interpreter, you need to set the `WALBERLA_CODEGEN_USE_PRIVATE_VENV` cache
+variable to `FALSE`, and set `Python_EXECUTABLE` to point at your Python binary.
+For instance, at configuration time:
+
+```bash
+cmake -S . -B build -DWALBERLA_CODEGEN_USE_PRIVATE_VENV=FALSE -DPython_EXECUTABLE=<path-to-python>
+```