diff --git a/docs/ipc b/docs/ipc
index fda289a0..5d47bcbd 100644
--- a/docs/ipc
+++ b/docs/ipc
@@ -671,6 +671,8 @@ barconfig_update (4)::
 binding (5)::
 	Sent when a configured command binding is triggered with the keyboard or
 	mouse
+shutdown (6)::
+	Sent when the ipc shuts down because of a restart or exit by user command
 
 *Example:*
 --------------------------------------------------------------------
@@ -829,6 +831,20 @@ input_type (string)::
 }
 ---------------------------
 
+=== shutdown event
+
+This event is triggered when the connection to the ipc is about to shutdown
+because of a user action such as a +restart+ or +exit+ command. The +change
+(string)+ field indicates why the ipc is shutting down. It can be either
++"restart"+ or +"exit"+.
+
+*Example:*
+---------------------------
+{
+ "change": "restart"
+}
+---------------------------
+
 == See also (existing libraries)
 
 [[libraries]]
diff --git a/include/i3/ipc.h b/include/i3/ipc.h
index 98ac35b0..249cc32e 100644
--- a/include/i3/ipc.h
+++ b/include/i3/ipc.h
@@ -91,3 +91,6 @@ typedef struct i3_ipc_header {
 
 /** The binding event will be triggered when bindings run */
 #define I3_IPC_EVENT_BINDING (I3_IPC_EVENT_MASK | 5)
+
+/** The shutdown event will be triggered when the ipc shuts down */
+#define I3_IPC_EVENT_SHUTDOWN (I3_IPC_EVENT_MASK | 6)
diff --git a/include/ipc.h b/include/ipc.h
index 7ff4704c..7ffbf7a8 100644
--- a/include/ipc.h
+++ b/include/ipc.h
@@ -77,11 +77,18 @@ int ipc_create_socket(const char *filename);
 void ipc_send_event(const char *event, uint32_t message_type, const char *payload);
 
 /**
- * Calls shutdown() on each socket and closes it. This function to be called
- * when exiting or restarting only!
+ * Calls to ipc_shutdown() should provide a reason for the shutdown.
+ */
+typedef enum {
+    SHUTDOWN_REASON_RESTART,
+    SHUTDOWN_REASON_EXIT
+} shutdown_reason_t;
+
+/**
+ * Calls shutdown() on each socket and closes it.
  *
  */
-void ipc_shutdown(void);
+void ipc_shutdown(shutdown_reason_t reason);
 
 void dump_node(yajl_gen gen, Con *con, bool inplace_restart);
 
diff --git a/src/commands.c b/src/commands.c
index 2387ddd7..56c07b6a 100644
--- a/src/commands.c
+++ b/src/commands.c
@@ -1562,7 +1562,7 @@ void cmd_exit(I3_CMD) {
 #ifdef I3_ASAN_ENABLED
     __lsan_do_leak_check();
 #endif
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_EXIT);
     unlink(config.ipc_socket_path);
     xcb_disconnect(conn);
     exit(0);
@@ -1595,7 +1595,7 @@ void cmd_reload(I3_CMD) {
  */
 void cmd_restart(I3_CMD) {
     LOG("restarting i3\n");
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_RESTART);
     unlink(config.ipc_socket_path);
     /* We need to call this manually since atexit handlers don’t get called
      * when exec()ing */
diff --git a/src/ipc.c b/src/ipc.c
index db2fa362..bb20b340 100644
--- a/src/ipc.c
+++ b/src/ipc.c
@@ -62,11 +62,39 @@ void ipc_send_event(const char *event, uint32_t message_type, const char *payloa
 }
 
 /*
- * Calls shutdown() on each socket and closes it. This function to be called
+ * For shutdown events, we send the reason for the shutdown.
+ */
+static void ipc_send_shutdown_event(shutdown_reason_t reason) {
+    yajl_gen gen = ygenalloc();
+    y(map_open);
+
+    ystr("change");
+
+    if (reason == SHUTDOWN_REASON_RESTART) {
+        ystr("restart");
+    } else if (reason == SHUTDOWN_REASON_EXIT) {
+        ystr("exit");
+    }
+
+    y(map_close);
+
+    const unsigned char *payload;
+    ylength length;
+
+    y(get_buf, &payload, &length);
+    ipc_send_event("shutdown", I3_IPC_EVENT_SHUTDOWN, (const char *)payload);
+
+    y(free);
+}
+
+/*
+ * Calls shutdown() on each socket and closes it. This function is to be called
  * when exiting or restarting only!
  *
  */
-void ipc_shutdown(void) {
+void ipc_shutdown(shutdown_reason_t reason) {
+    ipc_send_shutdown_event(reason);
+
     ipc_client *current;
     while (!TAILQ_EMPTY(&all_clients)) {
         current = TAILQ_FIRST(&all_clients);
diff --git a/src/util.c b/src/util.c
index cfe4c953..5c8fc774 100644
--- a/src/util.c
+++ b/src/util.c
@@ -259,7 +259,7 @@ void i3_restart(bool forget_layout) {
 
     restore_geometry();
 
-    ipc_shutdown();
+    ipc_shutdown(SHUTDOWN_REASON_RESTART);
 
     LOG("restarting \"%s\"...\n", start_argv[0]);
     /* make sure -a is in the argument list or add it */
diff --git a/testcases/t/264-ipc-shutdown-event.t b/testcases/t/264-ipc-shutdown-event.t
new file mode 100644
index 00000000..379b9bf2
--- /dev/null
+++ b/testcases/t/264-ipc-shutdown-event.t
@@ -0,0 +1,71 @@
+#!perl
+# vim:ts=4:sw=4:expandtab
+#
+# Please read the following documents before working on tests:
+# • http://build.i3wm.org/docs/testsuite.html
+#   (or docs/testsuite)
+#
+# • http://build.i3wm.org/docs/lib-i3test.html
+#   (alternatively: perldoc ./testcases/lib/i3test.pm)
+#
+# • http://build.i3wm.org/docs/ipc.html
+#   (or docs/ipc)
+#
+# • http://onyxneon.com/books/modern_perl/modern_perl_a4.pdf
+#   (unless you are already familiar with Perl)
+#
+# Test the ipc shutdown event. This event is triggered when the connection to
+# the ipc is about to shutdown because of a user action such as with a
+# `restart` or `exit` command. The `change` field indicates why the ipc is
+# shutting down. It can be either "restart" or "exit".
+#
+# Ticket: #2318
+# Bug still in: 4.12-46-g2123888
+use i3test;
+
+SKIP: {
+    skip "AnyEvent::I3 too old (need >= 0.17)", 1 if $AnyEvent::I3::VERSION < 0.17;
+
+my $i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+my $cv = AE::cv;
+my $timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'restart';
+
+my $e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is restarted by command');
+is($e->{change}, 'restart', 'the `change` field should tell the reason for the shutdown');
+
+# restarting kills the ipc client so we have to make a new one
+$i3 = i3(get_socket_path());
+$i3->connect->recv;
+
+$cv = AE::cv;
+$timer = AE::timer 0.5, 0, sub { $cv->send(0); };
+
+$i3->subscribe({
+        shutdown => sub {
+            $cv->send(shift);
+        }
+    })->recv;
+
+cmd 'exit';
+
+$e = $cv->recv;
+
+diag "Event:\n", Dumper($e);
+ok($e, 'the shutdown event should emit when the ipc is exited by command');
+is($e->{change}, 'exit', 'the `change` field should tell the reason for the shutdown');
+}
+
+done_testing;