diff --git a/apps/EchoServer.cpp b/apps/EchoServer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d21dac67043eee4caed1480cd48c65d17b5e931
--- /dev/null
+++ b/apps/EchoServer.cpp
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright © 2020-2021 Florian Fischer
+#include <sys/types.h>
+
+#include <cassert>
+#include <climits>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <string>
+
+#include "Common.hpp"
+#include "Runtime.hpp"
+#include "emper-common.h"
+#include "io.hpp"
+
+const int DECIMAL = 10;
+const std::string HOST = "0.0.0.0";
+const int PORT = 12345;
+
+auto main(int argc, char* argv[]) -> int {
+	int port = PORT;
+	std::string host = HOST;
+
+	if (argc > 2) {
+		std::cerr << "Usage: " << argv[0] << " [port]" << std::endl;
+		exit(EXIT_FAILURE);
+	}
+
+	if (argc > 1) {
+		long aport = strtol(argv[1], nullptr, DECIMAL);
+		assert(aport <= INT_MAX && aport >= INT_MIN);
+		port = (int)aport;
+	}
+
+	std::cout << "Echoserver listening on " << host << ":" << port << std::endl;
+
+	Runtime runtime;
+	auto* listener = emper::io::tcp_listener(host, port, [](int socket) {
+		// NOLINTNEXTLINE(modernize-avoid-c-arrays)
+		char buf[1024];
+		for (;;) {
+			ssize_t bytes_recv = emper::io::recvAndWait(socket, buf, sizeof(buf), 0);
+			if (unlikely(bytes_recv <= 0)) {
+				// socket was shutdown
+				if (bytes_recv < 0) {
+					DIE_MSG_ERRNO("server read failed");
+				}
+
+				emper::io::closeAndForget(socket);
+				return;
+			}
+
+			if (unlikely(bytes_recv == 5 && strncmp("quit\n", buf, bytes_recv) == 0)) {
+				exit(EXIT_SUCCESS);
+			}
+
+			ATTR_UNUSED ssize_t bytes_send = emper::io::sendAndWait(socket, buf, bytes_recv, 0);
+			assert(bytes_recv == bytes_send);
+		}
+	});
+
+	if (!listener) {
+		exit(EXIT_FAILURE);
+	}
+
+	runtime.scheduleFromAnywhere(*listener);
+
+	runtime.waitUntilFinished();
+
+	return EXIT_FAILURE;
+}
diff --git a/apps/meson.build b/apps/meson.build
index 7ed455b24328a4572a91e1e17c71e68f425191f8..82a23a5668c1c5c83db02f1314ad3d1b51b07f8a 100644
--- a/apps/meson.build
+++ b/apps/meson.build
@@ -9,3 +9,9 @@ worker_sleep_example_exe = executable(
   'WorkerSleepExample.cpp',
   dependencies: emper_dep,
 )
+
+echoserver_exe = executable(
+  'echoserver',
+  'EchoServer.cpp',
+  dependencies: emper_dep,
+)