diff --git a/docs/userguide b/docs/userguide
index f80f19f9..e07e544d 100644
--- a/docs/userguide
+++ b/docs/userguide
@@ -2115,17 +2115,21 @@ for this purpose: It lets you input a command and sends the command to i3. It
 can also prefix this command and display a custom prompt for the input dialog.
 
 The additional +--toggle+ option will remove the mark if the window already has
-this mark, add it if the window has none or replace the current mark if it has
-another mark.
+this mark or add it otherwise. Note that you may need to use this in
+combination with +--add+ (see below) as any other marks will otherwise be
+removed.
+
+By default, a window can only have one mark. You can use the +--add+ flag to
+put more than one mark on a window.
 
 Refer to <<show_marks>> if you don't want marks to be shown in the window decoration.
 
 *Syntax*:
-------------------------------
-mark [--toggle] <identifier>
+----------------------------------------------
+mark [--add|--replace] [--toggle] <identifier>
 [con_mark="identifier"] focus
 unmark <identifier>
-------------------------------
+----------------------------------------------
 
 *Example (in a terminal)*:
 ------------------------------
diff --git a/include/commands.h b/include/commands.h
index e0bb2f92..d3485f15 100644
--- a/include/commands.h
+++ b/include/commands.h
@@ -115,10 +115,10 @@ void cmd_workspace_back_and_forth(I3_CMD);
 void cmd_workspace_name(I3_CMD, const char *name);
 
 /**
- * Implementation of 'mark [--toggle] <mark>'
+ * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, const char *mark, const char *toggle);
+void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle);
 
 /**
  * Implementation of 'unmark [mark]'
diff --git a/include/con.h b/include/con.h
index df94c1ae..b448b8c2 100644
--- a/include/con.h
+++ b/include/con.h
@@ -158,13 +158,13 @@ bool con_has_mark(Con *con, const char *mark);
  * Otherwise, the mark is assigned to the container.
  *
  */
-void con_mark_toggle(Con *con, const char *mark);
+void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode);
 
 /**
  * Assigns a mark to the container.
  *
  */
-void con_mark(Con *con, const char *mark);
+void con_mark(Con *con, const char *mark, mark_mode_t mode);
 
 /**
  * If mark is NULL, this removes all existing marks.
diff --git a/include/data.h b/include/data.h
index ee7f82c0..3a752c2e 100644
--- a/include/data.h
+++ b/include/data.h
@@ -75,6 +75,9 @@ typedef enum { ADJ_NONE = 0,
                ADJ_UPPER_SCREEN_EDGE = (1 << 2),
                ADJ_LOWER_SCREEN_EDGE = (1 << 4) } adjacent_t;
 
+typedef enum { MM_REPLACE,
+               MM_ADD } mark_mode_t;
+
 /**
  * Container layouts. See Con::layout.
  */
diff --git a/parser-specs/commands.spec b/parser-specs/commands.spec
index b3b5e338..475dc4bd 100644
--- a/parser-specs/commands.spec
+++ b/parser-specs/commands.spec
@@ -199,12 +199,14 @@ state FLOATING:
   floating = 'enable', 'disable', 'toggle'
       -> call cmd_floating($floating)
 
-# mark [--toggle] <mark>
+# mark [--add|--replace] [--toggle] <mark>
 state MARK:
+  mode = '--add', '--replace'
+      ->
   toggle = '--toggle'
       ->
   mark = string
-      -> call cmd_mark($mark, $toggle)
+      -> call cmd_mark($mark, $mode, $toggle)
 
 # unmark [mark]
 state UNMARK:
diff --git a/src/commands.c b/src/commands.c
index ab9a4135..a9b98c53 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -1003,10 +1003,10 @@ void cmd_workspace_name(I3_CMD, const char *name) {
 }
 
 /*
- * Implementation of 'mark [--toggle] <mark>'
+ * Implementation of 'mark [--add|--replace] [--toggle] <mark>'
  *
  */
