diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c2002409c1db263e86a184ef005d84d1e8594431..5dd5539ca8218b0277c496802529134daac34e4d 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -54,8 +54,6 @@ benchmarks_and_profiling:
         - tests
     stage: benchmarks_and_profiling
     only:
-#        refs:
-#            - master
         variables:
             - $BENCHMARKS_AND_PROFILING
     script:
diff --git a/benchmarks/benchmark_all_examples.py b/benchmarks/benchmark_all_examples.py
index 676bfe16ba94d53682ff6e52d7b714d8fa1c2d8b..5687ad8a2be4ae02fb2f0b14f2c5873fe89e798e 100644
--- a/benchmarks/benchmark_all_examples.py
+++ b/benchmarks/benchmark_all_examples.py
@@ -9,6 +9,10 @@ import pkg_resources
 from benchmarker import Benchmarker
 from importlib_resources import path
 
+from flatland.utils import graphics_pil
+
+graphics_pil.unattended_switch = True
+
 for entry in [entry for entry in importlib_resources.contents('examples') if
               not pkg_resources.resource_isdir('examples', entry)
               and entry.endswith(".py")
diff --git a/benchmarks/benchmark_utils.py b/benchmarks/benchmark_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..720590be315ca03abd07838c4475a5c586edf8dd
--- /dev/null
+++ b/benchmarks/benchmark_utils.py
@@ -0,0 +1,35 @@
+import contextlib
+
+
+# Copied from https://docs.python.org/3/library/test.html
+
+@contextlib.contextmanager
+def swap_attr(obj, attr, new_val):
+    """Temporary swap out an attribute with a new object.
+
+    Usage:
+        with swap_attr(obj, "attr", 5):
+            ...
+
+        This will set obj.attr to 5 for the duration of the with: block,
+        restoring the old value at the end of the block. If `attr` doesn't
+        exist on `obj`, it will be created and then deleted at the end of the
+        block.
+
+        The old value (or None if it doesn't exist) will be assigned to the
+        target of the "as" clause, if there is one.
+    """
+    if hasattr(obj, attr):
+        real_val = getattr(obj, attr)
+        setattr(obj, attr, new_val)
+        try:
+            yield real_val
+        finally:
+            setattr(obj, attr, real_val)
+    else:
+        setattr(obj, attr, new_val)
+        try:
+            yield
+        finally:
+            if hasattr(obj, attr):
+                delattr(obj, attr)
diff --git a/benchmarks/profile_all_examples.py b/benchmarks/profile_all_examples.py
index 0b6db571e263567e72dc0f955ce478a9384a02d6..b41f52a8cb9a9d261dcaf656b7aa4a11e8885374 100644
--- a/benchmarks/profile_all_examples.py
+++ b/benchmarks/profile_all_examples.py
@@ -2,19 +2,17 @@ import cProfile
 import runpy
 import sys
 from io import StringIO
-from test.support import swap_attr
 
 import importlib_resources
 import pkg_resources
 from importlib_resources import path
 
+from benchmarks.benchmark_utils import swap_attr
+from flatland.utils import graphics_pil
+
 
 def profile(resource, entry):
     with path(resource, entry) as file_in:
-        # we use the test package, which is meant for internal use by Python only internal and
-        # Any use of this package outside of Python’s standard library is discouraged as code (..)
-        # can change or be removed without notice between releases of Python.
-        # https://docs.python.org/3/library/test.html
         # TODO remove input() from examples
         print("*****************************************************************")
         print("Profiling {}".format(entry))
@@ -27,6 +25,8 @@ def profile(resource, entry):
             cProfile.run('my_func()', sort='time')
 
 
+graphics_pil.unattended_switch = True
+
 for entry in [entry for entry in importlib_resources.contents('examples') if
               not pkg_resources.resource_isdir('examples', entry)
               and entry.endswith(".py")
diff --git a/benchmarks/run_all_examples.py b/benchmarks/run_all_examples.py
new file mode 100644
index 0000000000000000000000000000000000000000..972ac68ee862e4e80a342cf4d5133a42b28d3df2
--- /dev/null
+++ b/benchmarks/run_all_examples.py
@@ -0,0 +1,29 @@
+import runpy
+import sys
+from io import StringIO
+
+import importlib_resources
+import pkg_resources
+from importlib_resources import path
+
+from benchmarks.benchmark_utils import swap_attr
+from flatland.utils import graphics_pil
+
+graphics_pil.unattended_switch = True
+
+for entry in [entry for entry in importlib_resources.contents('examples') if
+              not pkg_resources.resource_isdir('examples', entry)
+              and entry.endswith(".py")
+              and '__init__' not in entry
+              and 'example_basic_elements_test' not in entry
+              and 'demo.py' not in entry
+              ]:
+    with path('examples', entry) as file_in:
+        print("")
+        print("")
+        print("")
+        print("*****************************************************************")
+        print("Running {}".format(entry))
+        print("*****************************************************************")
+        with swap_attr(sys, "stdin", StringIO("q")):
+            runpy.run_path(file_in, run_name="__main__")
diff --git a/flatland/utils/graphics_pil.py b/flatland/utils/graphics_pil.py
index 2332ecf3f0ba2e9ae244d8e4c4c3ab057652f195..22a5c5f25185dd3957470cf7c33c8d6fafd7cda1 100644
--- a/flatland/utils/graphics_pil.py
+++ b/flatland/utils/graphics_pil.py
@@ -33,6 +33,9 @@ from flatland.core.grid.rail_env_grid import RailEnvTransitions  # noqa: E402
 
 
 class PILGL(GraphicsLayer):
+    # hack: in continuous integration, we run multiple
+    unattended_switch = False
+
     def __init__(self, width, height, jupyter=False):
         self.yxBase = (0, 0)
         self.linewidth = 4
@@ -157,9 +160,14 @@ class PILGL(GraphicsLayer):
 
     def open_window(self):
         assert self.window_open is False, "Window is already open!"
-        # use tk.Toplevel() instead of tk.Tk()
-        # https://stackoverflow.com/questions/26097811/image-pyimage2-doesnt-exist
-        self.window_root = tk.Tk()
+        if self.unattended_switch:
+            # use tk.Toplevel() instead of tk.Tk() since we run all examples from the same python script
+            # https://stackoverflow.com/questions/26097811/image-pyimage2-doesnt-exist
+            tk.Toplevel()
+        else:
+            tk.Tk()
+
+        self.window_root = tk.Toplevel()
         self.window_root.withdraw()
         self.window = tk.Toplevel(self.window_root)
         self.window.title("Flatland")
diff --git a/tox.ini b/tox.ini
index b64a20a8db0e6e3df846a0c05bd1e372f93f9136..6e4223172b83be922ac0fa97f981c6ea4c48c1ea 100644
--- a/tox.ini
+++ b/tox.ini
@@ -52,6 +52,7 @@ setenv =
     PYTHONPATH = {toxinidir}
 passenv =
     DISPLAY
+    XAUTHORITY
 ; HTTP_PROXY+HTTPS_PROXY required behind corporate proxies
     HTTP_PROXY
     HTTPS_PROXY
@@ -68,6 +69,7 @@ setenv =
     PYTHONPATH = {toxinidir}
 passenv =
     DISPLAY
+    XAUTHORITY
 ; HTTP_PROXY+HTTPS_PROXY required behind corporate proxies
     HTTP_PROXY
     HTTPS_PROXY
@@ -94,8 +96,7 @@ deps =
 commands =
 ; run examples from subfolder to ensure that resources are accessed via resources and not via relative paths
     sh -c 'mkdir -p {envtmpdir}/c236d3c240d61a0969d4cb59e2180ce5'
-; pipe echo into python since some examples expect input to close the window after the example is run
-    sh -c 'cd {envtmpdir}/c236d3c240d61a0969d4cb59e2180ce5 && ls {toxinidir}/examples/*.py | xargs -I{} -n 1 sh -c "echo -e \"\n====== Running {} ========\n\"; echo "q" | python {}"'
+    sh -c 'cd {envtmpdir}/c236d3c240d61a0969d4cb59e2180ce5 &&  python {toxinidir}/benchmarks/run_all_examples.py'
 
 [testenv:notebooks]
 basepython = python