diff --git a/src/lua/init.lua b/src/lua/init.lua
index 9fd56f483fac969e5609d2578b3eddd19256066c..ff3e74c3c366dea1d355c4ea4576aca8d496f2ad 100644
--- a/src/lua/init.lua
+++ b/src/lua/init.lua
@@ -44,6 +44,33 @@ tarantool_exit(int);
 
 local fio = require("fio")
 
+local soext = (jit.os == "OSX" and "dylib" or "so")
+
+local ROCKS_LIB_PATH = '.rocks/lib/tarantool'
+local ROCKS_LUA_PATH = '.rocks/share/tarantool'
+local LIB_TEMPLATES = { '?.'..soext }
+local LUA_TEMPLATES = { '?.lua', '?/init.lua' }
+local ROCKS_LIB_TEMPLATES = { ROCKS_LIB_PATH .. '/?.'..soext }
+local ROCKS_LUA_TEMPLATES = { ROCKS_LUA_PATH .. '/?.lua', ROCKS_LUA_PATH .. '/?/init.lua' }
+
+local package_searchroot
+
+local function searchroot()
+    return package_searchroot or fio.cwd()
+end
+
+local function setsearchroot(path)
+    if not path then
+        -- Here we need to get this function caller's sourcedir.
+        path = debug.sourcedir(3)
+    elseif path == box.NULL then
+        path = nil
+    else
+        assert(type(path) == 'string', 'Search root must be a string')
+    end
+    package_searchroot = path and fio.abspath(path)
+end
+
 dostring = function(s, ...)
     local chunk, message = loadstring(s)
     if chunk == nil then
@@ -70,8 +97,6 @@ local function pid()
     return tonumber(ffi.C.getpid())
 end
 
-local soext = (jit.os == "OSX" and "dylib" or "so")
-
 local function mksymname(name)
     local mark = string.find(name, "-")
     if mark then name = string.sub(name, mark + 1) end
@@ -86,81 +111,72 @@ local function load_lua(file)
     return loadfile(file)
 end
 
-local function search_cwd_lib(name)
-    local path = "./?."..soext
-    return package.searchpath(name, path)
-end
+local function traverse_path(path)
+    path = fio.abspath(path)
+    local paths = { path }
 
-local function search_cwd_lua(name)
-    local path = "./?.lua;./?/init.lua"
-    return package.searchpath(name, path)
-end
-
-local function traverse_rocks(name, pathes_search)
-    local cwd = fio.cwd()
-    local index = string.len(cwd) + 1
-    local strerr = ""
-    while index ~= nil do
-        cwd = string.sub(cwd, 1, index - 1)
-        for i, path in ipairs(pathes_search) do
-            local file, err = package.searchpath(name, cwd .. path)
-            if err == nil then
-                return file
-            end
-            strerr = strerr .. err
-        end
-        index = string.find(cwd, "/[^/]*$")
+    while path ~= '/' do
+        path = fio.dirname(path)
+        table.insert(paths, path)
     end
-    return nil, strerr
-end
 
-local function search_rocks_lua(name)
-    local pathes_search = {
-        "/.rocks/share/tarantool/?.lua;",
-        "/.rocks/share/tarantool/?/init.lua;",
-    }
-    return traverse_rocks(name, pathes_search)
+    return paths
 end
 
-local function search_rocks_lib(name)
-    local pathes_search = {
-        "/.rocks/lib/tarantool/?."..soext
-    }
-    return traverse_rocks(name, pathes_search)
-end
+-- Generate a search function, which performs searching through
+-- templates setup in options.
+--
+-- @param path_fn function which returns a base path for the
+--     resulting template
+-- @param templates table with lua search templates
+-- @param need_traverse bool flag which tells search function to
+--     build multiple paths by expanding base path up to the
+--     root ('/')
+-- @return a searcher function which builds a path template and
+--     calls package.searchpath
+local function gen_search_func(path_fn, templates, need_traverse)
+    assert(type(path_fn) == 'function', 'path_fn must be a function')
+    assert(type(templates) == 'table', 'templates must be a table')
 
