diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index fea8640a6f4c185f3dff1cccecad9029e4a9f1fc..cc5a3451af97b19a5b3db7031fc05d11a3d68fe6 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -18,6 +18,8 @@ local tuple_encode = box.internal.tuple.encode
 local tuple_bless = box.internal.tuple.bless
 local is_tuple = box.tuple.is
 assert(tuple_encode ~= nil and tuple_bless ~= nil and is_tuple ~= nil)
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_put = buffer.internal.cord_ibuf_put
 
 local INT64_MIN = tonumber64('-9223372036854775808')
 local INT64_MAX = tonumber64('9223372036854775807')
@@ -1785,9 +1787,12 @@ base_index_mt.__len = base_index_mt.len
 -- min and max
 base_index_mt.min_ffi = function(index, key)
     check_index_arg(index, 'min')
-    local pkey, pkey_end = tuple_encode(key)
-    if builtin.box_index_min(index.space_id, index.id,
-                             pkey, pkey_end, ptuple) ~= 0 then
+    local ibuf = cord_ibuf_take()
+    local pkey, pkey_end = tuple_encode(ibuf, key)
+    local nok = builtin.box_index_min(index.space_id, index.id, pkey, pkey_end,
+                                      ptuple) ~= 0
+    cord_ibuf_put(ibuf)
+    if nok then
         box.error() -- error
     elseif ptuple[0] ~= nil then
         return tuple_bless(ptuple[0])
@@ -1802,9 +1807,12 @@ base_index_mt.min_luac = function(index, key)
 end
 base_index_mt.max_ffi = function(index, key)
     check_index_arg(index, 'max')
-    local pkey, pkey_end = tuple_encode(key)
-    if builtin.box_index_max(index.space_id, index.id,
-                             pkey, pkey_end, ptuple) ~= 0 then
+    local ibuf = cord_ibuf_take()
+    local pkey, pkey_end = tuple_encode(ibuf, key)
+    local nok = builtin.box_index_max(index.space_id, index.id, pkey, pkey_end,
+                                      ptuple) ~= 0
+    cord_ibuf_put(ibuf)
+    if nok then
         box.error() -- error
     elseif ptuple[0] ~= nil then
         return tuple_bless(ptuple[0])
@@ -1837,10 +1845,12 @@ end
 -- iteration
 base_index_mt.pairs_ffi = function(index, key, opts)
     check_index_arg(index, 'pairs')
-    local pkey, pkey_end = tuple_encode(key)
+    local ibuf = cord_ibuf_take()
+    local pkey, pkey_end = tuple_encode(ibuf, key)
     local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
 
     local keybuf = ffi.string(pkey, pkey_end - pkey)
