Skip to main content

Automatic setup

gm_rdb is a binary module that runs inside SRCDS and exposes Lua state for debugging. Use the built-in setup wizard. The walkthrough should appear when you install the extension for the first time. You can also start it yourself:
  1. Open the Command Palette (Ctrl+Shift+P)
  2. Run GLua: Setup Debugger
  3. Follow the wizard steps. It will download the correct gm_rdb binary for your server’s OS and architecture, then guide you through placing the files
You can also click GLuaLS in the status bar to start the setup wizard.

Manual installation

The VS Code extension can set up the debugger for you. Use manual setup only if the extension setup fails.

Step 1: Download and install gm_rdb

Download the correct binary for your server from the GitHub releases page:
PlatformFile
Windows 64-bitgmsv_rdb_win64.dll
Windows 32-bitgmsv_rdb_win32.dll
Linux 64-bitgmsv_rdb_linux64.dll
Linux 32-bitgmsv_rdb_linux.dll
Place the binary in garrysmod/lua/bin/ on your server. Create the bin folder if it does not exist. The final path should be garrysmod/lua/bin/gmsv_rdb_*.dll. Place this loader script at garrysmod/lua/autorun/debug.lua:
garrysmod/lua/autorun/debug.lua
-- [GLuaLS] Auto-managed by GLuaLS extension. Do not edit.
_GLUALS = _GLUALS or {}

if SERVER then
    require("rdb")
    rdb.activate(21111)
    util.AddNetworkString("gm_rdb_exec")

    local function normalizeRealm(realm)
        realm = string.lower(tostring(realm or "server"))
        if realm ~= "server" and realm ~= "client" and realm ~= "shared" then
            realm = "server"
        end
        return realm
    end

    local function sendClientExec(kind, payload)
        net.Start("gm_rdb_exec")
        net.WriteString(kind)
        net.WriteString(payload)
        net.Broadcast()
    end

    local function runServerChunk(code, chunkName)
        local fn, compileErr = CompileString(code, chunkName or "gluals_run_lua", false)
        if not isfunction(fn) then
            return false, tostring(compileErr)
        end
        local ok, runtimeErr = xpcall(fn, debug.traceback)
        if not ok then
            return false, tostring(runtimeErr)
        end
        return true
    end

    local function includeServerFile(filePath)
        local ok, includeErr = pcall(include, filePath)
        if not ok then
            return false, tostring(includeErr)
        end
        return true
    end

    local function readServerFileForClient(filePath)
        local content = file.Read(filePath, "LUA")
        if isstring(content) then
            return content
        end

        content = file.Read("lua/" .. filePath, "GAME")
        if isstring(content) then
            return content
        end

        return nil, "unable to read file for client execution: " .. filePath
    end

    function _GLUALS.runLua(realm, code)
        realm = normalizeRealm(realm)
        if type(code) ~= "string" then
            return false, "lua chunk must be a string"
        end

        if realm == "server" or realm == "shared" then
            local ok, err = runServerChunk(code, "gluals_run_lua")
            if not ok then
                return false, err
            end
        end

        if realm == "client" or realm == "shared" then
            sendClientExec("lua", code)
        end

        return true
    end

    function _GLUALS.runFile(realm, filePath)
        realm = normalizeRealm(realm)
        filePath = tostring(filePath or "")
        if filePath == "" then
            return false, "file path is required"
        end

        if realm == "server" or realm == "shared" then
            local ok, err = includeServerFile(filePath)
            if not ok then
                return false, err
            end
        end

        if realm == "client" or realm == "shared" then
            local clientCode, readErr = readServerFileForClient(filePath)
            if not isstring(clientCode) then
                return false, tostring(readErr)
            end
            sendClientExec("lua", clientCode)
        end

        return true
    end

    function _GLUALS.refreshFile(filePath)
        filePath = tostring(filePath or "")
        if filePath == "" then
            return false, "file path is required"
        end

        if not game or not game.ConsoleCommand then
            return false, "game.ConsoleCommand is unavailable"
        end

        local quotedPath = string.format("%q", filePath)
        game.ConsoleCommand("lua_refresh_file " .. quotedPath .. "\n")
        return true
    end
end

if CLIENT then
    net.Receive("gm_rdb_exec", function()
        local kind = net.ReadString()
        local payload = net.ReadString()
        if kind == "lua" then
            local func, err = CompileString(payload, "gluals_client_exec", false)
            if not isfunction(func) then
                ErrorNoHalt("[GLuaLS] Client exec compile error: " .. tostring(err) .. "\n")
                return
            end

            local ok, runtimeErr = xpcall(func, debug.traceback)
            if not ok then
                ErrorNoHalt("[GLuaLS] Client exec runtime error: " .. tostring(runtimeErr) .. "\n")
            end
            return
        end

        ErrorNoHalt("[GLuaLS] Unknown exec kind: " .. tostring(kind) .. "\n")
    end)
end

This script comes from version 1.0.6 and may be outdated if you use a newer extension version. Use the setup wizard for the latest loader script.

