diff --git a/.luacheckrc b/.luacheckrc
new file mode 100644
index 0000000000000000000000000000000000000000..2e2f2775e791dec68557f6e41b0fdbd936952840
--- /dev/null
+++ b/.luacheckrc
@@ -0,0 +1,76 @@
+std = 'luajit'
+new_read_globals = {
+	'help',
+	'quit',
+	'hostname',
+	'moduledir',
+	'user',
+	'verbose',
+	'resolve',
+	'tojson',
+	'todname',
+	'map',
+	'net',
+	'cache',
+	'modules',
+	'trust_anchors',
+	'worker',
+	'event',
+	'_hint_root_file',
+	-- Sandbox declarations
+	'kB',
+	'MB',
+	'GB',
+	'sec',
+	'second',
+	'minute',
+	'min',
+	'hour',
+	'day',
+	'panic',
+	'warn',
+	'log',
+	'mode',
+	'trust_anchors',
+	'reorder_RR',
+	'option',
+	'env',
+	'kres',
+	'trustanchor',
+	'libknot_SONAME',
+	'libzscanner_SONAME',
+	'table_print',
+	'__engine',
+	'_ENV',
+	'_SANDBOX',
+}
+
+new_globals = {
+	-- Modules are allowed to be set and accessed from global namespace
+	'policy',
+	'view',
+	'stats',
+	'http',
+}
+
+-- Luacheck < 0.18 doesn't support new_read_globals
+for _, v in ipairs(new_read_globals) do
+	table.insert(new_globals, v)
+end
+
+-- Ignore test files
+exclude_files = {
+	'modules/policy/lua-aho-corasick', -- Vendored
+}
+
+-- Ignore some pedantic checks
+ignore = {
+	'4.1/err', -- Shadowing err
+	'4.1/.',   -- Shadowing one letter variables
+}
+
+-- Sandbox can set global variables
+files['daemon/lua'].ignore = {'111', '121', '122'}
+-- Tests and scripts can use global variables
+files['scripts'].ignore = {'111', '112', '113'}
+files['tests'].ignore = {'111', '112', '113'}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 144721c0c41fb716c2b294224036be7bb36006a2..58093eb87d65e74e92146d54abb3316488f0a25d 100644
--- a/Makefile
+++ b/Makefile
@@ -8,6 +8,8 @@ check: all tests
 clean: contrib-clean lib-clean daemon-clean client-clean modules-clean \
 	tests-clean doc-clean bench-clean
 doc: doc-html
+lint:
+	luacheck --codes .
 .PHONY: all install check clean doc info
 
 # Options
diff --git a/doc/build.rst b/doc/build.rst
index 3427645a3176d3b6959e3c59eeed068bff6201f0..e9dde53819af857b5c4a9ae4f858d93cf9eef363 100644
--- a/doc/build.rst
+++ b/doc/build.rst
@@ -62,6 +62,7 @@ There are also *optional* packages that enable specific functionality in Knot DN
    "libprotobuf_ 3.0+", "``modules/dnstap``", "Protocol Buffers support for dnstap_."
    "`libprotobuf-c`_ 1.0+", "``modules/dnstap``", "C bindings for Protobuf."
    "libfstrm_ 0.2+", "``modules/dnstap``", "Frame Streams data transport protocol."
+   "luacheck_", "``lint``", "Syntax and static analysis checker for Lua."
 
 .. [#] Requires C99, ``__attribute__((cleanup))`` and ``-MMD -MP`` for dependency file generation. GCC, Clang and ICC are supported.
 .. [#] You can use variables ``<dependency>_CFLAGS`` and ``<dependency>_LIBS`` to configure dependencies manually (i.e. ``libknot_CFLAGS`` and ``libknot_LIBS``).
@@ -252,6 +253,7 @@ The project can be built with code coverage tracking using the ``COVERAGE=1`` va
 Running unit and integration tests
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
+The linter requires luacheck_ and is executed by ``make lint``.
 The unit tests require cmocka_ and are executed by ``make check``.
 Tests for the dnstap module need go and are executed by ``make ckeck-dnstap``.
 
@@ -319,5 +321,6 @@ You can hack on the container by changing the container entrypoint to shell like
 .. _libprotobuf: https://developers.google.com/protocol-buffers/
 .. _libprotobuf-c: https://github.com/protobuf-c/protobuf-c/wiki
 .. _libfstrm: https://github.com/farsightsec/fstrm
+.. _luacheck: http://luacheck.readthedocs.io
 
 .. _DESTDIR: https://www.gnu.org/prep/standards/html_node/DESTDIR.html