-void cmd_mark(I3_CMD, const char *mark, const char *toggle) {
+void cmd_mark(I3_CMD, const char *mark, const char *mode, const char *toggle) {
     HANDLE_EMPTY_MATCH;
 
     owindow *current = TAILQ_FIRST(&owindows);
@@ -1022,10 +1022,12 @@ void cmd_mark(I3_CMD, const char *mark, const char *toggle) {
     }
 
     DLOG("matching: %p / %s\n", current->con, current->con->name);
+
+    mark_mode_t mark_mode = (mode == NULL || strcmp(mode, "--replace") == 0) ? MM_REPLACE : MM_ADD;
     if (toggle != NULL) {
-        con_mark_toggle(current->con, mark);
+        con_mark_toggle(current->con, mark, mark_mode);
     } else {
-        con_mark(current->con, mark);
+        con_mark(current->con, mark, mark_mode);
     }
 
     cmd_output->needs_tree_render = true;
diff --git a/src/con.c b/src/con.c
index bf7dd545..9238acad 100644
--- a/src/con.c
+++ b/src/con.c
@@ -540,14 +540,14 @@ bool con_has_mark(Con *con, const char *mark) {
  * Otherwise, the mark is assigned to the container.
  *
  */
-void con_mark_toggle(Con *con, const char *mark) {
+void con_mark_toggle(Con *con, const char *mark, mark_mode_t mode) {
     assert(con != NULL);
     DLOG("Toggling mark \"%s\" on con = %p.\n", mark, con);
 
     if (con_has_mark(con, mark)) {
         con_unmark(mark);
     } else {
-        con_mark(con, mark);
+        con_mark(con, mark, mode);
     }
 }
 
@@ -555,11 +555,19 @@ void con_mark_toggle(Con *con, const char *mark) {
  * Assigns a mark to the container.
  *
  */
-void con_mark(Con *con, const char *mark) {
+void con_mark(Con *con, const char *mark, mark_mode_t mode) {
     assert(con != NULL);
     DLOG("Setting mark \"%s\" on con = %p.\n", mark, con);
 
     con_unmark(mark);
+    if (mode == MM_REPLACE) {
+        DLOG("Removing all existing marks on con = %p.\n", con);
+
+        mark_t *current;
+        TAILQ_FOREACH(current, &(con->marks_head), marks) {
+            con_unmark(current->name);
+        }
+    }
 
     mark_t *new = scalloc(1, sizeof(mark_t));
     new->name = sstrdup(mark);
diff --git a/src/load_layout.c b/src/load_layout.c
index 68c4f4a2..dc84c607 100644
--- a/src/load_layout.c
+++ b/src/load_layout.c
@@ -246,7 +246,7 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
         char *mark;
         sasprintf(&mark, "%.*s", (int)len, val);
 
-        con_mark(json_node, mark);
+        con_mark(json_node, mark, MM_ADD);
     } else {
         if (strcasecmp(last_key, "name") == 0) {
             json_node->name = scalloc(len + 1, 1);
@@ -354,7 +354,7 @@ static int json_string(void *ctx, const unsigned char *val, size_t len) {
             char *buf = NULL;
             sasprintf(&buf, "%.*s", (int)len, val);
 
-            con_mark(json_node, buf);
+            con_mark(json_node, buf, MM_REPLACE);
         } else if (strcasecmp(last_key, "floating") == 0) {
             char *buf = NULL;
             sasprintf(&buf, "%.*s", (int)len, val);
diff --git a/testcases/t/210-mark-unmark.t b/testcases/t/210-mark-unmark.t
index 39fc0904..446d5465 100644
--- a/testcases/t/210-mark-unmark.t
+++ b/testcases/t/210-mark-unmark.t
@@ -118,7 +118,17 @@ cmd 'mark --toggle important';
 is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container now has the mark');
 
 ##############################################################
-# 7: mark a con, toggle the mark on another con,
+# 7: mark a con, toggle a different mark, check it is marked
+#    with the new mark
+##############################################################
+
+$con = open_window;
+cmd 'mark boring';
+cmd 'mark --replace --toggle important';
+is_deeply(get_mark_for_window_on_workspace($tmp, $con), [ 'important' ], 'container has the most recent mark');
+
+##############################################################
+# 8: mark a con, toggle the mark on another con,
 #    check only the latter has the mark
 ##############################################################
 
@@ -133,7 +143,7 @@ is_deeply(get_mark_for_window_on_workspace($tmp, $first), [ 'important' ], 'left
 ok(!get_mark_for_window_on_workspace($tmp, $second), 'second containr no longer has the mark');
 
 ##############################################################
-# 8: try to mark two cons with the same mark and check that
+# 9: try to mark two cons with the same mark and check that
 #    it fails
 ##############################################################
 
diff --git a/testcases/t/255-multiple-marks.t b/testcases/t/255-multiple-marks.t
index d6d86e23..925e39db 100644
--- a/testcases/t/255-multiple-marks.t
+++ b/testcases/t/255-multiple-marks.t
@@ -38,8 +38,8 @@ sub get_mark_for_window_on_workspace {
 
 $ws = fresh_workspace;
 $con = open_window;
-cmd 'mark A';
-cmd 'mark B';
+cmd 'mark --add A';
+cmd 'mark --add B';
 
 is_deeply(sort(get_marks()), [ 'A', 'B' ], 'both marks exist');
 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
@@ -54,9 +54,9 @@ $ws = fresh_workspace;
 $con = open_window;
 cmd 'mark A';
 
-cmd 'mark --toggle B';
+cmd 'mark --add --toggle B';
 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B' ], 'both marks are on the same window');
-cmd 'mark --toggle B';
+cmd 'mark --add --toggle B';
 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A' ], 'only mark B has been removed');
 
 cmd 'unmark';
@@ -67,9 +67,9 @@ cmd 'unmark';
 
 $ws = fresh_workspace;
 $con = open_window;
-cmd 'mark A';
-cmd 'mark B';
-cmd 'mark C';
+cmd 'mark --add A';
+cmd 'mark --add B';
+cmd 'mark --add C';
 
 cmd 'unmark B';
 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'C' ], 'only mark B has been removed');
@@ -82,11 +82,11 @@ cmd 'unmark';
 
 $ws = fresh_workspace;
 $con = open_window;
-cmd 'mark A';
-cmd 'mark B';
+cmd 'mark --add A';
+cmd 'mark --add B';
 open_window;
 
-cmd '[con_mark=B] mark C';
+cmd '[con_mark=B] mark --add C';
 is_deeply(get_mark_for_window_on_workspace($ws, $con), [ 'A', 'B', 'C' ], 'matching on a mark works with multiple marks');
 
 cmd 'unmark';