diff --git a/src/box/lua/config/utils/odict.lua b/src/box/lua/config/utils/odict.lua
index ba973f7f4ce408bef453f7bef695d86577faef1a..bb92e89e875f46df073a7a992e962c98e05ab92f 100644
--- a/src/box/lua/config/utils/odict.lua
+++ b/src/box/lua/config/utils/odict.lua
@@ -65,7 +65,7 @@ local function gen(od, prev_key)
 
     -- The previous key is nil only on the first call of the
     -- generator function.
-    local id = prev_key == nil and 0 or ctx.key2id[prev_key]
+    local id = type(prev_key) == 'nil' and 0 or ctx.key2id[prev_key]
     -- NB: This assert doesn't catch all the kinds of changes
     -- during an iteration. It rather verifies a precondition
     -- for the following cycle.
@@ -80,7 +80,7 @@ local function gen(od, prev_key)
         -- id2key may contain a stalled entry, because __newindex
         -- is not called on assignment of an existing field,
         -- including assignment to nil.
-        if key ~= nil and od[key] ~= nil then
+        if type(key) ~= 'nil' and od[key] ~= nil then
             return key, od[key]
         end
     end
@@ -148,11 +148,11 @@ local function reindex(od, ctx)
     for id = 1, old_max_id do
         local key = ctx.id2key[id]
         -- Drop the given key-id pair from the key<->id mappings.
-        if key ~= nil then
+        if type(key) ~= 'nil' then
             release(ctx, key)
         end
         -- Add the new key-id pair into the key<->id mappings.
-        if key ~= nil and od[key] ~= nil then
+        if type(key) ~= 'nil' and od[key] ~= nil then
             track(ctx, key)
         end
     end
diff --git a/test/config-luatest/odict_test.lua b/test/config-luatest/odict_test.lua
index 0ddd52dcd9299dcf75822ad7506d7903f5fb316a..a66b943d3b87a29198e70503fb17db88cec87051 100644
--- a/test/config-luatest/odict_test.lua
+++ b/test/config-luatest/odict_test.lua
@@ -1,4 +1,5 @@
 local fun = require('fun')
+local ffi = require('ffi')
 local odict = require('internal.config.utils.odict')
 local t = require('luatest')
 
@@ -117,6 +118,41 @@ g.test_pairs_delete_and_set = function()
     })
 end
 
+-- Using cdata as a key is unusual, but it is legal in LuaJIT.
+--
+-- There is a potential pitfall: ffi.new('void *') == nil. Let's
+-- verify that we handle such a key correctly.
+g.test_null_as_key = function()
+    local od = odict.new()
+
+    local nulls = {
+        ffi.new('void *'),
+        ffi.new('void *'),
+        ffi.new('void *'),
+    }
+
+    od[nulls[1]] = 1
+    od[nulls[2]] = 2
+    od[nulls[3]] = 3
+
+    local res = {}
+    for k, v in odict.pairs(od) do
+        table.insert(res, {k, v})
+    end
+
+    -- It doesn't differentiate nulls.
+    t.assert_equals(res, {
+        {nulls[1], 1},
+        {nulls[2], 2},
+        {nulls[3], 3},
+    })
+
+    -- It does.
+    t.assert(rawequal(res[1][1], nulls[1]))
+    t.assert(rawequal(res[2][1], nulls[2]))
+    t.assert(rawequal(res[3][1], nulls[3]))
+end
+
 -- {{{ Helpers for reindexing test cases
 
 -- Parse a range expressed like Python's slice operator.