diff --git a/docs/changelog/3318.feature.rst b/docs/changelog/3318.feature.rst
new file mode 100644
index 000000000..eaa5ba350
--- /dev/null
+++ b/docs/changelog/3318.feature.rst
@@ -0,0 +1 @@
+Suppress spinner in parallel runs in CI - by :user:`ziima`.
diff --git a/src/tox/session/cmd/run/parallel.py b/src/tox/session/cmd/run/parallel.py
index 3ba22af07..d83d72a24 100644
--- a/src/tox/session/cmd/run/parallel.py
+++ b/src/tox/session/cmd/run/parallel.py
@@ -8,6 +8,7 @@
 
 from tox.plugin import impl
 from tox.session.env_select import CliEnv, register_env_select_flags
+from tox.util.ci import is_ci
 from tox.util.cpu import auto_detect_cpus
 
 from .common import env_run_create_flags, execute
@@ -28,7 +29,7 @@ def tox_add_option(parser: ToxParser) -> None:
     our = parser.add_command("run-parallel", ["p"], "run environments in parallel", run_parallel)
     register_env_select_flags(our, default=CliEnv())
     env_run_create_flags(our, mode="run-parallel")
-    parallel_flags(our, default_parallel=DEFAULT_PARALLEL)
+    parallel_flags(our, default_parallel=DEFAULT_PARALLEL, default_spinner=is_ci())
 
 
 def parse_num_processes(str_value: str) -> int | None:
@@ -51,6 +52,8 @@ def parallel_flags(
     our: ArgumentParser,
     default_parallel: int | str,
     no_args: bool = False,  # noqa: FBT001, FBT002
+    *,
+    default_spinner: bool = False,
 ) -> None:
     our.add_argument(
         "-p",
@@ -75,7 +78,11 @@ def parallel_flags(
         "--parallel-no-spinner",
         action="store_true",
         dest="parallel_no_spinner",
-        help="run tox environments in parallel, but don't show the spinner, implies --parallel",
+        default=default_spinner,
+        help=(
+            "run tox environments in parallel, but don't show the spinner, implies --parallel. "
+            "Disabled by default if CI is detected (not in legacy API)."
+        ),
     )
 
 
diff --git a/tests/session/cmd/test_legacy.py b/tests/session/cmd/test_legacy.py
index 6b4bd2433..c85fb82c7 100644
--- a/tests/session/cmd/test_legacy.py
+++ b/tests/session/cmd/test_legacy.py
@@ -1,6 +1,8 @@
 from __future__ import annotations
 
+import os
 from typing import TYPE_CHECKING
+from unittest.mock import patch
 
 import pytest
 
@@ -121,6 +123,16 @@ def test_legacy_run_sequential(tox_project: ToxProjectCreator, mocker: MockerFix
     assert run_sequential.call_count == 1
 
 
+def test_legacy_run_sequential_ci(tox_project: ToxProjectCreator, mocker: MockerFixture) -> None:
+    """Test legacy run sequential in CI by default."""
+    run_sequential = mocker.patch("tox.session.cmd.legacy.run_sequential")
+
+    with patch.dict(os.environ, {"CI": "1"}):
+        tox_project({"tox.ini": ""}).run("le", "-e", "py")
+
+    assert run_sequential.call_count == 1
+
+
 def test_legacy_help(tox_project: ToxProjectCreator) -> None:
     outcome = tox_project({"tox.ini": ""}).run("le", "-h")
     outcome.assert_success()
diff --git a/tests/session/cmd/test_parallel.py b/tests/session/cmd/test_parallel.py
index 01b5b6bc7..ab75a62a6 100644
--- a/tests/session/cmd/test_parallel.py
+++ b/tests/session/cmd/test_parallel.py
@@ -1,5 +1,6 @@
 from __future__ import annotations
 
+import os
 import sys
 from argparse import ArgumentTypeError
 from signal import SIGINT
@@ -186,6 +187,19 @@ def test_parallel_no_spinner(tox_project: ToxProjectCreator) -> None:
     )
 
 
+def test_parallel_no_spinner_ci(tox_project: ToxProjectCreator) -> None:
+    """Ensure spinner is disabled by default in CI."""
+    with mock.patch.object(parallel, "execute") as mocked, mock.patch.dict(os.environ, {"CI": "1"}):
+        tox_project({"tox.ini": ""}).run("p")
+
+    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")