diff --git a/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md b/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md
new file mode 100644
index 0000000000000000000000000000000000000000..58a3659a7a548d82d709714122e5f39523cdeb58
--- /dev/null
+++ b/changelogs/unreleased/gh-8051-set-box-cfg-thru-env.md
@@ -0,0 +1,4 @@
+## feature/box/cfg
+
+* Implemented a way to set a table as box.cfg{} options value via
+  environment variables (gh-8051).
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 3ccf9bc65ed9b3314aa6a8737de5875ab00650bb..d4f3fae9e0dad4381e0b387a55995cafa9eb84ac 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -1151,7 +1151,30 @@ local function get_option_from_env(option)
 
     -- This code lean on the existing set of template_cfg
     -- types for simplicity.
-    if param_type:find('table') and raw_value:find(',') then
+    if param_type:find('table') and (raw_value:startswith('{') or
+                                     raw_value:startswith('[')) then
+        return json.decode(raw_value)
+    elseif param_type:find('table') and raw_value:find('=') then
+        assert(not param_type:find('boolean'))
+        local res = {}
+        for _, v in ipairs(raw_value:split(',')) do
+            local eq = v:find('=')
+            if eq == nil then
+                error(err_msg_fmt:format(env_var_name, option,
+                                         'in `key=value` or `value` format'))
+            end
+            local lhs = string.sub(v, 1, eq - 1)
+            local rhs = string.sub(v, eq + 1)
+
+            if lhs == '' then
+                error(err_msg_fmt:format(env_var_name, option,
+                                         'in `key=value` or `value` format, ' ..
+                                         '`key` must not be empty'))
+            end
+            res[lhs] = tonumber(rhs) or rhs
+        end
+        return res
+    elseif param_type:find('table') and raw_value:find(',') then
         assert(not param_type:find('boolean'))
         local res = {}
         for i, v in ipairs(raw_value:split(',')) do
diff --git a/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua b/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..fcbec619ec846b9047b8b7b2344129a35a1cab53
--- /dev/null
+++ b/test/box-luatest/gh_8051_set_box_cfg_thru_env_test.lua
@@ -0,0 +1,92 @@
+local server = require('luatest.server')
+local popen = require('popen')
+local clock = require('clock')
+
+local t = require('luatest')
+local g = t.group()
+
+g.after_each = function()
+    if g.server ~= nil then
+       g.server:stop()
+   end
+end
+
+local TARANTOOL_PATH = arg[-1]
+
+local function popen_run(command)
+    local cmd = TARANTOOL_PATH .. ' -i 2>&1'
+    local ph = popen.new({cmd}, {
+        shell = true,
+        setsid = true,
+        group_signal = true,
+        stdout = popen.opts.PIPE,
+        stderr = popen.opts.DEVNULL,
+        stdin = popen.opts.PIPE,
+    })
+    t.assert(ph, 'process is not up')
+
+    ph:write(command)
+
+    local output = ''
+    local time_quota = 10.0
+    local start_time = clock.monotonic()
+    while clock.monotonic() - start_time < time_quota do
+        local chunk = ph:read({timeout = 1.0})
+        if chunk == '' or chunk == nil then
+            -- EOF or error
+            break
+        end
+        output = output .. chunk
+    end
+
+    ph:close()
+    return output
+end
+
+g.test_json_table_curly_bracket = function()
+    local env = {["TT_METRICS"] = '{"labels":{"alias":"gh_8051"},' ..
+                                  '"include":"all","exclude":["vinyl"]}'}
+
+    g.server = server:new{alias='json_table_curly_bracket', env=env}
+    g.server:start()
+
+    t.assert_equals(g.server:get_box_cfg().metrics.labels.alias, 'gh_8051')
+end
+
+g.test_json_table_square_bracket = function()
+    local res = popen_run([=[
+        os.setenv('TT_LISTEN', '["localhost:0"]')
+        box.cfg{}
+        box.cfg.listen
+    ]=])
+
+    t.assert_str_contains(res, "- ['localhost:0']")
+end
+
+g.test_plain_table = function()
+    local env = {["TT_LOG_MODULES"] = 'aaa=info,bbb=error'}
+
+    g.server = server:new{alias='plain_table', env=env}
+    g.server:start()
+
+    t.assert_equals(g.server:get_box_cfg().log_modules,
+                    {['aaa'] = 'info', ['bbb'] = 'error'})
+end
+
+g.test_format_error = function()
+    local res = popen_run([[
+        os.setenv('TT_LOG_MODULES', 'aaa=info,bbb')
+        box.cfg{}
+    ]])
+
+    t.assert_str_contains(res, "in `key=value` or `value` format'")
+end
+
+g.test_format_error_empty_key = function()
+    local res = popen_run([[
+        os.setenv('TT_LOG_MODULES', 'aaa=info,=error')
+        box.cfg{}
+    ]])
+
+    t.assert_str_contains(res, "`key` must not be empty'")
+end