{"id":675548,"date":"2026-01-23T17:17:13","date_gmt":"2026-01-23T16:17:13","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=idea&#038;p=675548"},"modified":"2026-01-30T10:32:58","modified_gmt":"2026-01-30T09:32:58","slug":"spring-boot-debugging-now-remote","status":"publish","type":"idea","link":"https:\/\/blog.jetbrains.com\/de\/idea\/2026\/01\/spring-boot-debugging-now-remote","title":{"rendered":"Spring Boot Debugging \u2013 Now Remote"},"content":{"rendered":"\n<p>We released <a href=\"https:\/\/blog.jetbrains.com\/idea\/2025\/06\/demystifying-spring-boot-with-spring-debugger\/\" data-type=\"link\" data-id=\"https:\/\/blog.jetbrains.com\/idea\/2025\/06\/demystifying-spring-boot-with-spring-debugger\/\">Spring Debugger<\/a> in May 2025 to bring more clarity to the \u201cmagic\u201d behind Spring Boot \u2013 helping you see what\u2019s really happening inside your running application. By the end of 2025, the plugin had reached more than 300,000 unique downloads and became a trusted tool within the developer community.<\/p>\n\n\n\n<p>We\u2019ve received a lot of valuable feedback, including feature requests, bug reports, and improvement ideas. Surprisingly, this was the most common question:<\/p>\n\n\n\n<p><em>&#8220;Can I debug remote applications?&#8221;<\/em><\/p>\n\n\n\n<p>In this version, we\u2019re happy to say that yes, you can.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Remote debug with Spring Debugger<\/h2>\n\n\n\n<p>When we started developing Spring Debugger, one of our cornerstone principles was:<\/p>\n\n\n\n<p><em>\u201cWe do not want to use any debug agents.\u201d<\/em><\/p>\n\n\n\n<p>Adding agents to local code often makes setup harder, increases maintenance overhead, and can interfere with the debugging process. We decided to keep this approach for remote debugging as well.<\/p>\n\n\n\n<p>The main challenge in remote debugging was gathering bean information after attaching to the app.<\/p>\n\n\n\n<p>In the local setup, that\u2019s simple \u2013 we set a non-suspending breakpoint in the method that finalizes Spring context initialization and then read all beans from the context. Remotely, however, the application is already running, and the context is fully initialized when we attach.<\/p>\n\n\n\n<p><strong>Container threading model and debugging<\/strong><\/p>\n\n\n\n<p>Our current solution is to suspend one of the servlet-container threads and read the Spring context from that thread. This approach is where our decision to avoid using debug agents is truly tested.<\/p>\n\n\n\n<p>The behavior of Spring Debugger depends on which embedded servlet container your Spring Boot app is using, as each container handles network I\/O and worker threads differently.<\/p>\n\n\n\n<p>With Apache Tomcat, the connector maintains a pool of worker threads as soon as the server starts. Because these threads are available and ready before any HTTP request arrives, the Spring Debugger can hook into one of them immediately after startup and fetch the Spring application context.<\/p>\n\n\n\n<p>By contrast, with Eclipse Jetty or Undertow, the thread model is more layered. For example, Jetty uses separate producer (I\/O) threads and worker (task) threads submitted to an Executor.&nbsp;<\/p>\n\n\n\n<p>Undertow uses I\/O (event\u2010loop) threads for non\u2010blocking network handling and dispatches to worker threads for actual request processing.&nbsp;<\/p>\n\n\n\n<p>Because a fully available worker thread with access to the Spring context can only appear once a request is processed, the debugger cannot load context information until the first incoming connection.<\/p>\n\n\n\n<p>In short, when running on Tomcat, Spring Debugger can present the context immediately. When running on Jetty\/Undertow, there is a slight delay in context loading before the first HTTP request. This is not a bug \u2013 it is a design trade-off linked to our agent-free inspection approach.<\/p>\n\n\n\n<p>You can still debug remote Spring applications with nearly all Spring Debugger features \u2013 the main difference is when the context becomes available for inspection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">How to configure remote debugging<\/h2>\n\n\n\n<p>The process is the same as standard JVM remote debugging \u2013 run your application with an open debug port and connect using the Remote JVM Debug configuration.<\/p>\n\n\n\n<p>For example, in a Docker Compose setup on a remote server:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">http-server:\n depends_on:\n   - postgresql\n image: &#039;jb\/http-server:latest&#039;\n environment:\n   - SPRING_DATASOURCE_URL=jdbc:postgresql:\/\/postgresql:5432\/db\n   - SPRING_DATASOURCE_USERNAME=user\n   - SPRING_DATASOURCE_PASSWORD=secret\n   - JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005\n ports:\n   - &#039;8080:8080&#039;\n   - &#039;5005:5005&#039;<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The key line is:<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\">JAVA_TOOL_OPTIONS=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>This is a well-known option for enabling remote debugging. It opens port 5005 for debugger connections and exposes it from the container.<\/p>\n\n\n\n<p>To start debugging, create a Remote JVM Debug configuration in IntelliJ IDEA and make sure to specify the correct module classpath.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img decoding=\"async\" loading=\"lazy\" width=\"2278\" height=\"1902\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/01\/Remote_JVM_Debug_config.png\" alt=\"\" class=\"wp-image-675550\"\/><\/figure>\n\n\n\n<p>That\u2019s it \u2013 you can now debug the remote application, inspect property values, evaluate bean expressions, and analyze transactions right inside your IDE.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Limitations<\/h2>\n\n\n\n<p>Remote debugging differs slightly from local debugging, but there are a few limitations:<\/p>\n\n\n\n<ul>\n<li>Only embedded containers (Tomcat, Jetty, Undertow) are currently supported.<\/li>\n\n\n\n<li>You need to specify the module classpath in your run configuration.<\/li>\n\n\n\n<li>Database connections are shown, but the database structure view is not available.<\/li>\n<\/ul>\n\n\n\n<p>The last point might be disappointing, but direct access to a remote database is often not technically possible \u2013 it may be inside a Docker network, a separate environment, or behind a firewall. We\u2019re working on finding a solution, but this will take some time.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>The new version of Spring Debugger takes another step toward application troubleshooting by adding remote attach support. Even if you\u2019ve never tried remote debugging before, you\u2019ll find the process almost identical to local debugging \u2013 just open a port and connect from IntelliJ IDEA.<\/p>\n\n\n\n<p>No agents, no actuators, just a standard remote port and a regular debug session.<\/p>\n\n\n\n<p>Simply hit <em>Debug<\/em> and dive into your running application \u2013 inspect beans, properties, transactions, and use extended expression evaluation capabilities.<br><br>We\u2019re exploring optional agent-based extensions for environments where deeper introspection is worth the trade-off, and we\u2019d love to hear your feedback.<\/p>\n\n\n\n<p>Happy coding!<\/p>\n","protected":false},"author":1511,"featured_media":675573,"comment_status":"closed","ping_status":"closed","template":"","categories":[89],"tags":[3530,1038,8826],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/idea\/675548"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/idea"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/types\/idea"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/users\/1511"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/comments?post=675548"}],"version-history":[{"count":4,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/idea\/675548\/revisions"}],"predecessor-version":[{"id":677550,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/idea\/675548\/revisions\/677550"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/media\/675573"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/media?parent=675548"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/categories?post=675548"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/tags?post=675548"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/de\/wp-json\/wp\/v2\/cross-post-tag?post=675548"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}