diff --git a/src/fiber.cc b/src/fiber.cc
index dfed60327ac401a22808c2cdf99522c790e30d92..7afdc17dc25eba3b7a9dacc496ea438356020bbf 100644
--- a/src/fiber.cc
+++ b/src/fiber.cc
@@ -458,6 +458,7 @@ fiber_new(const char *name, void (*f) (va_list))
 	fiber->session = NULL;
 	fiber->flags = 0;
 	fiber->waiter = NULL;
+	fiber->lua_storage = -2; /* LUA_NOREF */;
 	fiber_set_name(fiber, name);
 	register_fid(fiber);
 
diff --git a/src/fiber.h b/src/fiber.h
index 0dd014c8d76bccb107321ba233e27ae4ea2ab04f..89de53f622612bba1779e98feac5a47b9b3f72ef 100644
--- a/src/fiber.h
+++ b/src/fiber.h
@@ -111,6 +111,7 @@ struct fiber {
 	uint32_t flags;
 	struct fiber *waiter;
 	uint64_t cookie;
+	int lua_storage;
 };
 
 enum { FIBER_CALL_STACK = 16 };
diff --git a/src/lua/fiber.cc b/src/lua/fiber.cc
index ba69d6ac001d6f00d39f50e47eb824c3e9606479..d5724f2b2473acd96ab749305e8f077f5add501b 100644
--- a/src/lua/fiber.cc
+++ b/src/lua/fiber.cc
@@ -31,6 +31,7 @@
 #include <fiber.h>
 #include "lua/utils.h"
 #include <session.h>
+#include <scoped_guard.h>
 
 extern "C" {
 #include <lua.h>
@@ -261,6 +262,12 @@ box_lua_fiber_run_detached(va_list ap)
 	LuarefGuard coro_guard(va_arg(ap, int));
 	struct lua_State *L = va_arg(ap, struct lua_State *);
         SessionGuard session_guard(-1, 0);
+	auto storage_guard = make_scoped_guard([=] {
+		/* Destroy local storage */
+		if (fiber()->lua_storage != LUA_NOREF)
+			lua_unref(L, fiber()->lua_storage);
+		fiber()->lua_storage = LUA_NOREF;
+	});
 
 	try {
 		lbox_call(L, lua_gettop(L) - 1, LUA_MULTRET);
@@ -357,6 +364,33 @@ lbox_fiber_name(struct lua_State *L)
 	}
 }
 
