Thanks!
It took me a while to figure out what was going on, but after inspecting that line a bit and fiddling around, I’ve realized the following:
- Perhaps as should have been obvious, you need to run that command (
xcaddy build --with $(grep module go.mod | awk '{print $2}')=$PWD)
from the root directory of your plugin
- The
(grep module go.mod | awk '{print $2}')
part is just looking within the go.mod file for the first module
line and then extracting the module name/plugin repo url (e.g. github.com/user-name/plugin-name
)
- The $PWD part is, obviously, the path of the present working directory in your terminal. I suppose you could even just replace
=$PWD
with =./
So, what its really doing is just dynamically populating this command, which is a way to build the caddy binary with a local version of a plugin package repo.
xcaddy build --with github.com/user-name/plugin-name=/local/path/to/plugin/package
This is already explained in the examples in the Custom Builds section of the xcaddy Readme
However, this alone didn’t seem to work for me. What I needed to do was set the XCADDY_DEBUG
environment variable with export XCADDY_DEBUG=1
.
@francislavoie is there a way to provide that variable as a CLI flag? If not, could it be added? That would result in much better DX than having to add an environment variable.
This config in launch.json
also worked for me
{
"name": "Debug Caddy (exec)",
"type": "go",
"request": "launch",
"mode": "exec",
"program": "${fileDirname}/caddy"
},
Change the program path to whatever you like - an absolute path to the caddy binary, or whatever else. What I’ve used there will use the path of whatever file is currently selected in the editor (e.g. the Caddyfile).
Then just launch it from the debugger tab.

An unexpected bonus of all of this is that you can also step debug Caddy itself. However, it uses the code that is stored in ~/go/pkg/mod/github.com/caddyserver/caddy/v2@v2.7.6/
. If you want to step debug a locally cloned (and perhaps modified) version of Caddy, you can clone the caddy repo somewhere (perhaps in a sibling directory within a parent project directory) and then build caddy with this (as was already explained in the xcaddy readme:
xcaddy build --with $(grep module go.mod | awk '{print $2}')=$PWD --with github.com/caddyserver/caddy/v2=../relative/path/to/caddy/repo
Finally, the instructions in the Xcaddy Readme’s For plugin Development section are useful. As it turns out, you don’t need to do any of the --with
stuff if you just use xcaddy run
or xcaddy list-modules
from within the plugin’s root directory. If the XCADDY_DEBUG
env variable is set in the terminal, then it’ll build it with debugging enabled (you can see -gcflags all=-N -l
in the terminal output, which indicates debugging support).
However, I can’t seem to leverage this to only run xcaddy build
(without the --with grep
stuff) from the plugin directory - it builds a debuggable (because of the env variable) caddy binary, but doesn’t include the plugin.
I suppose I’ll just stick to the xcaddy build --with
stuff, and then launch the debugger by pressing F5 (once that debug launcher has already been selected once).
I hope this helps others who stumble upon this later. While fmt.Printf("\n%v\n", var)
may be sufficient for the pros here who already know their way around Caddy and Go, I always get IMMENSE value from stepping through an application in order to see what does what, and when. As such, I also greatly look forward to digging into the guts of Caddy (the Call Stack is a great way to find places to place a breakpoint) to get a better appreciation for how it (and Go itself) works.