diff --git a/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md
new file mode 100644
index 0000000000000..f5be63b796c94
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-environment-variables/COLORTERM.md
@@ -0,0 +1,5 @@
+# `COLORTERM`
+
+This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator.
+
+[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html
diff --git a/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md
new file mode 100644
index 0000000000000..46511598e7058
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-environment-variables/QNX_TARGET.md
@@ -0,0 +1,11 @@
+# `QNX_TARGET`
+
+----
+
+This environment variable is mandatory when linking on `nto-qnx*_iosock` platforms. It is used to determine an `-L` path to pass to the QNX linker.
+
+You should [set this variable] by running `source qnxsdp-env.sh`.
+See [the QNX docs] for more background information.
+
+[set this variable]: https://www.qnx.com/developers/docs/qsc/com.qnx.doc.qsc.inst_larg_org/topic/build_server_developer_steps.html
+[the QNX docs]: https://www.qnx.com/developers/docs/7.1/#com.qnx.doc.neutrino.io_sock/topic/migrate_app.html.
diff --git a/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md
new file mode 100644
index 0000000000000..be9ed02f54d39
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-environment-variables/SDKROOT.md
@@ -0,0 +1,6 @@
+# `SDKROOT`
+
+This environment variable is used on Apple targets.
+It is passed through to the linker (currently either as `-isysroot` or `-syslibroot`).
+
+Note that this variable is not always respected. When the SDKROOT is clearly wrong (e.g. when the platform of the SDK does not match the `--target` used by rustc), this is ignored and rustc does its own detection.
diff --git a/src/doc/unstable-book/src/compiler-environment-variables/TERM.md b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md
new file mode 100644
index 0000000000000..9208fd378a3a5
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-environment-variables/TERM.md
@@ -0,0 +1,5 @@
+# `TERM`
+
+This environment variable is used by [`-Zterminal-urls`] to detect if URLs are supported by the terminal emulator.
+
+[`-Zterminal-urls`]: ../compiler-flags/terminal-urls.html
diff --git a/src/doc/unstable-book/src/compiler-flags/terminal-urls.md b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md
new file mode 100644
index 0000000000000..a5427978f258b
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/terminal-urls.md
@@ -0,0 +1,13 @@
+# `-Z terminal-urls`
+
+The tracking feature for this issue is [#125586]
+
+[#125586]: https://github.com/rust-lang/rust/issues/125586
+
+---
+
+This flag takes either a boolean or the string "auto".
+
+When enabled, use the OSC 8 hyperlink terminal specification to print hyperlinks in the compiler output.
+Use "auto" to try and autodetect whether the terminal emulator supports hyperlinks.
+Currently, "auto" only enables hyperlinks if `COLORTERM=truecolor` and `TERM=xterm-256color`.
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index fb00b3a943f87..6618ba24be609 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -9,6 +9,7 @@
 //! * All unstable lang features have tests to ensure they are actually unstable.
 //! * Language features in a group are sorted by feature name.
 
+use std::collections::BTreeSet;
 use std::collections::hash_map::{Entry, HashMap};
 use std::ffi::OsStr;
 use std::num::NonZeroU32;
@@ -21,6 +22,7 @@ use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many};
 mod tests;
 
 mod version;
+use regex::Regex;
 use version::Version;
 
 const FEATURE_GROUP_START_PREFIX: &str = "// feature-group-start";
@@ -623,3 +625,36 @@ fn map_lib_features(
         },
     );
 }
