PHPStorm and Docker Compose: Connecting Multiple Services Correctly
AI generated
IDE
{ }
PHPStorm · Docker Compose · Xdebug · PHP
PHPStorm and Docker Compose:
Connecting Multiple Services Correctly

A Docker Compose project with a PHP container, MySQL, Redis, and Nginx is up and running quickly, but fully integrating PHPStorm so that the interpreter, debugger, database data source, and Composer all work from within the IDE requires targeted configuration in several places.

18 min read Remote Interpreter · Xdebug · Data Source · Composer PHPStorm 2024+ · Docker Compose v2

1. Why IDE integration is more than just an interpreter path

Many developers configure their Docker-Compose-based PHP interpreter in PHPStorm, notice that basic syntax checking works, and leave it at that. That gives away a significant part of the value PHPStorm can offer in containerized projects. A complete integration means: the debugger pauses at breakpoints inside the container, database queries run directly from the IDE, Composer commands execute in the correct container context, and PHPUnit tests run with coverage analysis, all without ever opening a terminal.

The effort of setting all of this up is a one-time cost per project. After that, the entire team works with an identical IDE configuration, provided the PHPStorm settings are version-controlled in the repository. This pays off twice over in Magento projects using a Mark Shust Docker setup, where the interpreter, CLI tools, and database run on separate services: no more manually switching between the terminal and the IDE, and no more guessing at values inside the database.

2. The Docker Compose setup: laying the groundwork

Before PHPStorm can connect to a container at all, a few prerequisites in the Compose setup must be met. The PHP container needs the Xdebug extension installed and a correct xdebug.ini. The MySQL container must have an exposed port that PHPStorm can use to reach the data source. In a Mark Shust setup, the PHP container is typically defined as the phpfpm service, and the MySQL container as db with port 3306 mapped to host port 3306.

Important: PHPStorm does not communicate with the container interpreter over Docker networks, but through the Docker daemon via a Unix socket or TCP. The daemon must be registered in PHPStorm under Settings → Build, Execution, Deployment → Docker. On Linux, the path points to /var/run/docker.sock, on macOS to the Docker Desktop socket. Without this base configuration, not a single container will appear in the interpreter dialog.


# docker-compose.yml: relevant excerpt for PHPStorm integration
services:
  phpfpm:
    build: .docker/phpfpm
    volumes:
      - ./src:/var/www/html:cached
      # Composer cache for performance
      - ~/.composer:/var/www/.composer:cached
    environment:
      XDEBUG_MODE: "debug,develop"
      XDEBUG_CONFIG: "client_host=host-gateway idekey=PHPSTORM"
    extra_hosts:
      - "host-gateway:host-gateway"

  db:
    image: mysql:8.0
    ports:
      - "3306:3306"
    environment:
      MYSQL_ROOT_PASSWORD: "${MYSQL_ROOT_PASSWORD}"
      MYSQL_DATABASE: "${MYSQL_DATABASE}"

  redis:
    image: redis:7-alpine
    ports:
      - "6379:6379"

The extra_hosts: host-gateway:host-gateway entry is essential for Xdebug on Linux: the container needs to know the host machine's IP in order to open the debugger connection back to PHPStorm. On macOS, host.docker.internal works as a hostname directly. The XDEBUG_CONFIG environment variable with client_host=host-gateway ensures Xdebug connects to the correct host; without this setting, the debugger just sits idle.

3. Setting up a remote PHP interpreter in PHPStorm

The remote interpreter is created in PHPStorm under Settings → PHP → CLI Interpreter. Click the plus icon, then select From Docker, Vagrant, VM, WSL, Remote. The dialog then offers a choice between Docker and Docker Compose. For Compose projects, choose Docker Compose, point it at the compose.yaml file, and select the phpfpm service. PHPStorm briefly starts the container, reads the PHP version and the extension list, and displays them in the dialog.

After creating the interpreter, the PHP language level under Settings → PHP must be set to the version installed in the container, for example PHP 8.4. This makes PHPStorm activate the correct syntax checks and code completion for the actual runtime environment. Choosing the wrong version here results in incorrect deprecation warnings or missing new PHP 8.x features in autocompletion.

