diff --git a/Makefile.in b/Makefile.in
index e684a09184a782c8454d65053dc12c385b73b9b4..3655a645a47008cd91f75561c8b8e25457bac1f3 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -37,7 +37,7 @@ start:
 	@echo Starting the web server on 0.0.0.0:${WEBSERVER_PORT}
 	@#
 	@tmux send-keys -t ${SESSION_NAME}:main.3  \
-	    "gunicorn -w 4 -b 0.0.0.0:${WEBSERVER_PORT} km3mon:app" Enter
+	    "gunicorn --pid gunicorn.pid -w 4 -b 0.0.0.0:${WEBSERVER_PORT} km3mon:app" Enter
 	@tmux select-layout even-vertical
 
 	@echo Starting the monitoring scripts
@@ -99,17 +99,21 @@ start:
 	    "python scripts/k40_calibration.py -d ${DETECTOR_ID} -p ${MONITORING_LIGIER_PORT}" Enter
 	@tmux select-layout even-vertical
 
-	@# Log.io
+	@# Logs
 	@#
-	@tmux new-window -n logio -t ${SESSION_NAME}
-	@tmux split-window -v -t ${SESSION_NAME}:logio
-	@tmux send-keys -t ${SESSION_NAME}:logio.1  \
-	    "python scripts/logio.py -p ${MONITORING_LIGIER_PORT} -x ${LOGIO_IP} -q ${LOGIO_PORT}" Enter
+	@tmux new-window -n log -t ${SESSION_NAME}
+	@tmux split-window -v -t ${SESSION_NAME}:log
+	@tmux send-keys -t ${SESSION_NAME}:log.1  \
+	    "python scripts/msg_dumper.py -l 127.0.0.1 -p ${MONITORING_LIGIER_PORT} -f logs/MSG.log" Enter
+	@tmux send-keys -t ${SESSION_NAME}:log.2  \
+	    "touch logs/MSG.log && frontail logs/*.log --ui-highlight --ui-highlight-preset frontail.json --theme dark -l 10000 -n 200 -p 8082" Enter
 	@tmux select-layout even-vertical
 
 stop:
+	@echo Stopping monitoring session...
 	tmux kill-session -t ${SESSION_NAME}
-	killall gunicorn
+	kill -9 $(shell cat gunicorn.pid)
+	@sleep 5
 
 clean:
 	rm Makefile
diff --git a/logs/.gitkeep b/logs/.gitkeep
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/scripts/live_triggermap.py b/scripts/live_triggermap.py
index 5cfcf6f4e8b5d189cb93768e5557be1e8e99efeb..74dc41842d987e8a02effe8e9474dff36b4a7eb2 100755
--- a/scripts/live_triggermap.py
+++ b/scripts/live_triggermap.py
@@ -19,7 +19,7 @@ Options:
 from __future__ import division
 
 from datetime import datetime
-from collections import deque
+from collections import deque, defaultdict
 import os
 import shutil
 import time
@@ -54,16 +54,20 @@ from km3pipe.logger import logging
 lock = threading.Lock()
 
 
-class DOMHits(Module):
+class TriggerMap(Module):
     def configure(self):
         self.plots_path = self.require('plots_path')
         det_id = self.require('det_id')
+        self.max_events = self.get("max_events", default=1000)
         self.det = kp.hardware.Detector(det_id=det_id)
 
         self.run = True
-        self.max_events = 1000
-        self.hits = deque(maxlen=1000)
-        self.triggered_hits = deque(maxlen=1000)
+        self.hits = deque(maxlen=self.max_events)
+        self.triggered_hits = deque(maxlen=self.max_events)
+        self.runchanges = defaultdict(int)
+        self.current_run_id = 0
+        self.n_events = 0
+
         self.thread = threading.Thread(target=self.plot).start()
 
     def process(self, blob):
@@ -74,6 +78,19 @@ class DOMHits(Module):
 
         event_hits = blob['Hits']
         with lock:
+            run_id = blob['EventInfo'].run_id[0]
+            if run_id > self.current_run_id:
+                self.current_run_id = run_id
+            for _run_id in set(list(self.runchanges.keys()) + [run_id]):
+                self.runchanges[_run_id] += 1
+                if _run_id != self.current_run_id and \
+                        self.runchanges[_run_id] > self.max_events:
+                    self.print("Removing run {} from the annotation list".
+                               format(_run_id))
+                    del self.runchanges[_run_id]
+
+            self.n_events += 1
+
             hits = np.zeros(self.det.n_doms)
             for dom_id in event_hits.dom_id:
                 du, floor, _ = self.det.doms[dom_id]