Runtime launch flags

gm_rdb reads these command-line flags when the module loads. You can skip them unless you need the listed behavior.
FlagEffectDefault without flag
-rdb_allow_remoteAccept debugger connections from non-loopback addressesOnly localhost or loopback connections are allowed
-rdb_pause_on_activate [seconds]Pause on the next event after rdb.activate(...) runs. Optional timeout in seconds; use 0 to wait indefinitely.activate does not pause
Add these CLI flags to your Garry’s Mod or SRCDS launch options.

Step 2: Configure VS Code

Create a launch.json in your .vscode/ folder (or use the VS Code Run & Debug panel → Create a launch.json file):

Attach to a running server

Use attach when the server is already running. Most addon development uses this option.
Use the settings menu instead of editing the JSON file directly. This helps you avoid config mistakes.
.vscode/launch.json
{
    "version": "0.2.0",
    "configurations": [
        {
            "type": "gluals_gmod",
            "request": "attach",
            "name": "GMod Attach (SRCDS)",
            "host": "127.0.0.1",
            "port": 21111,
            "sourceRoot": "${workspaceFolder}/../..",
            "sourceFileMap": {
                "${workspaceFolder}/../../addons": "addons",
                "${workspaceFolder}/../../lua": "lua",
                "${workspaceFolder}/../../gamemodes/base": "gamemodes/base",
                "${workspaceFolder}/../../gamemodes/sandbox": "gamemodes/sandbox"
            },
            "stopOnEntry": false,
            "stopOnError": false,
            "realm": "server"
        }
    ]
}
  • “sourceRoot”: should point to your garrysmod folder (one level up from scrds)
  • “sourceFileMap” should map workspace folders to their corresponding paths, going from the sourceRoot folder.
Edit the configuration above to match your workspace structure. Most setups only need one new line in sourceFileMap. Addon example:
"${workspaceFolder}": "addons/your-addon-folder"
Gamemode example:
"${workspaceFolder}": "gamemodes/your-addon-folder"
This example covers common addon and gamemode layouts. You can use hardcoded paths, but other developers will need to edit them before using your config.

Launch a server

The launch option is unsupported and may not work on all systems. Use attach instead.

Step 3: Start debugging

  1. Start your SRCDS server
  2. In VS Code, open the Run & Debug panel (Ctrl+Shift+D)
  3. Select your launch configuration from the dropdown
  4. Press F5 (or click the green play button)
When VS Code connects, the debug toolbar appears at the top of the screen. The Debug Console opens in the bottom panel.

Configuration reference

Attach configuration

PropertyTypeDescription
hoststringServer hostname or IP. Default: "127.0.0.1"
portnumberPort gm_rdb is listening on. Default: 21111
sourceRootstringWorkspace path to map server files to. Default: ${workspaceFolder}
sourceFileMapobjectMap server paths to workspace paths manually
stopOnEntrybooleanPause at first line when connecting. Default: true
stopOnErrorbooleanPause when a Lua error is thrown. Default: false
realmstringDefault realm for code execution ("server" or "client")

Launch configuration (server process)

The launch option is unsupported and may not work on all systems. Use attach instead.
Includes the above, plus:
PropertyTypeDescription
programstringPath to srcds.exe or srcds_run
argsstring[]Additional server command-line arguments
cwdstringWorking directory for the server process

Changing the default port

To make gm_rdb listen on a different port, change this line in debug.lua:
garrysmod/lua/autorun/debug.lua
rdb.activate(<PORT_NUMBER>)
Update "port" in your launch.json to match.

Remote debugging

Do not use the debugger on untrusted or public networks. Use remote debugging only if you understand and accept the risk.
If debugging a remote server, start SRCDS with -rdb_allow_remote, open the gm_rdb port (default 21111) in your server’s firewall, and set host in your launch.json to the server’s reachable address. Without -rdb_allow_remote, gm_rdb only accepts loopback connections, so remote attach fails.
Do not enable this option unless you understand and accept the risk. If you enable this option on the client and expose your local port, any server could activate the debugger through clientside Lua.

Pause on activation

Do not use the debugger on untrusted or public networks. This option pauses execution once loaded (usually during startup/initial join). If no timeout is provided, execution resumes automatically after 60 seconds if no debugger connects.
rdb.activate(...) does not pause execution by default. The debugger may miss the server or client startup sequence unless you connect fast. To pause on the next hook event after rdb.activate(...) runs, add -rdb_pause_on_activate to the server startup arguments or client Steam launch options. You can provide a timeout in seconds.
-rdb_pause_on_activate
-rdb_pause_on_activate 120
-rdb_pause_on_activate 0
  • -rdb_pause_on_activate pauses on activation and resumes after 60 seconds if no debugger connects.
  • -rdb_pause_on_activate 120 pauses on activation with a 120-second timeout.
  • -rdb_pause_on_activate 0 pauses until a debugger connects.
Use this when you need to capture the server or client startup sequence.
If you enable this option on the client, any server could activate the debugger and freeze your game.