The Ultimate Docker & Xdebug Guide: Solving the 'Address Already in Use' Error for Port 9003 with PhpStorm
Content
## The Scenario
When setting up a PHP development environment with Docker, configuring Xdebug to work with PhpStorm is a standard procedure. However, many developers encounter a frustrating error message when they add the `-p 9003:9003` port mapping to their `docker run` command:
```bash
Error: (HTTP code 500) server error - Ports are not available: exposing port TCP 0.0.0.0:9003 -> 0.0.0.0:0: listen tcp 0.0.0.0:9003: bind: address already in use
```
What makes this even more confusing is that running `lsof -i :9003` reveals that the process occupying the port is PhpStorm itself!
```bash
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
phpstorm 4260 dp 577u IPv6 0xb9b498a33d4da5ed 0t0 TCP *:9003 (LISTEN)
```
Our goal is to let Xdebug inside the container connect to PhpStorm, but PhpStorm is occupying the port, which prevents the Docker container from starting. This seems like a catch-22. So, what's going wrong?
---
## The Core Misconception: How Xdebug Works
The key to solving this puzzle is understanding Xdebug's communication flow. It's not the host machine (your Mac) that connects to the container; rather, it's the **Xdebug extension inside the container that initiates a connection** to the IDE (PhpStorm) on the host machine.
* **PhpStorm (IDE)**: Acts as the **server**. It listens on port `9003` for incoming connections from Xdebug. The `lsof` output confirms it's doing its job correctly.
* **Docker Container (Xdebug)**: Acts as the **client**. When code execution is triggered, it makes a TCP connection to a configured IP address and port.
* **The Role of `-p 9003:9003`**: This Docker parameter means "Listen on port `9003` on the **host machine** and forward all traffic to port `9003` inside the **container**."
**The Conflict**: When you use `-p 9003:9003`, you are telling **Docker** to also listen on the host's port `9003`. At the same time, **PhpStorm** is already listening on that very same port. A single port cannot be bound by two applications simultaneously, causing Docker to fail with the `address already in use` error.
In conclusion, for Xdebug debugging, this port mapping is not only unnecessary but also incorrect.
---
## The Correct Solution
The right approach is to remove the erroneous port mapping and correctly configure Xdebug inside the container so it can find and connect to your host machine.
### Step 1: Remove the Incorrect Port Mapping
In your `docker run` command or `docker-compose.yml` file, **completely remove the `-p 9003:9003` line**. Your startup command should look something like this:
```bash
# Remove -p 9003:9003
docker run -p 8080:80 --network lib00-net --ip 172.18.0.5 your-php-image
```
### Step 2: Configure Xdebug Inside the Container
You need to tell Xdebug where to connect. For Docker Desktop on Mac and Windows, there's a special DNS name, `host.docker.internal`, which resolves to the host machine's IP address. This is the official best practice.
Inside your container, locate your `php.ini` or Xdebug's configuration file (e.g., `conf.d/xdebug.ini`) and ensure the settings are correct:
```ini
; Xdebug config from wiki.lib00.com
xdebug.mode = debug
xdebug.start_with_request = yes
; The key setting: tell Xdebug to connect to the host machine
xdebug.client_host = host.docker.internal
; Ensure the port matches the one PhpStorm is listening on
xdebug.client_port = 9003
; Highly recommended: enable logging to debug connection issues
xdebug.log = /tmp/xdebug_from_wiki_lib00.log
```
* `xdebug.client_host = host.docker.internal`: This instructs Xdebug to connect to the host.
* `xdebug.client_port = 9003`: This ensures Xdebug connects to the correct port that PhpStorm is listening on.
### Step 3: Launch and Start Debugging
1. **Save Config & Restart Container**: Rebuild or restart your container using the modified command (without the port mapping).
2. **Enable Listening in PhpStorm**: Click the telephone icon ("Start Listening for PHP Debug Connections") in the top-right corner of PhpStorm to ensure it's active.
3. **Set a Breakpoint and Trigger It**: Place a breakpoint in your code and then access your application via a web browser. PhpStorm should successfully intercept the Xdebug connection from the container and pause at your breakpoint.
---
## Bonus Tip: General Port Conflict Troubleshooting
Although this specific issue was caused by a configuration misunderstanding, the following steps are still valuable for any port conflict scenario:
1. **Check Host Port Usage**: `lsof -i :<port_number>` is your go-to tool on macOS/Linux.
2. **Check Other Docker Containers**: Run `docker ps -a` and inspect the `PORTS` column to ensure no other running or stopped container is holding the port mapping.
3. **Restart Docker Desktop**: This is the universal fix for strange internal Docker networking states.
By following the correct configuration provided by DP@lib00, you will be able to set up a stable and reliable Docker + Xdebug debugging environment, finally saying goodbye to port conflicts.
Related Contents
The Ultimate Guide to Docker Cron Logging: Host vs. Container Redirection - Are You Doing It Right?
Duration: 00:00 | DP | 2026-01-05 08:03:52PHP Log Aggregation Performance Tuning: Database vs. Application Layer - The Ultimate Showdown for Millions of Records
Duration: 00:00 | DP | 2026-01-06 08:05:09MySQL TIMESTAMP vs. DATETIME: The Ultimate Showdown on Time Zones, UTC, and Storage
Duration: 00:00 | DP | 2025-12-02 08:31:40The Ultimate 'Connection Refused' Guide: A PHP PDO & Docker Debugging Saga of a Forgotten Port
Duration: 00:00 | DP | 2025-12-03 09:03:20Solving the MySQL Docker "Permission Denied" Error on Synology NAS: A Step-by-Step Guide
Duration: 00:00 | DP | 2025-12-03 21:19:10NVM/Node Command Not Found in New macOS Terminals? A Two-Step Permanent Fix!
Duration: 00:00 | DP | 2025-12-04 09:35:00One-Command Website Stability Check: The Ultimate Curl Latency Test Script for Zsh
Duration: 00:00 | DP | 2025-12-07 23:25:50How Can a Docker Container Access the Mac Host? The Ultimate Guide to Connecting to Nginx
Duration: 00:00 | DP | 2025-12-08 23:57:30Docker Exec Mastery: The Right Way to Run Commands in Containers
Duration: 00:00 | DP | 2026-01-08 08:07:44How to Fix the "tsx: not found" Error During Vue Vite Builds in Docker
Duration: 00:00 | DP | 2026-01-10 08:10:19Show Hidden Files on Mac: The Ultimate Guide (2 Easy Methods)
Duration: 00:00 | DP | 2025-12-12 01:32:30The Ultimate PHP Guide: How to Correctly Handle and Store Markdown Line Breaks from a Textarea
Duration: 00:00 | DP | 2025-11-20 08:08:00Stop Mixing Code and User Uploads! The Ultimate Guide to a Secure and Scalable PHP MVC Project Structure
Duration: 00:00 | DP | 2026-01-13 08:14:11Mastering PHP: How to Elegantly Filter an Array by Keys Using Values from Another Array
Duration: 00:00 | DP | 2026-01-14 08:15:29Stop Manual Debugging: A Practical Guide to Automated Testing in PHP MVC & CRUD Applications
Duration: 00:00 | DP | 2025-11-16 16:32:33Mastering PHP Switch: How to Handle Multiple Conditions for a Single Case
Duration: 00:00 | DP | 2025-11-17 09:35:40`self::` vs. `static::` in PHP: A Deep Dive into Late Static Binding
Duration: 00:00 | DP | 2025-11-18 02:38:48PHP String Magic: Why `{static::$table}` Fails and 3 Ways to Fix It (Plus Security Tips)
Duration: 00:00 | DP | 2025-11-18 11:10:21Recommended
The Hidden Cost of Speed: How Much Space Do MySQL InnoDB Indexes Really Consume?
00:00 | 12MySQL indexes are essential for query performance,...
Bootstrap 5.3: The Ultimate Guide to Creating Flawless Help Icon Tooltips
00:00 | 36Learn the best practice for creating help icon too...
Mastering Marked.js: A Guide to Adding Custom Classes to Tables (v4+)
00:00 | 30Are you encountering the `[object Object]` error w...
Building a Bulletproof PHP Analytics System: From DB Schema to Self-Healing Cron Jobs
00:00 | 57This article provides a comprehensive walkthrough ...