diff --git a/core/admin.m b/core/admin.m
index c17bda4919d90225f7bb89924b8aa62f10db826e..8a09846feeb1e60800a23e872f747ffe58858563 100644
--- a/core/admin.m
+++ b/core/admin.m
@@ -1469,6 +1469,8 @@ admin_handler(void *data __attribute__((unused)))
 {
 	lua_State *L = lua_newthread(tarantool_L);
 	int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX);
+	/** Allow to interrupt/kill administrative connections. */
+	fiber_setcancelstate(true);
 	@try {
 		for (;;) {
 			if (admin_dispatch(L) <= 0)
diff --git a/core/admin.rl b/core/admin.rl
index 9c315f1686480c0d7d62418372ded9caaa2c2415..6a156cb80d7ca7eaac1daab3a9eb9bea35aea8f4 100644
--- a/core/admin.rl
+++ b/core/admin.rl
@@ -218,6 +218,8 @@ admin_handler(void *data __attribute__((unused)))
 {
 	lua_State *L = lua_newthread(tarantool_L);
 	int coro_ref = luaL_ref(tarantool_L, LUA_REGISTRYINDEX);
+	/** Allow to interrupt/kill administrative connections. */
+	fiber_setcancelstate(true);
 	@try {
 		for (;;) {
 			if (admin_dispatch(L) <= 0)
diff --git a/core/fiber.m b/core/fiber.m
index 6c32f632723dd41bb2e827aad889567b1528fd1a..04ca385f0d3eb9d7a3ec487ede5c9e07ebe5f00a 100644
--- a/core/fiber.m
+++ b/core/fiber.m
@@ -158,6 +158,11 @@ fiber_cancel(struct fiber *f)
 
 	f->flags |= FIBER_CANCEL;
 
+	if (f == fiber) {
+		fiber_testcancel();
+		return;
+	}
+
 	if (f->flags & FIBER_CANCELLABLE)
 		fiber_wakeup(f);
 
diff --git a/core/tarantool_lua.m b/core/tarantool_lua.m
index ac998e3807e2c102a2405ca40140dd7478e1d479..c5dde48b60e1c29d5f503bc084fe4f4a93739388 100644
--- a/core/tarantool_lua.m
+++ b/core/tarantool_lua.m
@@ -40,6 +40,11 @@
 
 struct lua_State *tarantool_L;
 
+/** {{{ box Lua library: common functions
+ */
+
+const char *boxlib_name = "box";
+
 /** Pack our BER integer into luaL_Buffer */
 static void
 luaL_addvarint32(luaL_Buffer *b, u32 u32)
@@ -188,7 +193,8 @@ lbox_unpack(struct lua_State *L)
 	}
 	return i-2;
 }
-/** A descriptor for box.tbuf object methods */
+
+/** A descriptor for box methods */
 
 static const struct luaL_reg boxlib[] = {
 	{"pack", lbox_pack},
@@ -196,6 +202,134 @@ static const struct luaL_reg boxlib[] = {
 	{NULL, NULL}
 };
 
+/* }}} */
+
+/** {{{ box.fiber Lua library: access to Tarantool/Box fibers
+ *
+ * Each fiber can be running, suspended or dead.
+ * It can also be attached or detached.
+ * All fibers are part of the fiber registry,
+ * box.fiber. This registry can be searched either by
+ * fiber id (fid), which is numeric, or by fiber name,
+ * which is a string. If there is more than one
+ * fiber with the given name, the first fiber that
+ * matches is returned.
+ *
+ * A new fiber can also be created from Lua, with
+ * fiber.create(). The fiber
+ * is created suspended, and is thus "attached"
+ * to the creator. The creator can invoke the suspended
+ * fiber with fiber.resume(), and the invoked fiber
+ * can yield control back to the caller with fiber.yield().
+ *
+ * An active fiber, however, may choose to "detach"
+ * from the caller, and become part of the main Tarantool/Box
+ * loop. This is what all internal Tarantool/Box fibers
+ * are -- detached. To "detach" from its creator,
+ * a running fiber can use fiber.detach(). A detached
+ * fiber becomes attached to the internal "sched"
+ * fiber, and can not be attached back to its original
+ * creator.
+ *
+ * Once fiber chunk is done or calls "return",
+ * the fiber is considered dead. Its carcass is put into
+ * fiber pool, and can be reused when another fiber is
+ * created.
+ *
+ * A runaway fiber can be stopped with fiber.cancel().
+ * fiber.cancel(), however, is advisory -- it works
+ * only if the runaway fiber is calling fiber.testcancel()
+ * once in a while. Most box.* hooks, such as box.delete()
+ * or box.update(), are calling fiber.testcancel().
+ * Thus a runaway fiber can really only become cuckoo
+ * if it does a lot of computations and doesn't check
+ * whether it's been cancelled (just don't do that).
+ */
+
+static const char *fiberlib_name = "box.fiber";
+
+struct fiber *
+lbox_checkfiber(struct lua_State *L, int index)
+{
+	return *(void **) luaL_checkudata(L, index, fiberlib_name);
+}
+
+int
+lbox_fiber_id(struct lua_State *L)
+{
+	struct fiber *f = lbox_checkfiber(L, 1);
+	lua_pushinteger(L, f->fid);
+	return 1;
+}
+
+/** Yield to the sched fiber and sleep.
+ * @param[in]  amount of time to sleep (double)
+ *
+ * Only the current fiber can be made to sleep.
+ */
+int
+lbox_fiber_sleep(struct lua_State *L)
+{
+	if (! lua_isnumber(L, 1) || lua_gettop(L) != 1)
+		luaL_error(L, "fiber.sleep(delay): bad arguments");
+	double delay = lua_tonumber(L, 1);
+	fiber_sleep(delay);
+	return 0;
+}
+
+int
+lbox_fiber_self(struct lua_State *L)
+{
+	void **ptr = lua_newuserdata(L, sizeof(void *));
+	luaL_getmetatable(L, fiberlib_name);
+	lua_setmetatable(L, -2);
+	*ptr = fiber;
+	return 1;
+}
+
+/** Running and suspended fibers can be cancelled.
+ * Zombie fibers can't.
+ */
+int
+lbox_fiber_cancel(struct lua_State *L)
+{
+	struct fiber *f = lbox_checkfiber(L, 1);
+	if (! (f->flags & FIBER_CANCELLABLE))
+		luaL_error(L, "fiber.cancel(): subject fiber does "
+			   "not permit cancel");
+	fiber_cancel(f);
+	return 0;
+}
+
+/**
+ * Check if this current fiber has been cancelled and
+ * throw an exception if this is the case.
+ */
+
+int
+lbox_fiber_testcancel(struct lua_State *L)
+{
+	if (lua_gettop(L) != 0)
+		luaL_error(L, "fiber.testcancel(): bad arguments");
+	fiber_testcancel();
+	return 0;
+}
+
+static const struct luaL_reg lbox_fiber_meta [] = {
+	{"id", lbox_fiber_id},
+	{NULL, NULL}
+};
+
+static const struct luaL_reg fiberlib[] = {
+	{"sleep", lbox_fiber_sleep},
+	{"self", lbox_fiber_self},
+	{"cancel", lbox_fiber_cancel},
+	{"testcancel", lbox_fiber_testcancel},
+	{NULL, NULL}
+};
+
+/* }}} */
+
 /*
  * lua_tostring does no use __tostring metamethod, and it has
  * to be called if we want to print Lua userdata correctly.
@@ -273,6 +407,25 @@ lbox_print(struct lua_State *L)
 	return 0;
 }
 
+/** A helper to register a single type metatable. */
+void
+tarantool_lua_register_type(struct lua_State *L, const char *type_name,
+			    const struct luaL_Reg *methods)
+{
+	luaL_newmetatable(L, type_name);
+	/*
+	 * Conventionally, make the metatable point to itself
+	 * in __index. If 'methods' contain a field for __index,
+	 * this is a no-op.
+	 */
+	lua_pushvalue(L, -1);
+	lua_setfield(L, -2, "__index");
+	lua_pushstring(L, type_name);
+	lua_setfield(L, -2, "__metatable");
+	luaL_register(L, NULL, methods);
+	lua_pop(L, 1);
+}
+
 struct lua_State *
 tarantool_lua_init()
 {
@@ -280,8 +433,11 @@ tarantool_lua_init()
 	if (L == NULL)
 		return L;
 	luaL_openlibs(L);
-	luaL_register(L, "box", boxlib);
+	luaL_register(L, boxlib_name, boxlib);
+	lua_pop(L, 1);
+	luaL_register(L, fiberlib_name, fiberlib);
 	lua_pop(L, 1);
+	tarantool_lua_register_type(L, fiberlib_name, lbox_fiber_meta);
 	lua_register(L, "print", lbox_print);
 	L = mod_lua_init(L);
 	lua_settop(L, 0); /* clear possible left-overs of init */
@@ -415,3 +571,7 @@ void tarantool_lua_load_cfg(struct lua_State *L, struct tarantool_cfg *cfg)
 		lua_pcall(L, 0, 0, 0);
 	lua_pop(L, 1);
 }
+
+/*
+ * vim: foldmethod=marker
+ */
diff --git a/include/tarantool.h b/include/tarantool.h
index c92abfe7d068df44958402c1f29eaf163ab1dda3..b9c7abea3d1e65dd8cb1f181299354458e37e510 100644
--- a/include/tarantool.h
+++ b/include/tarantool.h
@@ -31,6 +31,7 @@
 #include <log_io.h>
 
 struct lua_State;
+struct luaL_Reg;
 
 extern struct recovery_state *recovery_state;
 void mod_init(void);
@@ -51,6 +52,10 @@ void mod_info(struct tbuf *out);
  */
 struct lua_State *mod_lua_init(struct lua_State *L);
 
+void
+tarantool_lua_register_type(struct lua_State *L, const char *type_name,
+			    const struct luaL_Reg *methods);
+
 /**
  * Create an instance of Lua interpreter and load it with
  * Tarantool modules.  Creates a Lua state, imports global
diff --git a/mod/box/box_lua.m b/mod/box/box_lua.m
index 86cbb86613910b08ad43276fde538eab72195cd9..3819526b7299804ad1b825f61deefbfbd564c683 100644
--- a/mod/box/box_lua.m
+++ b/mod/box/box_lua.m
@@ -631,35 +631,16 @@ void box_lua_call(struct box_txn *txn __attribute__((unused)),
 	}
 }
 
-/** A helper to register a single type metatable. */
-static void
-lua_register_type(struct lua_State *L, const char *type_name,
-		  const struct luaL_reg *methods)
-{
-	luaL_newmetatable(L, type_name);
-	/*
-	 * Conventionally, make the metatable point to itself
-	 * in __index. If 'methods' contain a field for __index,
-	 * this is a no-op.
-	 */
-	lua_pushvalue(L, -1);
-	lua_setfield(L, -2, "__index");
-	lua_pushstring(L, type_name);
-	lua_setfield(L, -2, "__metatable");
-	luaL_register(L, NULL, methods);
-	lua_pop(L, 1);
-}
-
 struct lua_State *
 mod_lua_init(struct lua_State *L)
 {
 	lua_atpanic(L, box_lua_panic);
 	/* box, box.tuple */
-	lua_register_type(L, tuplelib_name, lbox_tuple_meta);
+	tarantool_lua_register_type(L, tuplelib_name, lbox_tuple_meta);
 	luaL_register(L, "box", boxlib);
 	lua_pop(L, 1);
 	/* box.index */
-	lua_register_type(L, indexlib_name, lbox_index_meta);
+	tarantool_lua_register_type(L, indexlib_name, lbox_index_meta);
 	luaL_register(L, "box.index", indexlib);
 	lua_pop(L, 1);
 	/* Load box.lua */
diff --git a/test/box/lua.result b/test/box/lua.result
index 2c5f7171dcbb3e1a7952101e2376bc6b73e30065..0c28179dbdaef6295cd75d07609c381d1c33e71e 100644
--- a/test/box/lua.result
+++ b/test/box/lua.result
@@ -12,6 +12,7 @@ lua print('  lua says: hello')
 ...
 lua for n in pairs(box) do print('  - box.', n) end
 ---
+  - box.fiber
   - box.space
   - box.cfg
   - box.on_reload_configuration
@@ -19,10 +20,10 @@ lua for n in pairs(box) do print('  - box.', n) end
   - box.process
   - box.delete
   - box.insert
-  - box.replace
+  - box.select
   - box.index
   - box.unpack
-  - box.select
+  - box.replace
   - box.select_range
   - box.pack
 ...
@@ -520,3 +521,35 @@ one more
 lua box.space[0]:truncate()
 ---
 ...
+lua box.fiber.sleep(0)
+---
+...
+lua box.fiber.sleep(0.01)
+---
+...
+lua box.fiber.sleep(0.0001)
+---
+...
+lua box.fiber.sleep('hello')
+---
+error: 'Lua error: fiber.sleep(delay): bad arguments'
+...
+lua box.fiber.sleep(box, 0.001)
+---
+error: 'Lua error: fiber.sleep(delay): bad arguments'
+...
+lua f = box.fiber.self()
+---
+...
+lua box.fiber.cancel(f)
+lua f:id(), box.fiber.self():id()
+---
+ - 109
+ - 109
+...
+lua box.fiber.cancel(box.fiber.self())
+lua f:id(), box.fiber.self():id()
+---
+ - 110
+ - 110
+...
diff --git a/test/box/lua.test b/test/box/lua.test
index 8fcd3fc58ff4028bec6cc2400769c30703a9c9dd..e887edfc9dbc8a6dcca15f03eb01f765453d1e81 100644
--- a/test/box/lua.test
+++ b/test/box/lua.test
@@ -142,3 +142,13 @@ exec admin "lua for k, v in t:pairs() do print(v) end"
 exec admin "lua t=box.space[0]:replace('test', 'another field', 'one more')"
 exec admin "lua for k, v in t:pairs() do print(v) end"
 exec admin "lua box.space[0]:truncate()"
+exec admin "lua box.fiber.sleep(0)"
+exec admin "lua box.fiber.sleep(0.01)"
+exec admin "lua box.fiber.sleep(0.0001)"
+exec admin "lua box.fiber.sleep('hello')"
+exec admin "lua box.fiber.sleep(box, 0.001)"
+exec admin "lua f = box.fiber.self()"
+exec admin "lua box.fiber.cancel(f)"
+exec admin "lua f:id(), box.fiber.self():id()"
+exec admin "lua box.fiber.cancel(box.fiber.self())"
+exec admin "lua f:id(), box.fiber.self():id()"