+    cord_ibuf_put(ibuf)
     local pkeybuf = ffi.cast('const char *', keybuf)
     local cdata = builtin.box_index_iterator(index.space_id, index.id,
         itype, pkeybuf, pkeybuf + #keybuf);
@@ -1864,10 +1874,12 @@ end
 -- index subtree size
 base_index_mt.count_ffi = function(index, key, opts)
     check_index_arg(index, 'count')
-    local pkey, pkey_end = tuple_encode(key)
+    local ibuf = cord_ibuf_take()
+    local pkey, pkey_end = tuple_encode(ibuf, key)
     local itype = check_iterator_type(opts, pkey + 1 >= pkey_end);
     local count = builtin.box_index_count(index.space_id, index.id,
         itype, pkey, pkey_end);
+    cord_ibuf_put(ibuf)
     if count == -1 then
         box.error()
     end
@@ -1882,9 +1894,12 @@ end
 
 base_index_mt.get_ffi = function(index, key)
     check_index_arg(index, 'get')
-    local key, key_end = tuple_encode(key)
-    if builtin.box_index_get(index.space_id, index.id,
-                             key, key_end, ptuple) ~= 0 then
+    local ibuf = cord_ibuf_take()
+    local key, key_end = tuple_encode(ibuf, key)
+    local nok = builtin.box_index_get(index.space_id, index.id, key, key_end,
+                                      ptuple) ~= 0
+    cord_ibuf_put(ibuf)
+    if nok then
         return box.error() -- error
     elseif ptuple[0] ~= nil then
         return tuple_bless(ptuple[0])
@@ -1915,13 +1930,15 @@ end
 
 base_index_mt.select_ffi = function(index, key, opts)
     check_index_arg(index, 'select')
-    local key, key_end = tuple_encode(key)
+    local ibuf = cord_ibuf_take()
+    local key, key_end = tuple_encode(ibuf, key)
     local iterator, offset, limit = check_select_opts(opts, key + 1 >= key_end)
 
     local port = ffi.cast('struct port *', port_c)
-
-    if builtin.box_select(index.space_id, index.id,
-        iterator, offset, limit, key, key_end, port) ~= 0 then
+    local nok = builtin.box_select(index.space_id, index.id, iterator, offset,
+                                   limit, key, key_end, port) ~= 0
+    cord_ibuf_put(ibuf)
+    if nok then
         return box.error()
     end
 
diff --git a/src/box/lua/tuple.c b/src/box/lua/tuple.c
index ffd01d89994a306dac03cef7387efaf7d243f39e..f7198a0251b6d726ddb7ed90a5e0a29aa9b30a9a 100644
--- a/src/box/lua/tuple.c
+++ b/src/box/lua/tuple.c
@@ -36,6 +36,7 @@
 #include "diag.h" /* diag_set() */
 #include <small/ibuf.h>
 #include <small/region.h>
+#include "cord_buf.h"
 #include <fiber.h>
 
 #include "box/tuple.h"
@@ -280,16 +281,18 @@ luaT_tuple_encode(struct lua_State *L, int idx, size_t *tuple_len_ptr)
 box_tuple_t *
 luaT_tuple_new(struct lua_State *L, int idx, box_tuple_format_t *format)
 {
-	struct ibuf *ibuf = tarantool_lua_ibuf;
-	ibuf_reset(ibuf);
+	struct ibuf *ibuf = cord_ibuf_take();
 	size_t tuple_len;
+	box_tuple_t *tuple;
 	char *tuple_data = luaT_tuple_encode_on_lua_ibuf(L, idx, &tuple_len,
 							 ibuf);
-	if (tuple_data == NULL)
-		return NULL;
-	box_tuple_t *tuple = box_tuple_new(format, tuple_data,
-					   tuple_data + tuple_len);
-	ibuf_reinit(ibuf);
+	if (tuple_data == NULL) {
+		tuple = NULL;
+	} else {
+		tuple = box_tuple_new(format, tuple_data,
+				      tuple_data + tuple_len);
+	}
+	cord_ibuf_drop(ibuf);
 	return tuple;
 }
 
@@ -308,12 +311,11 @@ lbox_tuple_new(lua_State *L)
 	 */
 	box_tuple_format_t *fmt = box_tuple_format_default();
 	if (argc != 1 || (!lua_istable(L, 1) && !luaT_istuple(L, 1))) {
-		struct ibuf *buf = tarantool_lua_ibuf;
-		ibuf_reset(buf);
+		struct ibuf *buf = cord_ibuf_take();
 		luaT_tuple_encode_values(L, buf); /* may raise */
 		struct tuple *tuple = box_tuple_new(fmt, buf->buf,
 						    buf->buf + ibuf_used(buf));
-		ibuf_reinit(buf);
+		cord_ibuf_drop(buf);
 		if (tuple == NULL)
 			return luaT_error(L);
 		luaT_pushtuple(L, tuple);
@@ -573,8 +575,7 @@ lbox_tuple_transform(struct lua_State *L)
 		return 1;
 	}
 
-	struct ibuf *buf = tarantool_lua_ibuf;
-	ibuf_reset(buf);
+	struct ibuf *buf = cord_ibuf_take();
 	struct mpstream stream;
 	mpstream_init(&stream, buf, ibuf_reserve_cb, ibuf_alloc_cb,
 		      luamp_error, L);
@@ -619,12 +620,11 @@ lbox_tuple_transform(struct lua_State *L)
 		new_tuple = tuple_new(box_tuple_format_default(),
 				      new_data, new_data + new_size);
 	region_truncate(region, used);
-
+	cord_ibuf_put(buf);
 	if (new_tuple == NULL)
 		luaT_error(L);
 
 	luaT_pushtuple(L, new_tuple);
-	ibuf_reset(buf);
 	return 1;
 }
 
