diff --git a/.github/workflows/tooling.yml b/.github/workflows/tooling.yml
index 1f6ccf244..896d22001 100644
--- a/.github/workflows/tooling.yml
+++ b/.github/workflows/tooling.yml
@@ -53,7 +53,7 @@ jobs:
         export ASAN_OPTIONS=detect_stack_use_after_return=1:detect_leaks=1:check_initialization_order=true:strict_init_order=true:detect_stack_use_after_scope=1:fast_unwind_on_malloc=0
         export LSAN_OPTIONS=suppressions="$SOURCEPATH/.github/ci/sanitizer/clang/Leak.supp:use_tls=0"
         export LD_PRELOAD=/usr/lib/clang/14/lib/linux/libclang_rt.asan-x86_64.so
-        ctest --test-dir build -E AMReX --output-on-failure
+        ctest --test-dir build -E AMReX --output-on-failure --label-exclude=slow
 
 # note: use_tls=0 because of glibc 3.23 on GitHub actions
 # https://github.com/google/sanitizers/issues/1342
diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml
index 7dc20b7e3..29cfef0d9 100644
--- a/.github/workflows/ubuntu.yml
+++ b/.github/workflows/ubuntu.yml
@@ -47,7 +47,7 @@ jobs:
 
     - name: run tests
       run: |
-        ctest --test-dir build --output-on-failure
+        ctest --test-dir build --output-on-failure --label-exclude=slow
 
     - name: run installed app
       run: |
@@ -110,7 +110,7 @@ jobs:
 
     - name: run tests
       run: |
-        ctest --test-dir build --output-on-failure
+        ctest --test-dir build --output-on-failure --label-exclude=slow
 
     - name: run installed app
       run: |
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 3b68c817e..bede36e29 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -19,6 +19,19 @@ if(ImpactX_MPI)
 endif()
 
 
+# Add an ImpactX test case.
+#
+# The test will be run and can optionally perform an analysis and/or a plot step.
+#
+# Parameters
+# ----------
+# name: unique ImpactX test name
+# input: inputs file or Python script
+# is_mpi: run with mpirun?
+# is_python: run as a Python script?
+# analysis_script: an analysis script that validates test output
+# plot_script: a plot script that visualizes test output
+#
 function(add_impactx_test name input is_mpi is_python analysis_script plot_script)
     # cannot run Python tests w/o Python support
     if(is_python AND NOT ImpactX_PYTHON)
@@ -98,6 +111,27 @@ function(add_impactx_test name input is_mpi is_python analysis_script plot_scrip
 endfunction()
 
 
+# Add a CTest label to an ImpactX test
+#
+# The test must already been added with add_impactx_test.
+#
+# Parameters
+# ----------
+# name: unique ImpactX test name
+# label: ctest LABELS property value to add
+#
+function(label_impactx_test name label)
+    # see add_test calls in add_impactx_test for values
+    set(_test_names "${name}.run;${name}.analysis;${name}.plot")
+
+    foreach(_test_name IN LISTS _test_names)
+        if(TEST ${_test_name})
+            set_property(TEST ${_test_name} APPEND PROPERTY LABELS "${label}")
+        endif()
+    endforeach()
+endfunction()
+
+
 # FODO Cell ###################################################################
 #
 add_impactx_test(FODO
@@ -668,6 +702,7 @@ add_impactx_test(thermal
     examples/epac2004_benchmarks/analysis_thermal.py
     OFF  # no plot script yet
 )
+label_impactx_test(thermal slow)
 
 # Bithermal Beam EPAC2004 #####################################################
 #