diff --git a/emper/io/Future.cpp b/emper/io/Future.cpp
index 13e3be6179288c3ca174e67125e3f435d83d344d..4e915bb041a77b0d4c507ca20c39cdc6cbd5180d 100644
--- a/emper/io/Future.cpp
+++ b/emper/io/Future.cpp
@@ -17,6 +17,8 @@
 #include "io/Operation.hpp"						 // for emper::io::Operation
 #include "io/Stats.hpp"								 // for emper::io::Stats
 
+struct __kernel_timespec;
+
 namespace emper::io {
 template <CallerEnvironment callerEnvironment>
 void Future::tryComplete(int32_t res, bool syscall) {
@@ -132,6 +134,12 @@ void Future::prepareSqe(struct io_uring_sqe *sqe) {
 		case Operation::CLOSE:
 			io_uring_prep_close(sqe, fd);
 			break;
+		case Operation::LINK_TIMEOUT:
+			io_uring_prep_link_timeout(sqe, (struct ::__kernel_timespec *)buf, 0);
+			break;
+		case Operation::TIMEOUT:
+			io_uring_prep_timeout(sqe, (struct ::__kernel_timespec *)buf, 0, 0);
+			break;
 		default:
 			abort();
 	}
diff --git a/emper/io/Future.hpp b/emper/io/Future.hpp
index 10b58570c201d0bc0dc681181e7ce67d69a521ef..419981e7b8a6b425aad52d86f3589e00bf3afc60 100644
--- a/emper/io/Future.hpp
+++ b/emper/io/Future.hpp
@@ -111,7 +111,9 @@ class Future : public Logger<LogSubsystem::IO> {
 	 * set errno to ECANCELED
 	 *
 	 * See: <a href="https://unixism.net/loti/tutorial/link_liburing.html">Liburing linking
-	 * requests</a> The emper equivalent to the example from the liburing documentation.
+	 * requests</a>
+	 * The emper equivalent to the example from the liburing documentation.
+	 *
 	 * @code
 	 * int fd = open(FILE_NAME, O_RDWR|O_TRUNC|O_CREAT, 0644);
 	 * if (fd < 0 ) {
@@ -221,4 +223,39 @@ class CloseFuture : public Future {
  public:
 	CloseFuture(int fildes) : Future(Operation::CLOSE, fildes, nullptr, 0, 0){};
 };
+
+/*
+ * @brief Add a timeout to any future.
+ *
+ * A Timeout is added to a Future by inserting an dependent IOURING_OP_LINK_TIMEOUT
+ * sqe after the actual IO operation.
+ * The dependent timeout will not be started like usual dependent requests after
+ * the previous request is completed, it is armed instead when the actual request is started.
+ * See: https://lwn.net/Articles/803932/
+ *
+ * @code
+ * ReadFuture rf(fd, buf, buf_len, 0);
+ * struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 0);
+ * TimeoutWrapper t(rf, ts);
+ * // Both futures should be awaited by calling its wait method to
+ * // prevent use after free problems.
+ * t.submitAndWait()
+ * rf.Wait()
+ * @endcode
+ */
+class TimeoutWrapper : public Future {
+ public:
+	TimeoutWrapper(Future& future, struct __kernel_timespec& ts)
+			: Future(Operation::LINK_TIMEOUT, 0, (void*)&ts, 0, 0) {
+		addDependency(future);
+	};
+};
+
+/*
+ * @brief Arm a timeout which will signal the future when it is reached
+ */
+class AlarmFuture : public Future {
+ public:
+	AlarmFuture(struct __kernel_timespec& ts) : Future(Operation::TIMEOUT, 0, (void*)&ts, 0, 0){};
+};
 }	 // namespace emper::io
diff --git a/emper/io/Operation.cpp b/emper/io/Operation.cpp
index 3b4c46a0864a1194a07571d4aa3b28785d3d2e3e..66daee9c6a9a6fb973ac7933f5a9ad9f758a9082 100644
--- a/emper/io/Operation.cpp
+++ b/emper/io/Operation.cpp
@@ -29,6 +29,12 @@ auto operator<<(std::ostream& os, const Operation& op) -> std::ostream& {
 		case Operation::CLOSE:
 			os << "close";
 			break;
+		case Operation::LINK_TIMEOUT:
+			os << "linked timeout";
+			break;
+		case Operation::TIMEOUT:
+			os << "timeout";
+			break;
 		default:
 			abort();
 	}
diff --git a/emper/io/Operation.hpp b/emper/io/Operation.hpp
index 64d08b54f6342440b21727db22d148a0fb4c2ded..b9026b8dbac4dfbba9aebf2eb22b3b7e67210440 100644
--- a/emper/io/Operation.hpp
+++ b/emper/io/Operation.hpp
@@ -6,7 +6,18 @@
 
 namespace emper::io {
 
-enum class Operation { SEND = 0, RECV, CONNECT, ACCEPT, READ, WRITE, CLOSE, NUMBER_OF_OPERATIONS };
+enum class Operation {
+	SEND = 0,
+	RECV,
+	CONNECT,
+	ACCEPT,
+	READ,
+	WRITE,
+	CLOSE,
+	LINK_TIMEOUT,
+	TIMEOUT,
+	NUMBER_OF_OPERATIONS
+};
 
 auto operator<<(std::ostream& os, const Operation& o) -> std::ostream&;
 
diff --git a/tests/AlarmFutureTest.cpp b/tests/AlarmFutureTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..537c5a7939e8848d3e95d16ef5d70df689ec42d0
--- /dev/null
+++ b/tests/AlarmFutureTest.cpp
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: LGPL-3.0-or-later
+// Copyright © 2020 Florian Schmaus
+#include <linux/time_types.h>	 // for struct __kernel_timespec
+#include <sys/types.h>				 // ssize_t
+
+#include <cassert>	// for assert
+#include <cerrno>		// for errno
+#include <chrono>		// for high_resolution_clock
+#include <compare>	// for operator>=, strong_ordering
+#include <cstdlib>	// for exit, EXIT_FAILURE, EXIT_SUC...
+
+#include "Fiber.hpp"			 // for Fiber
+#include "Runtime.hpp"		 // for Runtime
+#include "emper-common.h"	 // for UNUSED_ARG
+#include "io/Future.hpp"	 // for Future
+
+using emper::io::AlarmFuture;
+
+void emperTest() {
+	struct __kernel_timespec ts = {.tv_sec = 1, .tv_nsec = 0};
+	AlarmFuture alarm(ts);
+
+	auto start = std::chrono::high_resolution_clock::now();
+	ssize_t res = alarm.submitAndWait();
+	auto end = std::chrono::high_resolution_clock::now();
+
+	// timeouts return -1 if they trigger
+	assert(res == -1);
+	// the timeout was triggered
+	assert(errno == ETIME);
+
+	assert(std::chrono::duration_cast<std::chrono::microseconds>(end - start) >=
+				 std::chrono::seconds(1));
+
+	exit(EXIT_SUCCESS);
+}
+
+auto main(UNUSED_ARG int arg, UNUSED_ARG char* argv[]) -> int {
+	Runtime runtime;
+
+	Fiber* fiber = Fiber::from(emperTest);
+	runtime.scheduleFromAnywhere(*fiber);
+
+	runtime.waitUntilFinished();
+
+	return EXIT_FAILURE;
+}
diff --git a/tests/meson.build b/tests/meson.build
index 057cfeb25be0ed3950aa528057e9e05192226da0..4e72e982d0f837dd0170e27baf79af5bd7481b3b 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -86,6 +86,12 @@ tests = {
 			'description': 'Test linking Future objects',
 			'test_suite': 'io',
 		  },
+		  'AlarmFutureTest.cpp':
+		  {
+			'feature_flags': ['io'],
+			'description': 'Test AlarmFuture object based timeouts',
+			'test_suite': 'io',
+		  },
 		  'SimpleDiskAndNetworkTest.cpp':
 		  {
 			'feature_flags': ['io'],