diff --git a/docs/changelog/3158.bugfix.rst b/docs/changelog/3158.bugfix.rst
new file mode 100644
index 00000000..e7391238
--- /dev/null
+++ b/docs/changelog/3158.bugfix.rst
@@ -0,0 +1,1 @@
+``--parallel-no-spinner`` flag now implies ``--parallel``
diff --git a/docs/user_guide.rst b/docs/user_guide.rst
index d08be62a..73a475df 100644
--- a/docs/user_guide.rst
+++ b/docs/user_guide.rst
@@ -394,8 +394,8 @@ Parallel mode
   - ``auto`` to limit it to CPU count,
   - or pass an integer to set that limit.
 - Parallel mode displays a progress spinner while running tox environments in parallel, and reports outcome of these as
-  soon as they have been completed with a human readable duration timing attached. This spinner can be disabled via the
-  ``--parallel-no-spinner`` flag.
+  soon as they have been completed with a human readable duration timing attached. To run parallelly without the spinner,
+  you can use the ``--parallel-no-spinner`` flag.
 - Parallel mode by default shows output only of failed environments and ones marked as :ref:`parallel_show_output`
   ``=True``.
 - There's now a concept of dependency between environments (specified via :ref:`depends`), tox will re-order the
diff --git a/src/tox/session/cmd/legacy.py b/src/tox/session/cmd/legacy.py
index 92a91fcf..a78d8bac 100644
--- a/src/tox/session/cmd/legacy.py
+++ b/src/tox/session/cmd/legacy.py
@@ -110,7 +110,7 @@ def legacy(state: State) -> int:
             option.env = CliEnv(["py"])
         option.devenv_path = Path(option.devenv_path)
         return devenv(state)
-    if option.parallel != 0:  # only 0 means sequential
+    if option.parallel_no_spinner is True or option.parallel != 0:  # only 0 means sequential
         return run_parallel(state)
     return run_sequential(state)
 
diff --git a/src/tox/session/cmd/run/parallel.py b/src/tox/session/cmd/run/parallel.py
index 9b7e2843..d02eb1f0 100644
--- a/src/tox/session/cmd/run/parallel.py
+++ b/src/tox/session/cmd/run/parallel.py
@@ -74,7 +74,7 @@ def parallel_flags(
         "--parallel-no-spinner",
         action="store_true",
         dest="parallel_no_spinner",
-        help="do not show the spinner",
+        help="run tox environments in parallel, but don't show the spinner, implies --parallel",
     )
 
 
@@ -83,7 +83,7 @@ def run_parallel(state: State) -> int:
     option = state.conf.options
     return execute(
         state,
-        max_workers=option.parallel,
+        max_workers=None if option.parallel_no_spinner is True else option.parallel,
         has_spinner=option.parallel_no_spinner is False and option.parallel_live is False,
         live=option.parallel_live,
     )

diff --git a/tests/session/cmd/test_parallel.py b/tests/session/cmd/test_parallel.py
index 8ab93a78..a546a263 100644
--- a/tests/session/cmd/test_parallel.py
+++ b/tests/session/cmd/test_parallel.py
@@ -6,9 +6,11 @@ from signal import SIGINT
 from subprocess import PIPE, Popen
 from time import sleep
 from typing import TYPE_CHECKING
+from unittest import mock
 
 import pytest
 
+from tox.session.cmd.run import parallel
 from tox.session.cmd.run.parallel import parse_num_processes
 from tox.tox_env.api import ToxEnv
 from tox.tox_env.errors import Fail
@@ -169,3 +171,28 @@ def test_parallel_requires_arg(tox_project: ToxProjectCreator) -> None:
     outcome = tox_project({"tox.ini": ""}).run("p", "-p", "-h")
     outcome.assert_failed()
     assert "argument -p/--parallel: expected one argument" in outcome.err
+
+
+def test_parallel_no_spinner(tox_project: ToxProjectCreator) -> None:
+    """Ensure passing `--parallel-no-spinner` implies `--parallel`."""
+    with mock.patch.object(parallel, "execute") as mocked:
+        tox_project({"tox.ini": ""}).run("p", "--parallel-no-spinner")
+
+    mocked.assert_called_once_with(
+        mock.ANY,
+        max_workers=None,
+        has_spinner=False,
+        live=False,
+    )
+
+
+def test_parallel_no_spinner_legacy(tox_project: ToxProjectCreator) -> None:
+    with mock.patch.object(parallel, "execute") as mocked:
+        tox_project({"tox.ini": ""}).run("--parallel-no-spinner")
+
+    mocked.assert_called_once_with(
+        mock.ANY,
+        max_workers=None,
+        has_spinner=False,
+        live=False,
+    )

`--parallel-no-spinner` should imply `--parallel`
## Issue

The flag `--parallel-no-spinner` reads like "the tests should run parallel, but don't show spinner". But if you just pass `--parallel-no-spinner` without also passing `--parallel`, it does nothing. I think the presence of this flag implies that we want to run the tests parallelly.

tests/session/cmd/test_parallel.py::test_parallel_no_spinner tests/session/cmd/test_parallel.py::test_parallel_no_spinner_legacy
tests/session/cmd/test_parallel.py::test_parse_num_processes_all tests/session/cmd/test_parallel.py::test_parse_num_processes_auto tests/session/cmd/test_parallel.py::test_parse_num_processes_exact tests/session/cmd/test_parallel.py::test_parse_num_processes_not_number tests/session/cmd/test_parallel.py::test_parse_num_processes_minus_one tests/session/cmd/test_parallel.py::test_parallel_general tests/session/cmd/test_parallel.py::test_parallel_run_live_out tests/session/cmd/test_parallel.py::test_parallel_show_output_with_pkg tests/session/cmd/test_parallel.py::test_keyboard_interrupt tests/session/cmd/test_parallel.py::test_parallels_help tests/session/cmd/test_parallel.py::test_parallel_legacy_accepts_no_arg tests/session/cmd/test_parallel.py::test_parallel_requires_arg