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
macOS RAM Disk Deep Dive: Is Memory Allocation Dynamic or Fixed?
Duration: 00:00 | DP | 2026-07-09 20:02:36Resolving PHP "could not find driver" Error: Ultimate Guide to Missing PDO Database Drivers
Duration: 00:00 | DP | 2026-07-04 08:03:00VS Code PHP Guide: How to Trace Function Definitions Like PHPStorm
Duration: 00:00 | DP | 2026-07-04 20:27:00Declutter Your Desktop: How to Change macOS Screenshot Save Location via Command Line
Duration: 00:00 | DP | 2026-07-10 08:05:12How to Fix "Permission denied" Error When Running Shell Scripts in Mac/Linux
Duration: 00:00 | DP | 2026-07-06 08:54:30macOS Advanced Guide: How to Elegantly Set Programs to Run at Startup
Duration: 00:00 | DP | 2026-07-07 09:20:15Resolving Nginx Permission Denied (13) Errors for WebP Images Generated by PHP Imagick
Duration: 00:00 | DP | 2026-07-05 21:17:00Boost Mac Productivity: How to Set F1-F12 as Standard Function Keys on macOS
Duration: 00:00 | DP | 2026-07-12 08:15:37Bypassing macOS Restrictions: How to Set a 1-Digit Login Password
Duration: 00:00 | DP | 2026-07-13 08:20:49How to Fix Mac mini Not Receiving SMS Messages and iCloud Sync Stuck
Duration: 00:00 | DP | 2026-07-06 22:07:00Complete Guide to Installing and Configuring Git Server on Synology NAS: Basic to Advanced
Duration: 00:00 | DP | 2026-07-16 20:39:02Why Do .smbdelete Hidden Files Appear After Deleting on Mac SMB Shares? Causes & Ultimate Solutions
Duration: 00:00 | DP | 2026-06-27 19:10:00Complete Guide to Setting Docker Container Timezone to UTC+8 (Asia/Shanghai)
Duration: 00:00 | DP | 2026-06-30 20:43:30Fixing Nginx 500 Error: Internal Redirection Cycle (SPA vs PHP Config)
Duration: 00:00 | DP | 2026-07-02 21:45:50Stop Making Timezone Mistakes in PHP: The Ultimate Guide to time() and UTC
Duration: 00:00 | DP | 2026-06-25 11:29:00Beyond 99.9%: A Deep Dive into a User-Centric Weighted Sampling Algorithm for Availability
Duration: 00:00 | DP | 2026-06-26 12:57:00The 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:09Recommended
From Zero to Platform: Build Your Own GitHub-Level Login System with NextAuth and Casdoor
00:00 | 86Many developers are puzzled by the complexity of m...
Demystifying AI API Pricing: Understanding Prompt, Completion, and Cache Token Costs
00:00 | 7When integrating LLM APIs, developers are often co...
Vue i18n Pitfall Guide: How to Fix the "Invalid Linked Format" Compilation Error Caused by Email Addresses?
00:00 | 128Encountering an "Invalid linked format" compilatio...
MySQL PV Log Table Optimization: A Deep Dive into Slashing Storage Costs by 73%
00:00 | 114How do you design a high-performance, cost-effecti...