Debugging OpenResty and Nginx Lua scripts with ZeroBrane Studio

ZeroBrane Studio has already been used to debug various Lua engines -- game frameworks (like Corona, Gideros, Moai, Love2d), home automation devices, wireshark scripts, Adobe Lightroom plugins, and more -- but there have been several Lua environments that I haven't tried it on. One of them is OpenResty/Nginx Lua scripts. OpenResty is a web application server built around nginx, a very fast web server, that provides non-blocking IO with various backends (Redis, Memcached, MySQL, HTTP servers, and others) and supports Lua as its scripting language.

I first tried to use the non-blocking socket API that OpenResty provides, but couldn't get the debugging to work because socket calls that were supposed to be blocking were returning too early, which was breaking the interaction between the debugger in the application and the IDE. Based on advice from Yichun Zhang (agentzh), the maintainer of the OpenResty bundle, I then tried to use the same luasocket library that ZeroBrane Studio is using and got it all working. These are the steps you can follow to try it yourself:

ZeroBrane Studio configuration.

1. Get ZeroBrane Studio. These instructions are for Windows, but the debugging should work on Linux and OSX as well.

2. Start ZBS (zbstudio.exe or and start the debugger Project | Start Debugger Server.

OpenResty configuration.

1. I'm using a very basic config (<NGINX>/conf/nginx.conf):

worker_processes  1;
events {
    worker_connections  1024;
http {
    lua_package_path '<ZBS>/lualibs/?/?.lua;<ZBS>/lualibs/?.lua;;';
    lua_package_cpath '<ZBS>/bin/clibs/?.dll;;';
    server {
        location /hellolua {
           default_type 'text/plain';
           content_by_lua_file 'lua/content.lua';

Make sure you replace <ZBS> with the actual path to ZeroBrane Studio location. If you are running on OSX, replace ?.dll with ?.dylib and if you are running on Linux, replace bin/clibs/?.dll with either bin/linux/x86/clibs/?.so or bin/linux/x64/clibs/?.so depending on your platform.

2. Create the file we are going to debug (<NGINX>/lua/content.lua), which may look like this:

local name = ngx.var.arg_name or "Anonymous"
ngx.say("Hello, ", name, "!")
ngx.say("Done debugging.")

Note that start() call takes the IP of the computer running the IDE. It uses "localhost" by default, but since your nginx instance is running there, you will need to specify the IP address of the computer running the IDE (in my case it is

3. Open this file (<NGINX>/lua/content.lua) file in the IDE and set the project directory to lua folder by going to Project | Project Directory | Set From Current File.

Script debugging.

Now start nginx and go to http://localhost/hellolua. If everything is right, you should see ZeroBrane Studio activated with the green arrow pointing to the second line (similar to what can be seen in the screenshot above). You can now set breakpoints, step through the code, look at the stack and so on.

You can also go to the remote console and run any ngx command there. For example, if you run ngx.say("Message from console") (as shown in the screenshot), you will see this text in the output after the script is done.

If you get "attempt to yield across C-call boundary" error in Nginx logs when you start debugging, try with a more recent ZeroBrane Studio (0.70+) as it includes several improvements that make debugging to work with recent versions of OpenResty.

OpenResty configuration for remote debugging.

The following steps are only needed if you configure OpenResty/Nginx for remote debugging when Nginx runs on one machine and ZeroBrane Studio runs on another one. When you run both on the same machine, Nginx is using modules included with ZeroBrane Studio as those are referenced by lua_package_path and lua_package_cpath directives.

1. Copy the debugger (mobdebug.lua) and socket files. Go to <ZBS>/lualibs/mobdebug/ and copy mobdebug.lua to <NGINX>/lua/mobdebug.lua; also copy <ZBS>/lualibs/socket.lua to <NGINX>/lua/. After all this is done, the content of lua folder is going to look like this:


2. Copy <ZBS>/bin/clibs/socket/core.dll to <NGINX>/socket/core.dll (core.dylib and files are in the bin folder as well).

You should get a copy of my slick ZeroBrane Studio IDE.


Hi Paul, I've tried debugging in linux as your guide, but could not make it work. After I made a request to http://localhost/hellolua, I got an error log in nginx's error.log, "[error] 23503#0: *22 lua entry thread aborted: runtime error: attempt to yield across C-call boundary" and nginx ended the request with a "500 Internal Server Error" response. It seemed that the luasocket which mobdebug is using produced the error. I was using openresty-1.5.8(with the bunlded LuaJIT-2.1-20140109) and zbstudio-0.60 in SuSE Linux, I also tried in Ubuntu 12.10 using openresty-1.5.12 and zbstudio-0.60 and got the same error. Did I miss somthing? Any suggestion? Any help would be greatly appreciated!

Hi LiuJun, I initially got the same error -- "attempt to yield across C-call boundary" -- and this is why I switched to using "blocking" luasocket, as this is needed for the debugger. Is it possible that in your case you use a nonblocking luasocket that comes with OpenResty?

Try removing references to "luapackagecpath" and replacing the content of content.lua with something like: "print(1) require 'socket' print(2)". This should fail if luasocket is indeed unavailable. If it does correctly reference the version from ZeroBrane Studio, I don't see why it would fail, but I haven't tested it on Linux. I'll try to set it up on my VM to see if I can reproduce this issue.

I removed the zbstudio's clibs path from lua_package_cpath as you suggested, and got the following error when require "socket":

"[error] 10473#0: *29 lua entry thread aborted: runtime error: /opt/zbstudio/lualibs/socket.lua:12: module 'socket.core' not found:"

So I think the lua script was using luasocket from zbstudio, not from openresty.

LiuJun, you seem to be doing everything correctly and the results are as expected. I'll have to try it myself on Linux to see what may be going on (in a day or two).

LiuJun, I can confirm the same issue. I tried on Ubuntu 12.04 using the latest OpenResty ( and got the same error. I don't have a good explanation for you and am working on a test case. Drop me an email (my address is at the bottom of the page) and I'll send you an update when I figure out what may be going on. Paul.

LiuJun, you need to add "coroutine = require('coroutine')" before "start()" call. It seems like openresty on Linux is using "lightweight threads" instead of "default" coroutines and those interfere with debugging as they are managed by ngx_lua. Paul.

It works smoothly now. Thank you very much.

LiuJun, glad it's working for you. The workaround I suggested earlier is likely not to work in the coming version of OpenResty. This issue should be fixed in the current master branch of ZeroBrane Studio; please check that version as it uses updated debugger, which doesn't require any workarounds.

Hi Paul, I'm trying to get the remote debugging going and seem to have trouble getting the two boxes to communicate. The actual server is an AWS Ubuntu box and the studio is running locally on my windows machine. Essentially, it looks like the script simply gives up a few minutes after I refresh the page (displays the following without ever reaching the studio running on my machine): "Hello, Anonymous! Done debugging." Is there any specific port or range of ports I need to open up to get this going, do you know? Thank you very much!!

Hi Ilya, per our email exchange, you should make sure the (default) port 8172 is open on your computer running the IDE as this is the port that is used for debugging. Also make sure that "require('mobdebug').start('IP-address-of-ZBS')" call includes the IP address of the machine that runs ZeroBrane Studio. This should resolve the issue.

We salute you, Thank you very much!!!

works, many thanks!

Leave a comment

what will you say?