diff --git a/src/box/lua/tuple.lua b/src/box/lua/tuple.lua
index 813d66ff04242a2453692cbd5d47157d46ad641c..fa76f4f7f99fe9678ba5d60ab83c7fe1232ec94b 100644
--- a/src/box/lua/tuple.lua
+++ b/src/box/lua/tuple.lua
@@ -5,6 +5,8 @@ local msgpackffi = require('msgpackffi')
 local fun = require('fun')
 local buffer = require('buffer')
 local internal = require('box.internal')
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_put = buffer.internal.cord_ibuf_put
 
 ffi.cdef[[
 /** \cond public */
@@ -71,9 +73,7 @@ local encode_fix = msgpackffi.internal.encode_fix
 local encode_array = msgpackffi.internal.encode_array
 local encode_r = msgpackffi.internal.encode_r
 
-local tuple_encode = function(obj)
-    local tmpbuf = buffer.IBUF_SHARED
-    tmpbuf:reset()
+local tuple_encode = function(tmpbuf, obj)
     if obj == nil then
         encode_fix(tmpbuf, 0x90, 0)  -- empty array
     elseif is_tuple(obj) then
@@ -229,8 +229,10 @@ local function tuple_update(tuple, expr)
     if type(expr) ~= 'table' then
         error("Usage: tuple:update({ { op, field, arg}+ })")
     end
-    local pexpr, pexpr_end = tuple_encode(expr)
+    local ibuf = cord_ibuf_take()
+    local pexpr, pexpr_end = tuple_encode(ibuf, expr)
     local tuple = builtin.box_tuple_update(tuple, pexpr, pexpr_end)
+    cord_ibuf_put(ibuf)
     if tuple == nil then
         return box.error()
     end
@@ -242,8 +244,10 @@ local function tuple_upsert(tuple, expr)
     if type(expr) ~= 'table' then
         error("Usage: tuple:upsert({ { op, field, arg}+ })")
     end
-    local pexpr, pexpr_end = tuple_encode(expr)
+    local ibuf = cord_ibuf_take()
+    local pexpr, pexpr_end = tuple_encode(ibuf, expr)
     local tuple = builtin.box_tuple_upsert(tuple, pexpr, pexpr_end)
+    cord_ibuf_put(ibuf)
     if tuple == nil then
         return box.error()
     end
diff --git a/src/exports.h b/src/exports.h
index eb72bc928e56fb70a58c00a5aa45046f1d818f4c..ddbe5723062594b5b8658b6c64295ecae2907653 100644
--- a/src/exports.h
+++ b/src/exports.h
@@ -113,6 +113,9 @@ EXPORT(coio_getaddrinfo)
 EXPORT(coio_wait)
 EXPORT(console_get_output_format)
 EXPORT(console_set_output_format)
+EXPORT(cord_ibuf_drop)
+EXPORT(cord_ibuf_put)
+EXPORT(cord_ibuf_take)
 EXPORT(cord_slab_cache)
 EXPORT(crc32_calc)
 EXPORT(crypto_EVP_MD_CTX_free)
@@ -493,7 +496,6 @@ EXPORT(swim_set_codec)
 EXPORT(swim_set_payload)
 EXPORT(swim_size)
 EXPORT(tarantool_exit)
-EXPORT(tarantool_lua_ibuf)
 EXPORT(tarantool_lua_slab_cache)
 EXPORT(tarantool_uptime)
 EXPORT(title_get)
diff --git a/src/lib/core/CMakeLists.txt b/src/lib/core/CMakeLists.txt
index 30cf0dd152ad3eec92a232bc726cfb7e19860a01..2cd4d0b4f39828211b208a5dc79cca8d03c6271a 100644
--- a/src/lib/core/CMakeLists.txt
+++ b/src/lib/core/CMakeLists.txt
@@ -29,6 +29,7 @@ set(core_sources
     port.c
     decimal.c
     mp_decimal.c
+    cord_buf.c
 )
 
 if (TARGET_OS_NETBSD)
diff --git a/src/lib/core/cord_buf.c b/src/lib/core/cord_buf.c
new file mode 100644
index 0000000000000000000000000000000000000000..cac508c3d515075acce58e0997553ca1ef338ca4
--- /dev/null
+++ b/src/lib/core/cord_buf.c
@@ -0,0 +1,47 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#include "cord_buf.h"
+#include "fiber.h"
+
+#include "small/ibuf.h"
+
+enum {
+	/* No any reason why that value. Historical constant. */
+	CORD_IBUF_START_CAPACITY = 16384,
+};
+
+static struct ibuf *cord_buf_global = NULL;
+
+struct ibuf *
+cord_ibuf_take(void)
+{
+	assert(cord_is_main());
+	struct ibuf *buf = cord_buf_global;
+	if (buf != NULL) {
+		ibuf_reset(buf);
+		return buf;
+	}
+	buf = malloc(sizeof(*buf));
+	if (buf == NULL)
+		panic("Couldn't allocate thread buffer");
+	ibuf_create(buf, &cord()->slabc, CORD_IBUF_START_CAPACITY);
+	cord_buf_global = buf;
+	return buf;
+}
+
+void
+cord_ibuf_put(struct ibuf *ibuf)
+{
+	(void)ibuf;
+	assert(ibuf == cord_buf_global);
+}
+
+void
+cord_ibuf_drop(struct ibuf *ibuf)
+{
+	ibuf_reinit(ibuf);
+	assert(ibuf == cord_buf_global);
+}
diff --git a/src/lib/core/cord_buf.h b/src/lib/core/cord_buf.h
new file mode 100644
index 0000000000000000000000000000000000000000..59f429c8f9e4d39b9a41f1f30c3e09fa02dd4439
--- /dev/null
+++ b/src/lib/core/cord_buf.h
@@ -0,0 +1,45 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright 2010-2021, Tarantool AUTHORS, please see AUTHORS file.
+ */
+#pragma once
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+struct ibuf;
+
+/**
+ * Take the global ibuf, or allocate a new one if the stash is empty.
+ */
+struct ibuf *
+cord_ibuf_take(void);
+
+/**
+ * Put the global ibuf back.
+ */
+void
+cord_ibuf_put(struct ibuf *ibuf);
+
+/**
+ * Put the global ibuf back and free its memory. So only the buffer object
+ * itself is saved to the stash. Main reason why it is a dedicated function is
+ * because it is often needed from Lua, and allows not to call :recycle() there,
+ * which would be an additional FFI call before cord_ibuf_put().
+ *
+ * XXX: recycle of the global buffer is a workaround for the ibuf being used in
+ * some places working with Lua API, where it wasn't wanted to "reuse" it
+ * anyhow. Instead, the global buffer is used to protect from the buffer leak in
+ * case it would be created locally, and then a Lua error would be raised. When
+ * the buffer is global, it is not a problem, because it is reused/recycled
+ * later. But it hurts the places, where re-usage could be good. Probably it is
+ * worth to separate take/put() from new/drop() API. Or delete drop() entirely.
+ */
+void
+cord_ibuf_drop(struct ibuf *ibuf);
+
+#if defined(__cplusplus)
+}
+#endif /* defined(__cplusplus) */
diff --git a/src/lua/buffer.lua b/src/lua/buffer.lua
index 00846bb20a4049f21980cf900fe9b383874f090f..d5dbedb0aa2effe64cb803395598ca2ec38e7d5b 100644
--- a/src/lua/buffer.lua
+++ b/src/lua/buffer.lua
@@ -7,7 +7,15 @@ ffi.cdef[[
 struct slab_cache;
 struct slab_cache *
 tarantool_lua_slab_cache();
-extern struct ibuf *tarantool_lua_ibuf;
+
+struct ibuf *
+cord_ibuf_take(void);
+
+void
+cord_ibuf_put(struct ibuf *ibuf);
+
+void
+cord_ibuf_drop(struct ibuf *ibuf);
 
 struct ibuf
 {
@@ -244,9 +252,48 @@ end
 --
 local reg_array = ffi.new('union c_register[?]', 2)
 
+--
+-- Cord buffer is useful for the places, where
+--
+-- * Want to reuse the already allocated memory which might be stored in the
+--   cord buf. Although sometimes the buffer is recycled, so should not rely on
+--   being able to reuse it always. When reused, the win is the biggest -
+--   becomes about x20 times faster than a new buffer creation (~5ns vs ~100ns);
+--
+-- * Want to avoid allocation of a new ibuf because it produces a new GC object
+--   which is additional load for Lua GC. Although according to benches it is
+--   not super expensive;
+--
+-- * Almost always can put the buffer back manually. Not rely on it being
+--   recycled automatically. It is recycled, but still should not rely on that;
+--
+-- It is important to wrap the C functions, not expose them directly. Because
+-- JIT works a bit better when C functions are called as 'ffi.C.func()' than
+-- 'func()' with func being cached. The only pros is to cache 'ffi.C' itself.
+-- It is quite strange though how having them wrapped into a Lua function is
+-- faster than cached directly as C functions.
+--
+local function cord_ibuf_take()
+    return builtin.cord_ibuf_take()
+end
+
+local function cord_ibuf_put(buf)
+    return builtin.cord_ibuf_put(buf)
+end
+
+local function cord_ibuf_drop(buf)
+    return builtin.cord_ibuf_drop(buf)
+end
+
+local internal = {
+    cord_ibuf_take = cord_ibuf_take,
+    cord_ibuf_put = cord_ibuf_put,
+    cord_ibuf_drop = cord_ibuf_drop,
+}
+
 return {
+    internal = internal,
     ibuf = ibuf_new;
-    IBUF_SHARED = ffi.C.tarantool_lua_ibuf;
     READAHEAD = READAHEAD;
     static_alloc = static_alloc,
     -- Keep reference.
diff --git a/src/lua/iconv.lua b/src/lua/iconv.lua
index e68509dec4b5b64b39d9d65592a3e1d08bdb3f90..732b80514f50af2e4ffc2092f9be01b43e2bf800 100644
--- a/src/lua/iconv.lua
+++ b/src/lua/iconv.lua
@@ -1,6 +1,8 @@
 local ffi    = require('ffi')
 local errno  = require('errno')
 local buffer = require('buffer')
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_put = buffer.internal.cord_ibuf_put
 
 ffi.cdef[[
 typedef struct iconv *iconv_t;
@@ -33,10 +35,9 @@ local function iconv_convert(iconv, data)
 
     -- prepare at lease BUF_SIZE and at most data_len bytes in shared buffer
     local output_len = data_len >= BUF_SIZE and data_len or BUF_SIZE
-    local buf      = buffer.IBUF_SHARED;
+    local buf      = cord_ibuf_take();
     local buf_ptr  = char_ptr_arr_t()
     local buf_left = size_t_arr_t()
-    buf:reset()
 
     while data_left[0] > 0 do
         buf_ptr[0]  = buf:reserve(output_len)
@@ -46,6 +47,7 @@ local function iconv_convert(iconv, data)
         if res == ffi.cast('size_t', -1) then
             local err = errno()
             if err ~= E2BIG then
+                cord_ibuf_put(buf)
                 ffi.C.tnt_iconv(iconv, nil, nil, nil, nil)
                 if err == EINVAL then
                     error('Invalid multibyte sequence')
@@ -62,7 +64,7 @@ local function iconv_convert(iconv, data)
     -- iconv function sets cd's conversion state to the initial state
     ffi.C.tnt_iconv(iconv, nil, nil, nil, nil)
     local result = ffi.string(buf.rpos, buf:size())
-    buf:reset()
+    cord_ibuf_put(buf)
     return result
 end
 
diff --git a/src/lua/init.c b/src/lua/init.c
index 767abdfc59ed89e76d124c34843326b78dd9955c..89d7f8f73bf44d7ece7804b345ff9b8388769ad0 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -73,8 +73,6 @@
  * The single Lua state of the transaction processor (tx) thread.
  */
 struct lua_State *tarantool_L;
-static struct ibuf tarantool_lua_ibuf_body;
-struct ibuf *tarantool_lua_ibuf = &tarantool_lua_ibuf_body;
 /**
  * The fiber running the startup Lua script
  */
@@ -453,7 +451,6 @@ tarantool_lua_init(const char *tarantool_bin, int argc, char **argv)
 	if (L == NULL) {
 		panic("failed to initialize Lua");
 	}
-	ibuf_create(tarantool_lua_ibuf, tarantool_lua_slab_cache(), 16000);
 	luaL_openlibs(L);
 	tarantool_lua_setpaths(L);
 
diff --git a/src/lua/msgpack.c b/src/lua/msgpack.c
index 24b0d2ccd60326ec007ff9c802ffcb3f76656663..1e74a6a3cb758d73c63a5fee93e6469bbfcfa4ff 100644
--- a/src/lua/msgpack.c
+++ b/src/lua/msgpack.c
@@ -46,6 +46,7 @@
 #include "lib/uuid/mp_uuid.h" /* mp_decode_uuid() */
 #include "lib/core/mp_extension_types.h"
 
+#include "cord_buf.h"
 #include <fiber.h>
 
 void
@@ -362,8 +363,7 @@ lua_msgpack_encode(lua_State *L)
 					  "must be of type 'struct ibuf'");
 		}
 	} else {
-		buf = tarantool_lua_ibuf;
-		ibuf_reset(buf);
+		buf = cord_ibuf_take();
 	}
 	size_t used = ibuf_used(buf);
 
@@ -380,7 +380,7 @@ lua_msgpack_encode(lua_State *L)
 		lua_pushinteger(L, ibuf_used(buf) - used);
 	} else {
 		lua_pushlstring(L, buf->buf, ibuf_used(buf));
-		ibuf_reinit(buf);
+		cord_ibuf_drop(buf);
 	}
 	return 1;
 }
diff --git a/src/lua/msgpackffi.lua b/src/lua/msgpackffi.lua
index 2bc37474281243b06f9505ee52a15117e3fd8eaf..6c245d7aa675187ebde92c06b8f432009e00d055 100644
--- a/src/lua/msgpackffi.lua
+++ b/src/lua/msgpackffi.lua
@@ -10,6 +10,8 @@ local uint16_ptr_t = ffi.typeof('uint16_t *')
 local uint32_ptr_t = ffi.typeof('uint32_t *')
 local uint64_ptr_t = ffi.typeof('uint64_t *')
 local char_ptr_t = ffi.typeof('char *')
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_drop = buffer.internal.cord_ibuf_drop
 
 ffi.cdef([[
 char *
@@ -294,11 +296,10 @@ local function encode_r(buf, obj, level)
 end
 
 local function encode(obj)
-    local tmpbuf = buffer.IBUF_SHARED
-    tmpbuf:reset()
+    local tmpbuf = cord_ibuf_take()
     encode_r(tmpbuf, obj, 0)
     local r = ffi.string(tmpbuf.rpos, tmpbuf:size())
-    tmpbuf:recycle()
+    cord_ibuf_drop(tmpbuf)
     return r
 end
 
diff --git a/src/lua/socket.lua b/src/lua/socket.lua
index 78d91f010eb6cd4e9c786bd744fc8ef311b346ae..7c24b56550528bc41a5ec215dfcd0afecd87c72f 100644
--- a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -13,6 +13,8 @@ local buffer = require('buffer')
 local reg1 = buffer.reg1
 local reg2 = buffer.reg2
 local static_alloc = buffer.static_alloc
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_drop = buffer.internal.cord_ibuf_drop
 
 local format = string.format
 
@@ -296,19 +298,15 @@ local function socket_sysread(self, arg1, arg2)
         error('socket:sysread(): size can not be negative')
     end
 
-    local buf = buffer.IBUF_SHARED
-    buf:reset()
+    local buf = cord_ibuf_take()
     local p = buf:alloc(size)
 
     local res = sysread(self, p, size)
     if res then
-        local str = ffi.string(p, res)
-        buf:recycle()
-        return str
-    else
-        buf:recycle()
-        return res
+        res = ffi.string(p, res)
     end
+    cord_ibuf_drop(buf)
+    return res
 end
 
 local function socket_nonblock(self, nb)
diff --git a/src/lua/swim.lua b/src/lua/swim.lua
index 1da55337adab644538b25348955c3315bcc569db..42b0d15ef0578afacf1f3ee0147a248707a6b00d 100644
--- a/src/lua/swim.lua
+++ b/src/lua/swim.lua
@@ -6,6 +6,8 @@ local crypto = require('crypto')
 local fiber = require('fiber')
 local internal = require('swim')
 local schedule_task = fiber._internal.schedule_task
+local cord_ibuf_take = buffer.internal.cord_ibuf_take
+local cord_ibuf_put = buffer.internal.cord_ibuf_put
 
 ffi.cdef[[
     struct swim;
@@ -655,14 +657,17 @@ end
 local function swim_set_payload(s, payload)
     local func_name = 'swim:set_payload'
     local ptr = swim_check_instance(s, func_name)
-    local payload_size = 0
-    if payload ~= nil then
-        local buf = buffer.IBUF_SHARED
-        buf:reset()
-        payload_size = msgpack.encode(payload, buf)
+    local rc
+    if payload == nil then
+        rc = capi.swim_set_payload(ptr, nil, 0)
+    else
+        local buf = cord_ibuf_take()
+        local payload_size = msgpack.encode(payload, buf)
         payload = buf.rpos
+        rc = capi.swim_set_payload(ptr, payload, payload_size)
+        cord_ibuf_put(buf)
     end
-    if capi.swim_set_payload(ptr, payload, payload_size) ~= 0 then
+    if rc ~= 0 then
         return nil, box.error.last()
     end
     return true
diff --git a/src/lua/utf8.c b/src/lua/utf8.c
index 9c678cad46493a07e4a99fdf3685d6ab0055e514..bf9bb98f45326a0eb53f523611b39aaffe2843e9 100644
--- a/src/lua/utf8.c
+++ b/src/lua/utf8.c
@@ -33,12 +33,11 @@
 #include "coll/coll.h"
 #include "lua/utils.h"
 #include "lua/utf8.h"
+#include "cord_buf.h"
 #include "diag.h"
 #include "small/ibuf.h"
 #include "tt_static.h"
 
-extern struct ibuf *tarantool_lua_ibuf;
-
 /** Collations for cmp/casecmp functions. */
 static struct coll *unicode_coll = NULL;
 static struct coll *unicode_ci_coll = NULL;
@@ -52,12 +51,13 @@ utf8_str_to_case(struct lua_State *L, const char *src, int src_bsize,
 	int i = 0;
 	int dst_bsize = src_bsize;
 	(void) i;
+	struct ibuf *ibuf = cord_ibuf_take();
 	do {
 		UErrorCode err = U_ZERO_ERROR;
-		ibuf_reset(tarantool_lua_ibuf);
-		char *dst = ibuf_alloc(tarantool_lua_ibuf, dst_bsize);
+		char *dst = ibuf_alloc(ibuf, dst_bsize);
 		if (dst == NULL) {
 			diag_set(OutOfMemory, dst_bsize, "ibuf_alloc", "dst");
+			cord_ibuf_put(ibuf);
 			return luaT_error(L);
 		}
 		int real_bsize;
@@ -73,11 +73,13 @@ utf8_str_to_case(struct lua_State *L, const char *src, int src_bsize,
 		if (err == U_ZERO_ERROR ||
 		    err == U_STRING_NOT_TERMINATED_WARNING) {
 			lua_pushlstring(L, dst, real_bsize);
+			cord_ibuf_put(ibuf);
 			return 1;
 		} else if (err == U_BUFFER_OVERFLOW_ERROR) {
 			assert(real_bsize > dst_bsize);
 			dst_bsize = real_bsize;
 		} else {
+			cord_ibuf_put(ibuf);
 			lua_pushnil(L);
 			lua_pushstring(L, tt_sprintf("error during ICU case "\
 						     "transform: %s",
@@ -249,9 +251,10 @@ utf8_char(struct lua_State *L)
 		return 1;
 	}
 	/* Slow way - use dynamic buffer. */
-	ibuf_reset(tarantool_lua_ibuf);
-	char *str = ibuf_alloc(tarantool_lua_ibuf, top * U8_MAX_LENGTH);
+	struct ibuf *ibuf = cord_ibuf_take();
+	char *str = ibuf_alloc(ibuf, top * U8_MAX_LENGTH);
 	if (str == NULL) {
+		cord_ibuf_put(ibuf);
 		diag_set(OutOfMemory, top * U8_MAX_LENGTH, "ibuf_alloc",
 			 "str");
 		return luaT_error(L);
@@ -261,6 +264,7 @@ utf8_char(struct lua_State *L)
 		U8_APPEND_UNSAFE(str, len, c);
 	}
 	lua_pushlstring(L, str, len);
+	cord_ibuf_put(ibuf);
 	return 1;
 }
 
diff --git a/src/lua/utils.h b/src/lua/utils.h
index 37531676d18c6af2df060b455157294d6e34bcfe..4a164868bc675c30477dbbde97eb6bad0abb9916 100644
--- a/src/lua/utils.h
+++ b/src/lua/utils.h
@@ -72,7 +72,6 @@ struct tt_uuid;
  * snprintf(m_errmsg, sizeof(m_errmsg), "%s", msg ? msg : "");
  */
 extern struct lua_State *tarantool_L;
-extern struct ibuf *tarantool_lua_ibuf;
 
 struct tt_uuid *
 luaL_pushuuid(struct lua_State *L);
diff --git a/static-build/test/static-build/exports.test.lua b/static-build/test/static-build/exports.test.lua
index 9b9eaa471f43b5cec265a2ba8acd64b9e39479bd..d5dbe7de9d5eb5ea6d7cd00fd9ce84cb103a0347 100755
--- a/static-build/test/static-build/exports.test.lua
+++ b/static-build/test/static-build/exports.test.lua
@@ -26,6 +26,9 @@ local check_symbols = {
     'guava',
     'base64_decode',
     'base64_encode',
+    'cord_ibuf_drop',
+    'cord_ibuf_put',
+    'cord_ibuf_take',
     'SHA1internal',
     'random_bytes',
     'fiber_time',
@@ -42,7 +45,6 @@ local check_symbols = {
     'exception_get_int',
     'exception_get_string',
 
-    'tarantool_lua_ibuf',
     'uuid_nil',
     'tt_uuid_create',
     'tt_uuid_str',
diff --git a/test/unit/luaT_tuple_new.c b/test/unit/luaT_tuple_new.c
index 965b2e6e0e67929cfbed86ab17a627ed182f9216..1d7c9072afad6508a88d5f8ea43defc690a6a041 100644
--- a/test/unit/luaT_tuple_new.c
+++ b/test/unit/luaT_tuple_new.c
@@ -25,8 +25,6 @@
  * box/tuple.test.lua.
  */
 
-extern struct ibuf *tarantool_lua_ibuf;
-
 uint32_t
 min_u32(uint32_t a, uint32_t b)
 {
@@ -184,8 +182,6 @@ main()
 	memory_init();
 	fiber_init(fiber_c_invoke);
 
-	ibuf_create(tarantool_lua_ibuf, &cord()->slabc, 16000);
-
 	struct lua_State *L = luaL_newstate();
 	luaL_openlibs(L);