Nextcloud PHP debugging with Xdebug in Zed editor

I started to switch from PhpStorm to Zed as IDE recently as Zed is open source and has a much smaller footprint and is more slick than PhpStorm.

One thing that I didn't get running immediately was Xdebug integration, so I did a bit of research and asked Claude for help. Here's a quick writeup of how to get it running.

I have Zed installed as Flatpak on a Debian Trixie host system.

The PHP process runs in a nextcloud-docker-dev Docker container.

Install Zed and configure debugging there

  1. Install Zed: flatpak install flathub dev.zed.Zed
  2. In Zed: open the Extensions view and install PHP.
  3. Configure the debugger:
    • Create ~/.var/app/dev.zed.Zed/config/zed/debug.json: json [ { "label": "PHP: Listen to Xdebug", "adapter": "Xdebug", "request": "launch", "port": 9003, "pathMappings": { "/var/www/html": "/home/<you>/devel/nextcloud/server", "/var/www/html/apps-shared": "/home/<you>/devel/nextcloud/apps-shared" } } ] Add one entry per bind-mounted app directory. Check the mounts with docker inspect --format '{{json .Mounts}}' | jq. Restart Zed.
    • Restart Zed
    • Select "debugger: start" from command palette and then "PHP: Listen to Xdebug"
  4. Verify Zed is listening. Running ss -tlnp | grep 9003 on the host should show *:9003 with Zed as the process.

Configure Xdebug inside the container

/usr/local/etc/php/conf.d/xdebug.ini:

xdebug.mode = debug
xdebug.idekey = PHPSTORM
xdebug.trace_output_name=trace.%R.%u
xdebug.profiler_output_name=profile.%R.%u
xdebug.output_dir=/shared/xdebug

xdebug.log = /var/log/xdebug.log
xdebug.log_level = 3

; Try to discover the client host, otherwise fall back to the docker host
xdebug.discover_client_host=true
xdebug.client_host=host.docker.internal

; When you cannot specify a trigger, use "xdebug.start_with_request = yes" to autostart debugging for all requests
; https://xdebug.org/docs/all_settings#start_with_request
xdebug.start_with_request = trigger

; Set xdebug.mode trace to use this
; More details at https://derickrethans.nl/flamboyant-flamegraphs.html
xdebug.trace_format=3
xdebug.trace_output_name=xdebug.%R.%u

Notes:

  • host.docker.internal resolves on Linux Docker only if the container was started with --add-host=host.docker.internal:host-gateway (nextcloud-docker-dev already does this).
  • discover_client_host = true makes xdebug follow X-Forwarded-For - useful behind Nextcloud's dev reverse proxy.

Apply changes by restarting apache in the container: apache2ctl -k graceful

Test xdebug with a PHP command inside the container

  • Run XDEBUG_SESSION=PHPSTORM php occ status inside the container
  • Check /var/log/xdebug.log

Install the browser extension

  • Install Xdebug Helper (Firefox/Chrome)
  • In its preferences, set the IDE Key to PhpStorm. It will set the XDEBUG_SESSION cookie when toggled to Debug.
  • Click the Xdebug Helper icon and set it to Debug.

Test Xdebug with browser extension

  • Load the URL that exercises the code path with the breakpoint.
  • Zed should stop the code exection at the breakpoint.