+static int
+lbox_fiber_storage(struct lua_State *L)
+{
+	struct fiber *f = lbox_checkfiber(L, 1);
+	if (f->lua_storage == LUA_NOREF) {
+		lua_newtable(L); /* create local storage on demand */
+		f->lua_storage = luaL_ref(L, LUA_REGISTRYINDEX);
+	}
+	lua_rawgeti(L, LUA_REGISTRYINDEX, f->lua_storage);
+	return 1;
+}
+
+static int
+lbox_fiber_index(struct lua_State *L)
+{
+	if (lua_gettop(L) < 2)
+		return 0;
+	if (lua_isstring(L, 2) && strcmp(lua_tostring(L, 2), "storage") == 0)
+		return lbox_fiber_storage(L);
+
+	/* Get value from metatable */
+	lua_getmetatable(L, 1);
+	lua_pushvalue(L, 2);
+	lua_gettable(L, -2);
+	return 1;
+}
+
 /**
  * Yield to the sched fiber and sleep.
  * @param[in]  amount of time to sleep (double)
@@ -456,6 +490,7 @@ static const struct luaL_reg lbox_fiber_meta [] = {
 	{"cancel", lbox_fiber_cancel},
 	{"status", lbox_fiber_status},
 	{"testcancel", lbox_fiber_testcancel},
+	{"__index", lbox_fiber_index},
 	{"__gc", lbox_fiber_gc},
 	{NULL, NULL}
 };
diff --git a/test/box/fiber.result b/test/box/fiber.result
index 7d6971c765fb9718d0b9cdd1eeacaf41163c1e66..606b86e50b3b5baed3489b3337473d7a46a58cb1 100644
--- a/test/box/fiber.result
+++ b/test/box/fiber.result
@@ -591,6 +591,123 @@ fiber.find(fib_id)
 ---
 - null
 ...
+--
+-- Test local storage
+--
+fiber.self().storage
+---
+- []
+...
+fiber.self().storage.key = 48
+---
+...
+fiber.self().storage.key
+---
+- 48
+...
+--# setopt delimiter ';'
+function testfun(ch)
+    while fiber.self().storage.key == nil do
+        print('wait')
+        fiber.sleep(0)
+    end
+    ch:put(fiber.self().storage.key)
+end;
+---
+...
+--# setopt delimiter ''
+ch = fiber.channel(1)
+---
+...
+f = fiber.create(testfun, ch)
+---
+...
+f.storage.key = 'some value'
+---
+...
+ch:get()
+---
+- some value
+...
+ch:close()
+---
+...
+ch = nil
+---
+...
+fiber.self().storage.key -- our local storage is not affected by f
+---
+- 48
+...
+-- attempt to access local storage of dead fiber raises error
+pcall(function(f) return f.storage end, f)
+---
+- false
+- '[string "return pcall(function(f) return f.storage end..."]:1: the fiber is dead'
+...
+--
+-- Test that local storage is garbage collected when fiber is died
+--
+ffi = require('ffi')
+---
+...
+ch = fiber.channel(1)
+---
+...
+--# setopt delimiter ';'
+function testfun()
+    fiber.self().storage.x = ffi.gc(ffi.new('char[1]'),
+         function() ch:put('gc ok') end)
+end;
+---
+...
+--# setopt delimiter ''
+f = fiber.create(testfun)
+---
+...
+collectgarbage('collect')
+---
+- 0
+...
+ch:get()
+---
+- gc ok
+...
+ch:close()
+---
+...
+ch = nil
+---
+...
+--
+-- Test that local storage is not garbage collected with fiber object
+--
+--# setopt delimiter ';'
+function testfun(ch)
+    fiber.self().storage.x = 'ok'
+    collectgarbage('collect')
+    ch:put(fiber.self().storage.x or 'failed')
+end;
+---
+...
+--# setopt delimiter ''
+ch = fiber.channel(1)
+---
+...
+fiber.create(testfun, ch):status()
+---
+- dead
+...
+ch:get()
+---
+- ok
+...
+ch:close()
+---
+...
+ch = nil
+---
+...
 fiber = nil
 ---
 ...
diff --git a/test/box/fiber.test.lua b/test/box/fiber.test.lua
index 14078a4226c927d307b73c89994cb428487ed938..bb5802db31b27068a5b16d20a073f8a01a8db845 100644
--- a/test/box/fiber.test.lua
+++ b/test/box/fiber.test.lua
@@ -212,4 +212,67 @@ f:cancel()
 fib_id = fiber.create(testfun):id()
 fiber.find(fib_id):cancel()
 fiber.find(fib_id)
+
+--
+-- Test local storage
+--
+
+fiber.self().storage
+fiber.self().storage.key = 48
+fiber.self().storage.key
+
+--# setopt delimiter ';'
+function testfun(ch)
+    while fiber.self().storage.key == nil do
+        print('wait')
+        fiber.sleep(0)
+    end
+    ch:put(fiber.self().storage.key)
+end;
+--# setopt delimiter ''
+ch = fiber.channel(1)
+f = fiber.create(testfun, ch)
+f.storage.key = 'some value'
+ch:get()
+ch:close()
+ch = nil
+fiber.self().storage.key -- our local storage is not affected by f
+-- attempt to access local storage of dead fiber raises error
+pcall(function(f) return f.storage end, f)
+
+--
+-- Test that local storage is garbage collected when fiber is died
+--
+ffi = require('ffi')
+ch = fiber.channel(1)
+--# setopt delimiter ';'
+function testfun()
+    fiber.self().storage.x = ffi.gc(ffi.new('char[1]'),
+         function() ch:put('gc ok') end)
+end;
+--# setopt delimiter ''
+f = fiber.create(testfun)
+collectgarbage('collect')
+ch:get()
+ch:close()
+ch = nil
+
+
+
+--
+-- Test that local storage is not garbage collected with fiber object
+--
+--# setopt delimiter ';'
+function testfun(ch)
+    fiber.self().storage.x = 'ok'
+    collectgarbage('collect')
+    ch:put(fiber.self().storage.x or 'failed')
+end;
+--# setopt delimiter ''
+ch = fiber.channel(1)
+fiber.create(testfun, ch):status()
+ch:get()
+ch:close()
+ch = nil
+
 fiber = nil