@@ -131,6 +148,30 @@ class DOMHits(Module):
         cb = fig.colorbar(im, pad=0.05)
         cb.set_label("number of hits")
 
+        for run, n_events_since_runchange in self.runchanges.items():
+            if n_events_since_runchange >= self.max_events:
+                continue
+            self.print("Annotating run {} ({} events passed)".format(
+                run, n_events_since_runchange))
+            x_pos = min(self.n_events,
+                        self.max_events) - n_events_since_runchange
+            plt.text(
+                x_pos,
+                self.det.n_doms,
+                "\nRUN %s  " % run,
+                rotation=60,
+                verticalalignment='top',
+                fontsize=12,
+                color='black',
+                zorder=10)
+            ax.axvline(
+                x_pos,
+                linewidth=3,
+                color='#ff0f5b',
+                linestyle='--',
+                alpha=0.8,
+                zorder=10)
+
         fig.tight_layout()
 
         f = os.path.join(self.plots_path, filename + '.png')
@@ -163,7 +204,7 @@ def main():
         timeout=60 * 60 * 24 * 7,
         max_queue=2000)
     pipe.attach(kp.io.daq.DAQProcessor)
-    pipe.attach(DOMHits, det_id=det_id, plots_path=plots_path)
+    pipe.attach(TriggerMap, det_id=det_id, plots_path=plots_path)
     pipe.drain()
 
 
diff --git a/scripts/msg_dumper.py b/scripts/msg_dumper.py
index 66e9d2d0c8d6c1e6f5b9c62d63fc21b4e1f1e61a..036647cb9e6b7135bb0822fff73b92343ebd6716 100755
--- a/scripts/msg_dumper.py
+++ b/scripts/msg_dumper.py
@@ -42,6 +42,7 @@ class MSGDumper(Module):
         entry = "{} [{}]: {}\n".format(
             os.path.basename(self.filename), source, data)
         self.fobj.write(entry)
+        self.fobj.flush()
         return blob
 
     def finish(self):
diff --git a/scripts/pmt_rates.py b/scripts/pmt_rates.py
index e726325c44ecc1dad0a59d0f65d103868ab516f3..df75d52dc5926c5252f9d6e5752cebd72f5fb829 100755
--- a/scripts/pmt_rates.py
+++ b/scripts/pmt_rates.py
@@ -96,7 +96,7 @@ class PMTRates(kp.Module):
 
     def update_plot(self):
         filename = os.path.join(self.plot_path, self.filename)
-        print("Updating plot at {}".format(filename))
+        self.log.debug("Updating plot at {}".format(filename))
         now = time.time()
         max_x = self.max_x
         interval = self.interval
diff --git a/scripts/ztplot.py b/scripts/ztplot.py
index 82b8b0cd01fb2050dc029a56adb3920f601513bd..d6b26b15a6cad2f5f9924e19dcedeea7b80f0ff2 100755
--- a/scripts/ztplot.py
+++ b/scripts/ztplot.py
@@ -59,6 +59,8 @@ lock = threading.Lock()
 class ZTPlot(Module):
     def configure(self):
         self.plots_path = self.require('plots_path')
+        self.ytick_distance = self.get('ytick_distance', default=200)
+        self.min_dus = self.get('min_dus', default=1)
         det_id = self.require('det_id')
         self.calib = kp.calib.Calibration(det_id=det_id)
 
@@ -77,7 +79,7 @@ class ZTPlot(Module):
         event_info = blob['EventInfo']
 
         n_triggered_dus = np.unique(hits[hits.triggered == True].du)
-        if n_triggered_dus < 1:
+        if n_triggered_dus < self.min_dus:
             print("Skipping...")
             return blob
 
@@ -125,11 +127,12 @@ class ZTPlot(Module):
                 c='#FF6363',
                 label='triggered hit')
             ax.set_title(
-                'DU{0}'.format(du), fontsize=fontsize, fontweight='bold')
+                'DU{0}'.format(int(du)), fontsize=fontsize, fontweight='bold')
 
         for idx, ax in enumerate(axes):
             ax.tick_params(labelsize=fontsize)
-            ax.yaxis.set_major_locator(ticker.MultipleLocator(200))
+            ax.yaxis.set_major_locator(
+                ticker.MultipleLocator(self.ytick_distance))
             xlabels = ax.get_xticklabels()
             for label in xlabels:
                 label.set_rotation(45)