Debugging PHP with XDEBUG 3 in Docker with VSCode ( Visual Studio Code )

Today we will learn how we debug a PHP application inside a DOCKER container with VSCODE. Debugging a PHP application using a debugger has a lot of benefits. It gives you insights into the code that you can never have while using manual debugging techniques such as var_dump(); die;.

With XDEBUG we can see the execution flow of the program and can examine variables just by adding breakpoints in our IDE effortlessly. XDEBUG enables us to navigate and understand complex codebases with ease and helps in identifying and fixing problems in our code that are usually very hard to find without a debugger.

Another benefit of using XDEBUG is that we can execute our custom PHP code on breakpoints and change the behaviour of the code to test and debug.

The code we are going to write is already available on github so you can follow this tutorial with ease.

Let’s start by creating a directory and the necessary files first.

Directory Structure and Files

We are going to need following directories and files for this application to work.

    - .vscode
        - launch.json
    - docker
        - site.conf
        - xdebug.ini
    - cli.php
    - docker-compose.yml
    - Dockerfile
    - index.php

Now that we know the files we are going to need lets create all the files one by one and also explain the contents.


Lets first create the Dockerfile. In our Dockerfile we are extending the php:8-fpm image and installing xdebug-3.1.3 on top of it.

FROM php:8-fpm

RUN pecl install xdebug-3.1.3 \
    && docker-php-ext-enable xdebug


Now that we have our Dockerfile ready lets create our docker-compose.yml file which will use our Dockerfile to build and start our application. Lets create a new file named docker-compose.yml in the root of the project and put the following code in it.

version: "3"
    image: nginx:latest
      - "8080:80"
      - "./:/var/www/html"
      - "./docker/site.conf:/etc/nginx/conf.d/default.conf"

      context: .
      - "./:/var/www/html"
      - ./docker/xdebug.ini:/usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
      - "host.docker.internal:host-gateway"

I will not go into too much details about the contents of docker-compose.yml file but will point out things that are of interest to us.

We are creating 2 services named webserver and php. webserver as the name indicates is going to be the webserver that will serve our application to the browsers and will communicate with our php service to execute php code. And the php service will be responsible to executing the php scripts

We are using volumes to mount our project files inside the docker containers. We are mounting our application code to a directory /var/www/html inside both services ( containers ). We are also mounting the docker/xdebug.ini to /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini in the php container ( service ) and the docker/site.conf to /etc/nginx/config.d/default.conf in webserver container ( service )


Lets create the xdebug configuration file in docker/xdebug.ini and put the following contents in it



Here we are setting the xdebug.mode to develop, debug which enables us to add breakpoints in our code and debug. There a more values that you can use here like coverage, profile, trace but we are not going to use them here. If you want to know in detail what each of these options do please read the xdebug documentation here
xdebug.client_host value tells xdebug the host we want to connect for debugging sessions, in our case it will be our local machine. In docker you can not access your host machine as localhost instead we have created this host host.docker.internal in our docker-compose.yml file to point to our host machine.
You can learn more about xdebug.start_with_request setting on the xdebug documentation


Lets create the nginx configuration file in docker/site.conf and put the following contents in it

server {
    listen 80 default_server;
    listen [::]:80 default_server;

    index index.php index.html;

    error_log  /var/log/nginx/error.log;
    access_log /var/log/nginx/access.log;
    root /var/www/html/;

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;

This is file is used by nginx to serve our application to web browsers. It sends the php code scripts to php service ( container ) for execution and sends back the response to the browser. If you are interested in learning more about how nginx works with php have look at this tutorial


The index.php is going to be a very simple file with following contents.

$a = 'Test';
$b = 1;

echo $a . '<br />';
echo $b . '<br />';


This file is responsible for configuring the VSCODE debugging. The contents are listed below

    "version": "0.2.0",
    "configurations": [
            "name": "Listen for Xdebug",
            "type": "php",
            "request": "launch",
            "port": 9003,
            "pathMappings": {
                "/var/www/html": "${workspaceFolder}"

This file is generated by VSCODE and the most important settings are port and pathMappings. The value of port is 9003 as for xdebug 3 the default value is 9003. In pathMappings we are telling the debugging server the directory it should map to our host machine directory so the IDE and XDEBUG can find the files properly.

Running the Docker Application

Now that we have all the files needed already created it is time to run our application for the first time. To run the application open the project directory in VSCODE and open New Terminal by clicking Terminal > New Terminal from the top menu and run docker compose up

This will build our entire application and will also start it if everything goes well. After the command completes successfully you will see some output in the terminal window.

PHP Docker Xdebug startup log
PHP Docker Xdebug startup log

Now open your browser and go to http://localhost:8080 and you should be able to see the output of your index.php script in the browser.

Debugging With XDEBUG

Now that we have our application running we can do the fun stuff of actually debugging our application. Open VSCODE and go to the debug page and click the Start Debugging button or press F5 to start the debugging session as shown in the image below.

VSCODE debug page
VSCODE debug page

You should see a orange bar at the bottom of the VSCODE. It indicates that the IDE is listening for incoming debugging session requests.

VSCODE Debugging Started
VSCODE Debugging Started

Now that our debugging server is running, open index.php and set a new breakpoint at line number 4 by clicking on the red dot that appears when you hover over the line number. Now go to your browser and reload http://localhost:8080 page. Now go to VSCODE and you should see line number 4 highligted which means that we have started a debugging session and the program is now paused at line number 4.

VSCODE debugger running
VSCODE debugger running

You also see a small set of buttons in the top center of VSCODE which has buttons for Continue, Step Over and other buttons which you can use to move forward in your code either line by line or breakpoint by breakpoint.

VSCODE step debugging
VSCODE step debugging

You should also see 4 panels in your editor sidebar named VARIABLES, WATCH, CALL STACK and BREAKPOINTS. All of these panels are useful while debugging and I will surely write a detailed post about how to use them to our advantage very soon.

Thats all for the day, I hope you now know more then you used to before reading this article about debugging php applications with xdebug in vscode. Let me know if I have missed something in the comments. Cheers.

Leave a Reply