4. Configuring Xdebug in the container and connecting it to PHPStorm

The Xdebug configuration inside the container must point to the correct listening port used by PHPStorm. By default, PHPStorm listens on port 9003. The xdebug.ini in the PHP container sets xdebug.client_port=9003, xdebug.mode=debug, and either xdebug.start_with_request=yes for always-on debugging or trigger for cookie-based debugging. In PHPStorm, under Settings → PHP → Debug, port 9003 must be entered and Xdebug selected as the debugger extension.

The most common issue: PHPStorm shows "Waiting for incoming connection," but the debugger never connects. The cause is almost always the wrong client_host value in the Xdebug configuration. On Linux, the actual host IP must be entered, or host-gateway (if defined as an extra_host in the compose file). A quick test: docker exec phpfpm php -r "var_dump(ini_get('xdebug.client_host'));" shows the current value directly. The validation dialog under Settings → PHP → Debug → Validate automatically walks through the most common diagnostic steps.


; .docker/phpfpm/xdebug.ini: Xdebug 3 configuration for Docker Compose
[xdebug]
zend_extension=xdebug.so
xdebug.mode=debug,develop
; Port PHPStorm listens on (default 9003 for Xdebug 3)
xdebug.client_port=9003
; On Linux: use host-gateway (mapped in compose extra_hosts)
; On macOS: use host.docker.internal
xdebug.client_host=host-gateway
; trigger = only debug when XDEBUG_TRIGGER cookie/env is set
; yes = always debug (useful during development, off in CI)
xdebug.start_with_request=trigger
xdebug.idekey=PHPSTORM
; Log to diagnose connection issues
xdebug.log=/tmp/xdebug.log
xdebug.log_level=3
; Coverage for PHPUnit
xdebug.mode=debug,develop,coverage

5. Path mappings: syncing local paths and container paths

Path mappings are the link between the local filesystem and the container filesystem. Without a correct mapping, PHPStorm has no way of knowing that /var/www/html/app/code/Mironsoft in the container corresponds to the local file ./src/app/code/Mironsoft. If Xdebug reports a file and PHPStorm doesn't know the mapping, no breakpoint gets set and the IDE just shows an empty file. Path mappings are entered in the interpreter dialog under Path Mappings: the absolute local path on the left, the absolute container path on the right.

With a Mark Shust setup using the volume mount ./src:/var/www/html, the mapping reads: local path /home/user/project/src maps to container path /var/www/html. Important: the local path must be absolute, never a tilde path. PHPStorm stores path mappings in .idea/workspace.xml; this file should not be committed to the repository, since absolute paths are developer-specific. Instead, share the mappings via .idea/php.xml with relative paths, which PHPStorm has supported since version 2023.1.

6. Creating a database data source for the MySQL container

PHPStorm offers a full-fledged database client that communicates directly with the MySQL container. The data source is created under Database → + → Data Source → MySQL. Enter localhost as the host, port 3306 (the port exposed on the host), plus the database name, username, and password from the .env file. PHPStorm automatically downloads the JDBC driver if it isn't already present.

Once the connection is established, the entire database structure appears in the Database panel: all tables, views, stored procedures, and indexes with their types and constraints. SQL files in PHPStorm automatically get code completion for table and column names from this data source. This is especially useful in Magento projects, where the database structure spans hundreds of tables; autocomplete for catalog_product_entity columns saves considerable research time. The data source configuration (without the password) can be shared with the team via .idea/dataSources.xml.

7. Composer and CLI interpreter for run configurations

Composer commands should run inside the PHP container, not on the local system, and that's the core of the containerized workflow. PHPStorm can run Composer directly inside the container: under Settings → PHP → Composer, select the remote interpreter and enter the path to the Composer PHAR inside the container (/usr/local/bin/composer). After that, Tools → Composer → Install runs the command inside the container, shows the result in the IDE output, and automatically refreshes the vendor directory indexing.