-local function cwd_loader_func(lib)
-    local search_cwd = lib and search_cwd_lib or search_cwd_lua
-    local load_func = lib and load_lib or load_lua
     return function(name)
-        if not name then
-            return "empty name of module"
-        end
-        local file, err = search_cwd(name)
-        if not file then
-            return err
-        end
-        local loaded, err = load_func(file, name)
-        if err == nil then
-            return loaded
-        else
-            return err
+        local path = path_fn() or '.'
+        local paths = need_traverse and traverse_path(path) or { path }
+
+        local searchpaths = {}
+
+        for _, path in ipairs(paths) do
+            for _, template in pairs(templates) do
+                table.insert(searchpaths, fio.pathjoin(path, template))
+            end
         end
+
+        local searchpath = table.concat(searchpaths, ';')
+
+        return package.searchpath(name, searchpath)
     end
 end
 
-local function rocks_loader_func(lib)
-    local search_rocks = lib and search_rocks_lib or search_rocks_lua
-    local load_func = lib and load_lib or load_lua
-    return function (name)
+-- Compose a loader function from options.
+--
+-- @param search_fn function will be used to search a file from
+--     path template
+-- @param load_fn function will be used to load a file, found by
+--     search function
+-- @return function a loader, which first search for the file and
+--     then loads it
+local function gen_loader_func(search_fn, load_fn)
+    assert(type(search_fn) == 'function', 'search_fn must be defined')
+    assert(type(load_fn) == 'function', 'load_fn must be defined')
+
+    return function(name)
         if not name then
             return "empty name of module"
         end
-        local file, err = search_rocks(name)
+        local file, err = search_fn(name)
         if not file then
             return err
         end
-        local loaded, err = load_func(file, name)
+        local loaded, err = load_fn(file, name)
         if err == nil then
             return loaded
         else
@@ -169,22 +185,25 @@ local function rocks_loader_func(lib)
     end
 end
 
-local function search_path_func(cpath)
-    return function(name)
-        return package.searchpath(name, cpath and package.cpath or package.path)
-    end
-end
+local search_lua = gen_search_func(searchroot, LUA_TEMPLATES)
+local search_lib = gen_search_func(searchroot, LIB_TEMPLATES)
+local search_rocks_lua = gen_search_func(searchroot, ROCKS_LUA_TEMPLATES, true)
+local search_rocks_lib = gen_search_func(searchroot, ROCKS_LIB_TEMPLATES, true)
+
+local search_funcs = {
+    search_lua,
+    search_lib,
+    search_rocks_lua,
+    search_rocks_lib,
+    function(name) return package.searchpath(name, package.path) end,
+    function(name) return package.searchpath(name, package.cpath) end,
+}
 
 local function search(name)
     if not name then
         return "empty name of module"
     end
-    local searchers = {
-        search_cwd_lua, search_cwd_lib,
-        search_rocks_lua, search_rocks_lib,
-        search_path_func(false), search_path_func(true)
-    }
-    for _, searcher in ipairs(searchers) do
+    for _, searcher in ipairs(search_funcs) do
         local file = searcher(name)
         if file ~= nil then
             return file
@@ -194,15 +213,17 @@ local function search(name)
 end
 
 -- loader_preload 1
-table.insert(package.loaders, 2, cwd_loader_func(false))
-table.insert(package.loaders, 3, cwd_loader_func(true))
-table.insert(package.loaders, 4, rocks_loader_func(false))
-table.insert(package.loaders, 5, rocks_loader_func(true))
+table.insert(package.loaders, 2, gen_loader_func(search_lua, load_lua))
+table.insert(package.loaders, 3, gen_loader_func(search_lib, load_lib))
+table.insert(package.loaders, 4, gen_loader_func(search_rocks_lua, load_lua))
+table.insert(package.loaders, 5, gen_loader_func(search_rocks_lib, load_lib))
 -- package.path   6
 -- package.cpath  7
 -- croot          8
 
 package.search = search