+
+fn should_document(var: &str) -> bool {
+    if var.starts_with("RUSTC_") || var.starts_with("RUST_") || var.starts_with("UNSTABLE_RUSTDOC_")
+    {
+        return true;
+    }
+    ["SDKROOT", "QNX_TARGET", "COLORTERM", "TERM"].contains(&var)
+}
+
+pub fn collect_env_vars(compiler: &Path) -> BTreeSet<String> {
+    let env_var_regex: Regex = Regex::new(r#"env::var(_os)?\("([^"]+)"#).unwrap();
+
+    let mut vars = BTreeSet::new();
+    walk(
+        compiler,
+        // skip build scripts, tests, and non-rust files
+        |path, _is_dir| {
+            filter_dirs(path)
+                || filter_not_rust(path)
+                || path.ends_with("build.rs")
+                || path.ends_with("tests.rs")
+        },
+        &mut |_entry, contents| {
+            for env_var in env_var_regex.captures_iter(contents).map(|c| c.get(2).unwrap().as_str())
+            {
+                if should_document(env_var) {
+                    vars.insert(env_var.to_owned());
+                }
+            }
+        },
+    );
+    vars
+}
diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs
index 9dc9d42d46600..0ed954d48deb1 100644
--- a/src/tools/tidy/src/unstable_book.rs
+++ b/src/tools/tidy/src/unstable_book.rs
@@ -6,6 +6,8 @@ use crate::features::{CollectedFeatures, Features, Status};
 
 pub const PATH_STR: &str = "doc/unstable-book";
 
+pub const ENV_VARS_DIR: &str = "src/compiler-environment-variables";
+
 pub const COMPILER_FLAGS_DIR: &str = "src/compiler-flags";
 
 pub const LANG_FEATURES_DIR: &str = "src/language-features";
diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs
index 159a1d0fa1745..a7c6173d88c48 100644
--- a/src/tools/unstable-book-gen/src/main.rs
+++ b/src/tools/unstable-book-gen/src/main.rs
@@ -5,11 +5,11 @@ use std::env;
 use std::fs::{self, write};
 use std::path::Path;
 
-use tidy::features::{Features, collect_lang_features, collect_lib_features};
+use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features};
 use tidy::t;
 use tidy::unstable_book::{
-    LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR, collect_unstable_book_section_file_names,
-    collect_unstable_feature_names,
+    ENV_VARS_DIR, LANG_FEATURES_DIR, LIB_FEATURES_DIR, PATH_STR,
+    collect_unstable_book_section_file_names, collect_unstable_feature_names,
 };
 
 fn generate_stub_issue(path: &Path, name: &str, issue: u32, description: &str) {
@@ -27,6 +27,11 @@ fn generate_stub_no_issue(path: &Path, name: &str, description: &str) {
     t!(write(path, content), path);
 }
 
+fn generate_stub_env_var(path: &Path, name: &str) {
+    let content = format!(include_str!("stub-env-var.md"), name = name);
+    t!(write(path, content), path);
+}
+
 fn set_to_summary_str(set: &BTreeSet<String>, dir: &str) -> String {
     set.iter()
         .map(|ref n| format!("    - [{}]({}/{}.md)", n.replace('-', "_"), dir, n))
@@ -59,7 +64,7 @@ fn generate_summary(path: &Path, lang_features: &Features, lib_features: &Featur
     t!(write(&summary_path, content), summary_path);
 }
 
-fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) {
+fn generate_feature_files(src: &Path, out: &Path, features: &Features) {
     let unstable_features = collect_unstable_feature_names(features);
     let unstable_section_file_names = collect_unstable_book_section_file_names(src);
     t!(fs::create_dir_all(&out));
@@ -83,6 +88,16 @@ fn generate_unstable_book_files(src: &Path, out: &Path, features: &Features) {
     }
 }
 
+fn generate_env_files(src: &Path, out: &Path, env_vars: &BTreeSet<String>) {
+    let env_var_file_names = collect_unstable_book_section_file_names(src);
+    t!(fs::create_dir_all(&out));
+    for env_var in env_vars - &env_var_file_names {
+        let file_name = format!("{env_var}.md");
+        let out_file_path = out.join(&file_name);
+        generate_stub_env_var(&out_file_path, &env_var);
+    }
+}
+
 fn copy_recursive(from: &Path, to: &Path) {
     for entry in t!(fs::read_dir(from)) {
         let e = t!(entry);
@@ -112,21 +127,23 @@ fn main() {
         .into_iter()
         .filter(|&(ref name, _)| !lang_features.contains_key(name))
         .collect();
+    let env_vars = collect_env_vars(compiler_path);
 
     let doc_src_path = src_path.join(PATH_STR);
 
     t!(fs::create_dir_all(&dest_path));
 
-    generate_unstable_book_files(
+    generate_feature_files(
         &doc_src_path.join(LANG_FEATURES_DIR),
         &dest_path.join(LANG_FEATURES_DIR),
         &lang_features,
     );
-    generate_unstable_book_files(
+    generate_feature_files(
         &doc_src_path.join(LIB_FEATURES_DIR),
         &dest_path.join(LIB_FEATURES_DIR),
         &lib_features,
     );
+    generate_env_files(&doc_src_path.join(ENV_VARS_DIR), &dest_path.join(ENV_VARS_DIR), &env_vars);
 
     copy_recursive(&doc_src_path, &dest_path);
 
diff --git a/src/tools/unstable-book-gen/src/stub-env-var.md b/src/tools/unstable-book-gen/src/stub-env-var.md
new file mode 100644
index 0000000000000..e8a7ddb855a67
--- /dev/null
+++ b/src/tools/unstable-book-gen/src/stub-env-var.md
@@ -0,0 +1,9 @@
+# `{name}`
+
+Environment variables have no tracking issue. This environment variable has no documentation, and therefore is likely internal to the compiler and not meant for general use.
+
+See [the code][github search] for more information.
+
+[github search]: https://github.com/search?q=repo%3Arust-lang%2Frust+%22{name}%22+path%3Acompiler&type=code
+
+------------------------