Run configurations for PHPUnit, PHPStan, and other CLI tools are also pointed at the remote interpreter. Under Run → Edit Configurations → PHP Script, select the container interpreter, enter the script, and set environment variables. This lets you, for example, run bin/magento cache:flush directly from PHPStorm, see the output in the run panel, and jump straight to the affected code on error, all without a terminal.


<?php
// .idea/php.xml: PHPStorm project settings (commit this, no absolute paths)
// This XML is maintained by PHPStorm; shown here for illustration only.
// Key sections to configure via UI, reflected here:

/*
<component name="PhpProjectSharedConfiguration">
  <option name="phpLanguageLevel" value="8.4" />
</component>

<component name="PhpInterpreters">
  <interpreters>
    <interpreter id="docker-compose-phpfpm"
                 name="Docker Compose: phpfpm"
                 home="docker-compose://[path/to/compose.yaml]:phpfpm/usr/bin/php">
      <path_mappings>
        <mapping local-root="$PROJECT_DIR$/src"
                 remote-root="/var/www/html" />
      </path_mappings>
    </interpreter>
  </interpreters>
</component>
*/

// Verify interpreter connection from PHPStorm Terminal:
// docker exec phpfpm php --version
// docker exec phpfpm php -m | grep xdebug

8. Comparing configuration variants

There are several ways to connect PHPStorm to Docker, each with its own strengths depending on project size and team size. Choosing the right variant affects not only the initial setup time, but also day-to-day working speed and how well the configuration can be rolled out across the team.

Variant Effort Debugging Recommendation
Local PHP interpreter Minimal Doesn't run in the container Only for small non-Docker projects
Docker interpreter (single) Medium Yes, inside the container Good for single-service projects
Docker Compose interpreter High Complete, including networking Best choice for multi-service projects
SSH interpreter (remote VM) Very high Yes, via SSH tunnel For remote dev servers
WSL2 interpreter Medium Yes, inside the WSL environment Windows with Docker Desktop + WSL2

For Magento projects using a Mark Shust Docker setup, the Docker Compose variant is clearly the best choice. It mirrors the actual service network, gives PHPStorm access to all services, and ensures the interpreter uses the same PHP version and extensions as the production environment. The initial setup effort of roughly 30 minutes pays for itself in the very first debugging session.

9. Common problems and how to fix them

The most common problem after setup: Xdebug doesn't connect, even though everything appears to be configured correctly. Checklist: (1) Is the Xdebug listener active in PHPStorm (green telephone icon)? (2) Is port 9003 open in the host system's firewall? On Linux, sudo ufw allow 9003 can help. (3) Is the client_host value in xdebug.ini correct? (4) Is XDEBUG_TRIGGER set as a cookie or environment variable when start_with_request=trigger is active?

Another frequent problem: PHPStorm can't find the breakpoint files because path mappings are missing or wrong. The simplest way to diagnose it: the Xdebug log (/tmp/xdebug.log inside the container) shows exactly which file paths Xdebug is reporting. These container paths must match exactly what's entered in the right-hand column of the path mappings. A tip for Magento: entering just the Magento root (/var/www/html) as a single mapping is enough; PHPStorm derives all subpaths automatically.


<?php
// Diagnostic commands: run in terminal to verify Docker/PHPStorm integration

// 1. Verify Xdebug is loaded in the container
// docker exec phpfpm php -m | grep -i xdebug

// 2. Check Xdebug config values
// docker exec phpfpm php -r "
//   echo 'client_host: ' . ini_get('xdebug.client_host') . PHP_EOL;
//   echo 'client_port: ' . ini_get('xdebug.client_port') . PHP_EOL;
//   echo 'mode: ' . ini_get('xdebug.mode') . PHP_EOL;
//   echo 'start_with_request: ' . ini_get('xdebug.start_with_request') . PHP_EOL;
// "

// 3. Test connection from container to host port 9003
// docker exec phpfpm bash -c 'timeout 2 bash -c "</dev/tcp/host-gateway/9003" && echo OK || echo FAIL'

