Every time I restart my Spring Boot service during development, I repeat the same ritual:

  1. Wait for startup to finish.
  2. Scroll the console to find the Swagger URL.
  3. Copy it.
  4. Paste it into the browser.

A hundred times a day. So I automated it.

In this post I'll show a small CommandLineRunner bean that auto-opens Swagger UI in your default browser, works identically on Linux, Windows, and macOS, and fails gracefully on headless servers (Docker, CI, remote VMs).

The whole thing is ~60 lines of pure JDK code. No extra dependencies.

What it does

  • Detects the OS at runtime via System.getProperty("os.name")
  • Uses xdg-open on Linux, open on macOS, and cmd /c start on Windows
  • Falls back to java.awt.Desktop#browse if the CLI tool is missing
  • Silently skips on headless machines (no crashes)
  • Toggleable via application.yml — set openProject.isOpen: false in prod
  • Prints a clickable link in the console for good measure

1. Configuration

Add a small block to application.yml:

openProject: isOpen: true web: openUrl: "http://localhost:8080/swagger-ui/index.html" 

2. The BrowserLauncher bean

package com.gama.swagger.config; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; import java.awt.Desktop; import java.net.URI; import java.util.Locale; @Component public class BrowserLauncher implements CommandLineRunner { private static final Logger log = LoggerFactory.getLogger(BrowserLauncher.class); @Value("${openProject.isOpen:true}") private boolean isOpen; @Value("${openProject.web.openUrl}") private String openUrl; @Override public void run(String... args) { if (!isOpen) { log.info("Auto-open browser disabled (openProject.isOpen=false)."); return; } String os = System.getProperty("os.name", "").toLowerCase(Locale.ROOT); log.info("Detected OS: {}", os); try { if (os.contains("win")) { // Windows: cmd /c start "" <url> (the empty "" is the window title) Runtime.getRuntime().exec(new String[]{"cmd", "/c", "start", "", openUrl}); } else if (os.contains("mac")) { Runtime.getRuntime().exec(new String[]{"open", openUrl}); } else if (os.contains("nux") || os.contains("nix") || os.contains("aix")) { if (isCommandAvailable("xdg-open")) { Runtime.getRuntime().exec(new String[]{"xdg-open", openUrl}); } else { log.warn("xdg-open not found. Install it: sudo apt install xdg-utils"); fallbackDesktopBrowse(); } } else { log.warn("Unrecognized OS '{}' — trying Desktop.browse fallback", os); fallbackDesktopBrowse(); } log.info("Attempted to open browser: {}", openUrl); } catch (Exception e) { log.warn("Could not auto-open browser: {}", e.getMessage()); } } /** Last-resort attempt using the Java Desktop API (works when a GUI is present). */ private void fallbackDesktopBrowse() { try { if (Desktop.isDesktopSupported() && Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) { Desktop.getDesktop().browse(new URI(openUrl)); log.info("Opened via Desktop.browse fallback."); } else { log.warn("No GUI available — please open manually: {}", openUrl); } } catch (Exception ex) { log.warn("Desktop.browse fallback failed: {}", ex.getMessage()); } } private boolean isCommandAvailable(String cmd) { try { Process p = new ProcessBuilder("which", cmd) .redirectErrorStream(true) .start(); return p.waitFor() == 0; } catch (Exception e) { return false; } } } 

3. Print a clickable link in the console

Most modern terminals (GNOME Terminal, Konsole, iTerm2, Windows Terminal, IntelliJ's console, VS Code's terminal) auto-detect URLs. Just log the full URL from main:

@SpringBootApplication public class SwaggerApplication { private static final Logger log = LoggerFactory.getLogger(SwaggerApplication.class); public static void main(String[] args) { var context = SpringApplication.run(SwaggerApplication.class, args); String port = context.getEnvironment().getProperty("server.port", "8080"); String ctx = context.getEnvironment().getProperty("server.servlet.context-path", ""); log.info("Swagger UI: http://localhost:{}{}/swagger-ui/index.html", port, ctx); } }