docker-compose.yml. Ensuring -agentlib:jdwp uses 0.0.0.0 and mapping port 5005 explicitly allows your IDE to attach successfully.
Understanding the JDWP Configuration
The Java Debug Wire Protocol (JDWP) must be configured correctly within the running container for remote debugging to function. By default, many development environments bind the debugger listener to127.0.0.1, which restricts access to only processes running inside the same container namespace. When you attempt to connect from your host machine’s IDE, the connection is refused because the port is not reachable from outside the container.
To fix this, you need to ensure that the JVM listens on all available network interfaces. This is achieved by adding specific arguments to the JVM options passed to the Gradle build or the runtime command. The critical flag is -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005. Notice the use of *:5005 instead of just 5005 or localhost:5005. The asterisk tells the JVM to bind to 0.0.0.0, making the debug port accessible from any IP address, including the host machine.
If you are using Spring Boot, the configuration might look slightly different, often involving environment variables like DEBUG_PORT. However, for standard Gradle applications, modifying the jvmArgs in your build.gradle file or the Dockerfile ENTRYPOINT is the standard approach. Ensure that the suspend parameter is set to n if you want the application to start immediately without waiting for the debugger to attach, which is crucial for testing startup issues.
Verifying Docker Compose Port Mappings
Even with correct JVM arguments, the container will not expose the debug port to the host unless it is explicitly mapped in yourdocker-compose.yml file. A frequent mistake is exposing the application port (e.g., 8080) but forgetting to map the debug port (5005). You must add a separate entry in the ports section of your service definition.
Here is an example of how the configuration should look:
services:
app:
image: my-gradle-app:latest
ports:
- "8080:8080"
- "5005:5005"
environment:
- DEBUG=true
The format host_port:container_port ensures that traffic directed to port 5005 on your local machine is forwarded to port 5005 inside the container. If you omit the second line, your IDE will fail to connect regardless of how well the JVM is configured. Additionally, verify that no firewall rules on your host machine are blocking incoming connections on port 5005, although this is less common in local development environments.
For more complex setups involving multiple services, such as those described in our guide on Microservice Health Check Instance Docker, ensure that the container networking mode allows inter-container communication if the debugger is attached from another container rather than the host.
Checking Network Connectivity and Hosts File
Sometimes the issue lies not in the configuration but in how the container resolves its own hostname. If the JVM binds to the container’s hostname instead of0.0.0.0, external connections may fail. You can verify this by executing a shell inside the running container and checking the listening ports.
Use the following command to inspect open ports:
docker exec -it <container_id> netstat -tlnp | grep 5005
Or, if netstat is unavailable, try:
docker exec -it <container_id> ss -tlnp | grep 5005
You should see the port listed as 0.0.0.0:5005 or *:5005. If it shows 127.0.0.1:5005, the JVM configuration is still incorrect. Also, check the /etc/hosts file inside the container to ensure there are no conflicting entries that might redirect localhost traffic unexpectedly. In some Docker Desktop configurations, changing the network_mode to host can bypass NAT issues, but this is generally discouraged for multi-service architectures due to port conflicts.
If you encounter issues where the debugger attaches but stops working after a few minutes, consider checking for idle timeouts in your IDE settings. Our article on How to always force 'Run Without Debugging' to Output to the Debug Console? touches on IDE behavior that might affect session stability, though it focuses on output redirection rather than connection drops.
Gradle Build Script Adjustments
To make debugging easier during development, you can create a custom Gradle task that launches the application with debug flags enabled. This avoids manually editing environment variables every time you restart the container. Define a new task in yourbuild.gradle file:
task runWithDebug(type: JavaExec) {
group = "application"
mainClass = "com.example.Application"
jvmArgs = [
"-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005",
"-Dspring.profiles.active=dev"
]
}
Then, modify your Dockerfile to use this task or pass these arguments via environment variables that the application reads at startup. This ensures consistency between local runs and containerized runs. Remember to exclude these debug flags from production builds to avoid performance overhead and security risks.
Frequently Asked Questions
Why does the debugger disconnect immediately after attaching?
This usually happens if the JVM is configured with suspend=y and the application crashes before the debugger connects. Set suspend=n to allow the app to start independently. Additionally, check for unhandled exceptions in the logs that might terminate the process instantly.
Can I use a different port for debugging instead of 5005?
Yes, you can change the port by modifying the address parameter in the -agentlib:jdwp flag, such as address=*:9009. Ensure you also update the port mapping in docker-compose.yml to match the new port number on both the host and container sides.
How do I debug a Gradle wrapper inside Docker?
You cannot directly debug the Gradle wrapper execution itself through the JVM debug agent easily. Instead, focus on debugging the application code launched by Gradle. Ensure the application process, not the Gradle daemon, has the debug flags enabled in its JVM arguments.
What if the container is behind a reverse proxy?
Reverse proxies typically handle HTTP traffic, not raw socket connections used by JDWP. You must bypass the proxy for the debug port or configure the proxy to support TCP forwarding. Directly mapping the debug port in Docker Compose is the recommended approach for local debugging.
Does Docker Desktop on Windows handle port 5005 differently?
Windows Docker Desktop uses a Linux VM internally, so port mappings work similarly to Linux. However, ensure that Windows Firewall is not blocking inbound connections on port 5005. You may need to create an inbound rule allowing traffic on this port for public and private networks.