// 4. Tail Xdebug log inside container
// docker exec phpfpm tail -f /tmp/xdebug.log

// 5. Verify MySQL datasource port is accessible from host
// nc -zv 127.0.0.1 3306 && echo "MySQL port open" || echo "Port closed"

10. Summary

Fully integrating PHPStorm with a Docker Compose setup requires work in four places: Docker daemon configuration inside PHPStorm, a remote interpreter with correct path mappings, Xdebug configuration with the right client_host, and a database data source for the directly exposed MySQL port. Each of these four configurations is independent and must be correct; a single broken piece breaks the entire integration. Building things up systematically from step 1 to step 4 keeps you from debugging in circles.

The long-term payoff is substantial: no more terminal for Composer commands, Xdebug with breakpoints on every line, SQL queries run directly from the IDE with autocomplete against the real database structure, and PHPUnit driven by a single run configuration instead of long command lines. Especially in Magento projects, where the code is spread across dozens of modules and database queries get complex, this is a genuine productivity gain for everyday development.

PHPStorm + Docker Compose: the essentials at a glance

Remote interpreter

Create a Docker Compose interpreter on the phpfpm service, set the PHP language level manually, enter path mappings for the src volume.

Xdebug connection

client_host=host-gateway (Linux) or host.docker.internal (macOS), port 9003, listener active in PHPStorm. Check the Xdebug log when something's wrong.

Database data source

Connect to the MySQL container via the exposed port 3306. The JDBC driver loads automatically. SQL autocomplete against the real table structure.

Composer & CLI

Enter the Composer path inside the container, pick the remote interpreter for run configurations. All CLI tools run in the correct container context.

Mironsoft

PHPStorm setup, Docker integration, and Magento development

Want PHPStorm and Docker Compose fully integrated?

We set up your PHPStorm project completely for Docker Compose, including remote interpreter, Xdebug, database data source, and Composer integration.

Setup review

Review your existing PHPStorm configuration and fill in the missing integrations

Xdebug integration

Set up working debugging with breakpoints inside the container

Team setup

Document and version the IDE configuration for the whole team

11. FAQ: PHPStorm and Docker Compose

1Does Docker Desktop need to be installed?
On macOS and Windows, yes. On Linux, directly via /var/run/docker.sock. Configure the daemon path in PHPStorm under Settings → Build, Execution, Deployment → Docker.
2Why doesn't Xdebug connect?
Usually a wrong client_host. Check the Xdebug log in /tmp/xdebug.log. Open port 9003 in the firewall. Make sure the listener is active in PHPStorm.
3What are path mappings?
The link between a local path and a container path. Without mappings, PHPStorm can't trace Xdebug's file reports back to local files, so breakpoints don't work.
4Share the PHPStorm configuration with the team?
Commit .idea/php.xml and dataSources.xml (without passwords). Add workspace.xml with its absolute paths to .gitignore.
5Set up Composer in the container?
Settings → PHP → Composer: choose the remote interpreter, enter the Composer path /usr/local/bin/composer. PHPStorm then runs all commands inside the container.
6Port 9000 or 9003 for Xdebug?
Xdebug 3 uses 9003 by default. Port 9000 collides with php-fpm. Set it to 9003 in PHPStorm under Settings → PHP → Debug.
7Does the MySQL container need a host port?
Yes. PHPStorm runs on the host, not inside the Docker network. ports: 3306:3306 in the compose file is required so PHPStorm can reach localhost:3306.
8Xdebug and coverage at the same time?
Yes. Set xdebug.mode=debug,coverage. PHPUnit run configurations enable coverage automatically, and reports appear with line-level highlighting in PHPStorm.
9Container not found during setup?
Check the Docker daemon configuration. docker ps must show the container as running. compose.yaml must contain the correct service name.
10How fast is the remote interpreter?
For code completion, PHPStorm uses local stubs, so there's no container overhead. The container only starts for Composer, PHPUnit, and Xdebug. Everyday performance impact is minimal.