diff --git a/doc/manual/src/command-ref/nix-collect-garbage.md b/doc/manual/src/command-ref/nix-collect-garbage.md
index 51db5fc67..a679ceaf7 100644
--- a/doc/manual/src/command-ref/nix-collect-garbage.md
+++ b/doc/manual/src/command-ref/nix-collect-garbage.md
@@ -1,6 +1,6 @@
# Name
-`nix-collect-garbage` - delete unreachable store paths
+`nix-collect-garbage` - delete unreachable [store objects]
# Synopsis
@@ -8,17 +8,57 @@
# Description
-The command `nix-collect-garbage` is mostly an alias of [`nix-store
---gc`](@docroot@/command-ref/nix-store/gc.md), that is, it deletes all
-unreachable paths in the Nix store to clean up your system. However,
-it provides two additional options: `-d` (`--delete-old`), which
-deletes all old generations of all profiles in `/nix/var/nix/profiles`
-by invoking `nix-env --delete-generations old` on all profiles (of
-course, this makes rollbacks to previous configurations impossible);
-and `--delete-older-than` *period*, where period is a value such as
-`30d`, which deletes all generations older than the specified number
-of days in all profiles in `/nix/var/nix/profiles` (except for the
-generations that were active at that point in time).
+The command `nix-collect-garbage` is mostly an alias of [`nix-store --gc`](@docroot@/command-ref/nix-store/gc.md).
+That is, it deletes all unreachable [store objects] in the Nix store to clean up your system.
+
+However, it provides two additional options,
+[`--delete-old`](#opt-delete-old) and [`--delete-older-than`](#opt-delete-older-than),
+which also delete old [profiles], allowing potentially more [store objects] to be deleted because profiles are also garbage collection roots.
+These options are the equivalent of running
+[`nix-env --delete-generations`](@docroot@/command-ref/nix-env/delete-generations.md)
+with various augments on multiple profiles,
+prior to running `nix-collect-garbage` (or just `nix-store --gc`) without any flags.
+
+> **Note**
+>
+> Deleting previous configurations makes rollbacks to them impossible.
+
+These flags should be used with care, because they potentially delete generations of profiles used by other users on the system.
+
+## Locations searched for profiles
+
+`nix-collect-garbage` cannot know about all profiles; that information doesn't exist.
+Instead, it looks in a few locations, and acts on all profiles it finds there:
+
+1. The default profile locations as specified in the [profiles] section of the manual.
+
+2. > **NOTE**
+ >
+ > Not stable; subject to change
+ >
+ > Do not rely on this functionality; it just exists for migration purposes and is may change in the future.
+ > These deprecated paths remain a private implementation detail of Nix.
+
+ `$NIX_STATE_DIR/profiles` and `$NIX_STATE_DIR/profiles/per-user`.
+
+ With the exception of `$NIX_STATE_DIR/profiles/per-user/root` and `$NIX_STATE_DIR/profiles/default`, these directories are no longer used by other commands.
+ `nix-collect-garbage` looks there anyways in order to clean up profiles from older versions of Nix.
+
+# Options
+
+These options are for deleting old [profiles] prior to deleting unreachable [store objects].
+
+- [`--delete-old`](#opt-delete-old) / `-d`\
+ Delete all old generations of profiles.
+
+ This is the equivalent of invoking `nix-env --delete-generations old` on each found profile.
+
+- [`--delete-older-than`](#opt-delete-older-than) *period*\
+ Delete all generations of profiles older than the specified amount (except for the generations that were active at that point in time).
+ *period* is a value such as `30d`, which would mean 30 days.
+
+ This is the equivalent of invoking [`nix-env --delete-generations `](@docroot@/command-ref/nix-env/delete-generations.md#generations-days) on each found profile.
+ See the documentation of that command for additional information about the *period* argument.
{{#include ./opt-common.md}}
@@ -32,3 +72,6 @@ generations of each profile, do
```console
$ nix-collect-garbage -d
```
+
+[profiles]: @docroot@/command-ref/files/profiles.md
+[store objects]: @docroot@/glossary.md#gloss-store-object
diff --git a/doc/manual/src/command-ref/nix-env/delete-generations.md b/doc/manual/src/command-ref/nix-env/delete-generations.md
index 92cb7f0d9..d828a5b9e 100644
--- a/doc/manual/src/command-ref/nix-env/delete-generations.md
+++ b/doc/manual/src/command-ref/nix-env/delete-generations.md
@@ -9,14 +9,39 @@
# Description
This operation deletes the specified generations of the current profile.
-The generations can be a list of generation numbers, the special value
-`old` to delete all non-current generations, a value such as `30d` to
-delete all generations older than the specified number of days (except
-for the generation that was active at that point in time), or a value
-such as `+5` to keep the last `5` generations ignoring any newer than
-current, e.g., if `30` is the current generation `+5` will delete
-generation `25` and all older generations. Periodically deleting old
-generations is important to make garbage collection effective.
+
+*generations* can be a one of the following:
+
+- `...`:\
+ A list of generation numbers, each one a separate command-line argument.
+
+ Delete exactly the profile generations given by their generation number.
+ Deleting the current generation is not allowed.
+
+- The special value `old`
+
+ Delete all generations older than the current one.
+
+- `d`:\
+ The last *days* days
+
+ *Example*: `30d`
+
+ Delete all generations older than *days* days.
+ The generation that was active at that point in time is excluded, and will not be deleted.
+
+- `+`:\
+ The last *count* generations up to the present
+
+ *Example*: `+5`
+
+ Keep the last *count* generations, along with any newer than current.
+
+Periodically deleting old generations is important to make garbage collection
+effective.
+The is because profiles are also garbage collection roots — any [store object] reachable from a profile is "alive" and ineligible for deletion.
+
+[store object]: @docroot@/glossary.md#gloss-store-object
{{#include ./opt-common.md}}
@@ -28,19 +53,35 @@ generations is important to make garbage collection effective.
# Examples
+## Delete explicit generation numbers
+
```console
$ nix-env --delete-generations 3 4 8
```
+Delete the generations numbered 3, 4, and 8, so long as the current active generation is not any of those.
+
+## Keep most-recent by count count
+
```console
$ nix-env --delete-generations +5
```
+Suppose `30` is the current generation, and we currently have generations numbered `20` through `32`.
+
+Then this command will delete generations `20` through `25` (`<= 30 - 5`),
+and keep generations `26` through `31` (`> 30 - 5`).
+
+## Keep most-recent in days
+
```console
$ nix-env --delete-generations 30d
```
+This command will delete all generations older than 30 days, except for the generation that was active 30 days ago (if it currently exists).
+
+## Delete all older
+
```console
$ nix-env --profile other_profile --delete-generations old
```
-
diff --git a/tests/gc.sh b/tests/gc.sh
index 95669e25c..ad09a8b39 100644
--- a/tests/gc.sh
+++ b/tests/gc.sh
@@ -50,31 +50,3 @@ if test -e $outPath/foobar; then false; fi
# Check that the store is empty.
rmdir $NIX_STORE_DIR/.links
rmdir $NIX_STORE_DIR
-
-## Test `nix-collect-garbage -d`
-testCollectGarbageD () {
- clearProfiles
- # Run two `nix-env` commands, should create two generations of
- # the profile
- nix-env -f ./user-envs.nix -i foo-1.0
- nix-env -f ./user-envs.nix -i foo-2.0pre1
- [[ $(nix-env --list-generations | wc -l) -eq 2 ]]
-
- # Clear the profile history. There should be only one generation
- # left
- nix-collect-garbage -d
- [[ $(nix-env --list-generations | wc -l) -eq 1 ]]
-}
-# `nix-env` doesn't work with CA derivations, so let's ignore that bit if we're
-# using them
-if [[ -z "${NIX_TESTS_CA_BY_DEFAULT:-}" ]]; then
- testCollectGarbageD
-
- # Run the same test, but forcing the profiles at their legacy location under
- # /nix/var/nix.
- #
- # Regression test for #8294
- rm ~/.nix-profile
- ln -s $NIX_STATE_DIR/profiles/per-user/me ~/.nix-profile
- testCollectGarbageD
-fi
diff --git a/tests/local.mk b/tests/local.mk
index 9e340e2e2..2da47d243 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -16,6 +16,7 @@ nix_tests = \
flakes/flake-in-submodule.sh \
ca/gc.sh \
gc.sh \
+ nix-collect-garbage-d.sh \
remote-store.sh \
legacy-ssh-store.sh \
lang.sh \
diff --git a/tests/nix-collect-garbage-d.sh b/tests/nix-collect-garbage-d.sh
new file mode 100644
index 000000000..bf30f8938
--- /dev/null
+++ b/tests/nix-collect-garbage-d.sh
@@ -0,0 +1,40 @@
+source common.sh
+
+clearStore
+
+## Test `nix-collect-garbage -d`
+
+# TODO make `nix-env` doesn't work with CA derivations, and make
+# `ca/nix-collect-garbage-d.sh` wrapper.
+
+testCollectGarbageD () {
+ clearProfiles
+ # Run two `nix-env` commands, should create two generations of
+ # the profile
+ nix-env -f ./user-envs.nix -i foo-1.0 "$@"
+ nix-env -f ./user-envs.nix -i foo-2.0pre1 "$@"
+ [[ $(nix-env --list-generations "$@" | wc -l) -eq 2 ]]
+
+ # Clear the profile history. There should be only one generation
+ # left
+ nix-collect-garbage -d
+ [[ $(nix-env --list-generations "$@" | wc -l) -eq 1 ]]
+}
+
+testCollectGarbageD
+
+# Run the same test, but forcing the profiles an arbitrary location.
+rm ~/.nix-profile
+ln -s $TEST_ROOT/blah ~/.nix-profile
+testCollectGarbageD
+
+# Run the same test, but forcing the profiles at their legacy location under
+# /nix/var/nix.
+#
+# Note that we *don't* use the default profile; `nix-collect-garbage` will
+# need to check the legacy conditional unconditionally not just follow
+# `~/.nix-profile` to pass this test.
+#
+# Regression test for #8294
+rm ~/.nix-profile
+testCollectGarbageD --profile "$NIX_STATE_DIR/profiles/per-user/me"