+package.searchroot = searchroot
+package.setsearchroot = setsearchroot
 
 return {
     uptime = uptime;
diff --git a/test/app/loaders.result b/test/app/loaders.result
index 1f4c9820502501d469fa46846d7f4147f091d3e4..969529599ee9cb5015bdeada73ccdff749ea34ac 100644
--- a/test/app/loaders.result
+++ b/test/app/loaders.result
@@ -78,7 +78,7 @@ test_run:cmd("setopt delimiter ';'");
 ---
 - true
 ...
-function create_dirs(name)
+function create_rocks_dirs(name)
     fio.mkdir(name)
     fio.mkdir(name .. "/.rocks")
     fio.mkdir(name .. "/.rocks/share")
@@ -92,13 +92,13 @@ test_run:cmd("setopt delimiter ''");
 ---
 - true
 ...
-create_dirs(work_dir)
+create_rocks_dirs(work_dir)
 ---
 ...
-create_dirs(pr1_dir)
+create_rocks_dirs(pr1_dir)
 ---
 ...
-create_dirs(pr2_dir)
+create_rocks_dirs(pr2_dir)
 ---
 ...
 soext = (jit.os == "OSX" and "dylib" or "so")
@@ -202,6 +202,183 @@ fio.chdir(orig_cwd)
 ---
 - true
 ...
+--
+-- Check searchroot .rocks loader
+--
+-- Rocks loader does not look into subdirectories
+package.loaded.loaders = nil
+---
+...
+package.loaded.loaders1 = nil
+---
+...
+package.loaded.loaderslib = nil
+---
+...
+f = rocks_loader("loaders")
+---
+...
+type(f) -- error
+---
+- string
+...
+f = rocks_loader_dyn("loaderslib")
+---
+...
+type(f) -- error
+---
+- string
+...
+f = rocks_loader("loaders1")
+---
+...
+type(f) -- error
+---
+- string
+...
+-- Rocks loader traverses paths upwards
+package.setsearchroot(pr2_dir)
+---
+...
+f = rocks_loader("loaders")
+---
+...
+type(f) -- function
+---
+- function
+...
+f()
+---
+- success
+...
+f = rocks_loader_dyn("loaderslib")
+---
+...
+type(f) -- function
+---
+- function
+...
+f()
+---
+- success
+...
+f = rocks_loader("loaders1")
+---
+...
+type(f) -- error
+---
+- string
+...
+--
+-- Check searchroot loader
+--
+package.loaded.loaders = nil
+---
+...
+package.loaded.loaders1 = nil
+---
+...
+package.loaded.loaderslib = nil
+---
+...
+lua_loader = package.loaders[2]
+---
+...
+lib_loader = package.loaders[3]
+---
+...
+fio.symlink(loaders_path, fio.pathjoin(work_dir, "loaders.lua"))
+---
+- true
+...
+fio.symlink(loaderslib_path, fio.pathjoin(pr1_dir, "loaderslib."..soext))
+---
+- true
+...
+-- Reset searchroot
+package.setsearchroot(box.NULL)
+---
+...
+-- Libs are not accessible from cwd
+f = lua_loader("loaders")
+---
+...
+type(f) -- error
+---
+- string
+...
+f = lib_loader("loaderslib")
+---
+...
+type(f) -- error
+---
+- string
+...
+package.loaded.loaders = nil
+---
+...
+package.loaded.loaders1 = nil
+---
+...
+package.loaded.loaderslib = nil
+---
+...
+-- "loaders" module is located in work_dir
+package.setsearchroot(work_dir)
+---
+...
+f = lua_loader("loaders")
+---
+...
+type(f) -- function
+---
+- function
+...
+f()
+---
+- success
+...
+f = lib_loader("loaderslib")
+---
+...
+type(f) -- error
+---
+- string
+...
+package.loaded.loaders = nil
+---
+...
+package.loaded.loaders1 = nil
+---
+...
+package.loaded.loaderslib = nil
+---
+...
+-- "loaderslib" module is located in pr1_dir
+package.setsearchroot(pr1_dir)
+---
+...
+f = lua_loader("loaders")
+---
+...
+type(f) -- error
+---
+- string
+...
+f = lib_loader("loaderslib")
+---
+...
+type(f) -- function
+---
+- function
+...
+f()
+---
+- success
+...
+package.setsearchroot(box.NULL)
+---
+...
 fio.rmtree(tmp_dir)
 ---
 - true
diff --git a/test/app/loaders.test.lua b/test/app/loaders.test.lua
index b98ad93337fb7b69c381a156b5fcdc83b894cba7..f7a903bbc24bb751d19d9a52dea39f60d0fd431b 100644
--- a/test/app/loaders.test.lua
+++ b/test/app/loaders.test.lua
@@ -30,7 +30,7 @@ lua_dir = ".rocks/share/tarantool"
 lib_dir = ".rocks/lib/tarantool"
 
 test_run:cmd("setopt delimiter ';'");
-function create_dirs(name)
+function create_rocks_dirs(name)
     fio.mkdir(name)
     fio.mkdir(name .. "/.rocks")
     fio.mkdir(name .. "/.rocks/share")
@@ -40,9 +40,9 @@ function create_dirs(name)
 end;
 test_run:cmd("setopt delimiter ''");
 
-create_dirs(work_dir)
-create_dirs(pr1_dir)
-create_dirs(pr2_dir)
+create_rocks_dirs(work_dir)
+create_rocks_dirs(pr1_dir)
+create_rocks_dirs(pr2_dir)
 
 soext = (jit.os == "OSX" and "dylib" or "so")
 loaders_path = fio.pathjoin(source_dir, "loaders.lua")
@@ -78,4 +78,90 @@ f = rocks_loader_dyn("loaderslib")
 type(f) -- error
 
 fio.chdir(orig_cwd)
+
+--
+-- Check searchroot .rocks loader
+--
+
+-- Rocks loader does not look into subdirectories
+
+package.loaded.loaders = nil
+package.loaded.loaders1 = nil
+package.loaded.loaderslib = nil
+
+f = rocks_loader("loaders")
+type(f) -- error
+f = rocks_loader_dyn("loaderslib")
+type(f) -- error
+f = rocks_loader("loaders1")
+type(f) -- error
+
+-- Rocks loader traverses paths upwards
+
+package.setsearchroot(pr2_dir)
+
+f = rocks_loader("loaders")
+type(f) -- function
+f()
+f = rocks_loader_dyn("loaderslib")
+type(f) -- function
+f()
+f = rocks_loader("loaders1")
+type(f) -- error
+
+--
+-- Check searchroot loader
+--
+
+package.loaded.loaders = nil
+package.loaded.loaders1 = nil
+package.loaded.loaderslib = nil
+
+lua_loader = package.loaders[2]
+lib_loader = package.loaders[3]
+
+fio.symlink(loaders_path, fio.pathjoin(work_dir, "loaders.lua"))
+fio.symlink(loaderslib_path, fio.pathjoin(pr1_dir, "loaderslib."..soext))
+
+-- Reset searchroot
+
+package.setsearchroot(box.NULL)
+
+-- Libs are not accessible from cwd
+
+f = lua_loader("loaders")
+type(f) -- error
+f = lib_loader("loaderslib")
+type(f) -- error
+
+package.loaded.loaders = nil
+package.loaded.loaders1 = nil
+package.loaded.loaderslib = nil
+
+-- "loaders" module is located in work_dir
+
+package.setsearchroot(work_dir)
+
+f = lua_loader("loaders")
+type(f) -- function
+f()
+f = lib_loader("loaderslib")
+type(f) -- error
+
+package.loaded.loaders = nil
+package.loaded.loaders1 = nil
+package.loaded.loaderslib = nil
+
+-- "loaderslib" module is located in pr1_dir
+
+package.setsearchroot(pr1_dir)
+
+f = lua_loader("loaders")
+type(f) -- error
+f = lib_loader("loaderslib")
+type(f) -- function
+f()
+
+package.setsearchroot(box.NULL)
+
 fio.rmtree(tmp_dir)