diff --git a/changelogs/unreleased/gh-10111-provide-key_def-length.md b/changelogs/unreleased/gh-10111-provide-key_def-length.md
new file mode 100644
index 0000000000000000000000000000000000000000..6b7a7cea0d0e0cde6a8544e2eeb38f5b8be752f2
--- /dev/null
+++ b/changelogs/unreleased/gh-10111-provide-key_def-length.md
@@ -0,0 +1,3 @@
+## feature/lua
+
+* Introduced a standard Lua way to get the length of `key_def` (gh-10111).
diff --git a/src/box/lua/key_def.c b/src/box/lua/key_def.c
index 2feadeb5fbd387fb66744d99b9272d10ad9a0c7a..a22d225f864435ba3ce962fe270b572563e883f0 100644
--- a/src/box/lua/key_def.c
+++ b/src/box/lua/key_def.c
@@ -633,6 +633,19 @@ lbox_key_def_to_table(struct lua_State *L)
 	return 1;
 }
 
+/**
+ * Return partitions count for key_def
+ */
+static int
+lbox_key_def_part_count(struct lua_State *L)
+{
+	struct key_def *key_def = luaT_is_key_def(L, 1);
+	if (key_def == NULL)
+		return luaL_error(L, "Usage: key_def:part_count()");
+	lua_pushinteger(L, key_def->part_count);
+	return 1;
+}
+
 int
 lbox_key_def_new(struct lua_State *L)
 {
@@ -704,6 +717,7 @@ luaopen_key_def(struct lua_State *L)
 		{"compare_keys", lbox_key_def_compare_keys},
 		{"merge", lbox_key_def_merge},
 		{"totable", lbox_key_def_to_table},
+		{"part_count", lbox_key_def_part_count},
 		{NULL, NULL}
 	};
 	luaT_newmodule(L, "key_def", meta);
diff --git a/src/box/lua/key_def.lua b/src/box/lua/key_def.lua
index fa911421da7694b73b9f5db7bc3ff2523fb5fb0e..f17d23e3be2f8de933923ef3674c672d9559afe4 100644
--- a/src/box/lua/key_def.lua
+++ b/src/box/lua/key_def.lua
@@ -20,4 +20,5 @@ ffi.metatype(key_def_t, {
         return methods[key]
     end,
     __tostring = function(self) return "<struct key_def &>" end,
+    __len = key_def.part_count,
 })
diff --git a/test/box-tap/key_def.test.lua b/test/box-tap/key_def.test.lua
index 22e91be543eb43df5e1680eda13ef17d55a49cda..02346e84cd2adc3c6a378c6abe73ff2c2cfdbd59 100755
--- a/test/box-tap/key_def.test.lua
+++ b/test/box-tap/key_def.test.lua
@@ -198,7 +198,7 @@ local key_def_new_cases = {
 
 local test = tap.test('key_def')
 
-test:plan(#key_def_new_cases - 1 + 8)
+test:plan(#key_def_new_cases - 1 + 9)
 for _, case in ipairs(key_def_new_cases) do
     if type(case) == 'function' then
         case()
@@ -598,4 +598,20 @@ test:test('Usage errors', function(test)
                    'totable()')
 end)
 
+-- Check key_def:part_count
+test:test('Key_def part_count', function(test)
+    test:plan(4)
+    local kd = key_def_lib.new({
+        {fieldno = 2, type = 'unsigned'}, {fieldno = 4, type = 'string'}
+    })
+    test:ok(kd, 'instance created')
+    test:is(#kd, 2, 'part_count')
+
+    local kd = key_def_lib.new({
+        {fieldno = 22, type = 'string'},
+    })
+    test:ok(kd, 'instance created')
+    test:is(#kd, 1, 'part_count')
+end)
+
 os.exit(test:check() and 0 or 1)