Skip to content

Fix: Spring Boot Web server failed to start. Port 8080 was already in use

FixDevs · (Updated: )

Part of:  Java & JVM Errors

Quick Answer

How to fix the Spring Boot error 'Web server failed to start. Port 8080 was already in use' by killing blocking processes, changing ports, fixing IDE issues, and resolving Docker conflicts.

The Error

You start your Spring Boot application and it immediately crashes with:

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 8080 was already in use.

Action:

Identify and stop the process that's listening on port 8080 or configure this application to listen on another port.

Or the Tomcat variant:

java.net.BindException: Address already in use
org.apache.catalina.LifecycleException: Protocol handler start failed
Caused by: java.net.BindException: Address already in use: bind

The message is straightforward: another process already occupies port 8080, and the embedded server (Tomcat, Jetty, or Undertow) cannot bind to it. Only one process can listen on a given port at a time.

Why This Happens

Spring Boot’s embedded web server tries to bind to port 8080 by default when the application starts. If anything else is already listening on that port, the JVM throws a BindException and Spring translates it into the “port already in use” startup failure.

Common causes:

  • A previous instance of your application is still running. You hit “Run” again in your IDE without stopping the first one, or a background java -jar process is still alive.
  • Another service uses port 8080. Apache Tomcat (standalone), Jenkins, Nexus, WildFly, and many other tools default to 8080.
  • A Docker container has port 8080 mapped. Even if the container runs a completely different service, the host-side port binding blocks your Spring app. If you run into Docker port issues frequently, check out the full guide on Docker port conflicts.
  • Your IDE did not terminate the previous run cleanly. IntelliJ IDEA and Eclipse sometimes leave zombie Java processes behind, especially after a crash or force-stop.
  • A system service or daemon occupies the port. On some Linux distributions, system services like httpd or custom daemons bind to 8080.

There is also a class of failure that only appears after a Spring Boot major upgrade. Several releases between 2.6 and 3.x changed startup ordering, actuator port handling, and the Tomcat version bundled by default. A project that worked silently on 2.5 can begin failing on 3.x because shutdown is now stricter, the actuator runs on a separate port by convention, or a circular bean reference now aborts startup before the previous port is released.

Version History That Changes the Failure Mode

The “port already in use” message itself has been part of Spring Boot since the framework’s earliest releases, but several version bumps changed how startup, shutdown, and the actuator interact with port binding. If you are migrating across major versions, expect the failure pattern to shift.

  • Spring Boot 2.6 (Nov 2021) flipped the default to spring.main.allow-circular-references=false. A circular bean reference that used to start up silently now aborts before the embedded server fully releases the previous port, which can leave port 8080 in a transient TIME_WAIT state and reproduce the “already in use” error on the very next restart.
  • Spring Boot 2.7 (May 2022) changed actuator endpoint defaults and the path of several management endpoints. If your management.server.port was set in a bootstrap.yml that 2.7 deprecated, the actuator may now bind to the main server.port, fighting your application for 8080.
  • Spring Boot 3.0 (Nov 2022) required Java 17 and migrated to Jakarta EE (the javax.* to jakarta.* rename). Embedded Tomcat jumped to 10.x. Old Tomcat connector configuration written for 9.x can still load on 10 but with subtly different defaults around bindOnInit and SO_REUSEADDR.
  • Spring Boot 3.1 (May 2023) bundled Spring Authorization Server. If you added it without picking a unique port for the auth server, it now competes with your main app.
  • Spring Boot 3.2 (Nov 2023) added official virtual threads support (spring.threads.virtual.enabled=true). Virtual threads do not change port binding, but they do change shutdown timing — a graceful shutdown that previously took 1 s can now overlap with the next start.
  • Spring Boot 3.3 (May 2024) added SBOM publishing and tightened observability defaults. Nothing here changes port binding directly, but the longer startup banner (with build metadata) can mask a “port in use” warning if your IDE truncates console output.
  • Spring Boot 3.4 / 3.5+ (late 2024 / 2025+) continued the Jakarta migration cleanup and refined the embedded server starters. The 3.4 release stream also stabilized structured logging, which makes the “port already in use” message machine-readable when you pipe logs to JSON.

If your error pattern differs from what older articles describe, check mvn -v (or ./mvnw -v) and your pom.xml spring-boot.version first.

Fix 1: Find and Kill the Process Using Port 8080

The fastest solution is to find whatever is holding port 8080 and kill it.

Linux and macOS

Find the process:

lsof -i :8080

You get output like:

COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
java    12345  devuser  48u  IPv6 0x1234  0t0  TCP *:http-alt (LISTEN)

Kill it by PID:

kill 12345

If it does not stop, force-kill it:

kill -9 12345

One-liner to find and kill in a single command:

lsof -ti :8080 | xargs kill -9

You can also use fuser on Linux:

fuser -k 8080/tcp

Windows

Find the process:

netstat -ano | findstr :8080

Sample output:

TCP    0.0.0.0:8080    0.0.0.0:0    LISTENING    12345

The last column is the PID. Kill it:

taskkill /PID 12345 /F

Or use PowerShell for a cleaner approach:

Get-NetTCPConnection -LocalPort 8080 | Select-Object OwningProcess
Stop-Process -Id 12345 -Force

After killing the process, restart your Spring Boot application. The port should now be free.

This approach is nearly identical to how you would fix a port 3000 conflict in Node.js — the underlying OS-level concept is the same regardless of the framework.

Fix 2: Change the Application Port

If you cannot free port 8080 (or simply prefer a different port), tell Spring Boot to use another one.

Using application.properties

Open src/main/resources/application.properties and add:

server.port=8081

Using application.yml

If you use YAML configuration:

server:
  port: 8081

Using a Command-Line Argument

Override the port at startup without changing any files:

java -jar myapp.jar --server.port=8081

Using an Environment Variable

Set the port through the SERVER_PORT environment variable:

SERVER_PORT=8081 java -jar myapp.jar

On Windows:

set SERVER_PORT=8081
java -jar myapp.jar

Pick any port between 1024 and 65535 that is not already in use. Ports below 1024 are privileged and require root/admin access on most systems.

Pro Tip: Use a port number that is easy to remember and unlikely to conflict. Many teams adopt a convention like 8081 for service A, 8082 for service B, etc. Document your port assignments in a shared wiki or README so the entire team stays consistent.

Fix 3: Use a Random Port (Port 0) for Testing

If you are running integration tests or do not care which port the application uses, set the port to 0. Spring Boot will pick a random available port:

server.port=0

This is especially useful in @SpringBootTest:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyApplicationTests {

    @LocalServerPort
    private int port;

    @Test
    void contextLoads() {
        // port is the randomly assigned port
        System.out.println("Running on port: " + port);
    }
}

Using RANDOM_PORT eliminates port conflicts entirely during test runs, which matters on CI/CD servers where multiple builds may execute in parallel.

Fix 4: Fix Duplicate Spring Boot Instances

Sometimes you accidentally launch multiple instances of the same application. This is the most common cause of the error when you are certain nothing else should be on port 8080.

Check for Running Java Processes

On Linux/macOS:

ps aux | grep java

On Windows:

tasklist | findstr java

You may see two or more java processes running your application. Kill the old ones and keep only the one you intend to use.

Gradle and Maven Daemon Issues

If you use ./gradlew bootRun or mvn spring-boot:run, the build tool may keep a daemon process alive even after you press Ctrl+C.

Stop Gradle daemons:

./gradlew --stop

Check for leftover Maven processes manually and kill them using the methods in Fix 1.

Spring Boot DevTools Hot Reload

If you use spring-boot-devtools, the application restarts automatically on code changes. In rare cases, the old process does not terminate before the new one starts, causing a brief port conflict. Adding a small shutdown delay can help:

spring.devtools.restart.poll-interval=2000
spring.devtools.restart.quiet-period=1000

If DevTools restarts consistently fail, check your application’s shutdown hooks and bean destruction order. Misconfigured beans that hang during shutdown can prevent timely port release. A bean that fails to destroy properly can block a clean restart and leave the port held until the JVM is force-terminated.

Fix 5: Fix Embedded Server Configuration

Spring Boot auto-configures an embedded Tomcat, Jetty, or Undertow server. Misconfiguration can cause port binding failures beyond a simple conflict.

Multiple Connector Definitions

If you define custom ServerConnector or Connector beans, you might accidentally create a second listener on port 8080:

@Bean
public ServletWebServerFactory serverFactory() {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    factory.setPort(8080); // This might conflict with server.port
    return factory;
}

The server.port property and a programmatic setPort() call can clash. Remove the programmatic setting and rely on server.port alone, or remove the property and control the port entirely in code.

Actuator on the Same Port

Spring Boot Actuator endpoints run on the same port as the main application by default. If you configure management.server.port to a value that conflicts with another process, you get the same bind error — but for the management server:

management.server.port=8081

Make sure management.server.port does not collide with server.port or any other occupied port.

SSL and Non-SSL on the Same Port

Attempting to configure both HTTP and HTTPS on port 8080 causes a bind conflict with itself. Use separate ports:

server.port=8443
server.ssl.enabled=true
server.ssl.key-store=classpath:keystore.p12

Then redirect HTTP traffic from a different port (e.g., 8080) if needed.

Fix 6: Fix Docker Port Conflicts

When you run your Spring Boot app locally but Docker containers map to port 8080, you get the same error.

Check for containers using port 8080:

docker ps --format "table {{.Names}}\t{{.Ports}}"

Look for entries like 0.0.0.0:8080->8080/tcp. Stop the conflicting container:

docker stop <container-name>

Or change the host port mapping in your docker-compose.yml:

services:
  myservice:
    ports:
      - "9090:8080"  # Map host port 9090 to container port 8080

If you frequently deal with Docker networking issues, walk through the host port mappings carefully — Compose, Swarm, and stale container cleanup are common sources of the same conflict.

Common Mistake: Running docker-compose up in one terminal and then launching your Spring Boot app outside Docker on the same port. If your docker-compose.yml maps port 8080 to the host, your local Spring app cannot also bind to 8080. Either stop the container, change the Docker port mapping, or change server.port in your Spring configuration.

Fix 7: Use Profiles for Per-Environment Ports

Hardcoding a single port works for one environment, but causes conflicts when you run multiple services or deploy to different environments. Spring profiles solve this cleanly.

Create environment-specific configuration files:

application-dev.properties:

server.port=8080

application-staging.properties:

server.port=8081

application-prod.properties:

server.port=80

Activate a profile at startup:

java -jar myapp.jar --spring.profiles.active=dev

Or set it as an environment variable:

SPRING_PROFILES_ACTIVE=dev java -jar myapp.jar

In your main application.properties, you can set a default profile:

spring.profiles.active=dev

This approach prevents port conflicts across environments and makes your configuration explicit. Each developer on the team can use their own profile with a unique port if needed.

For YAML users, you can combine all profiles in a single application.yml:

spring:
  profiles:
    active: dev

---
spring:
  config:
    activate:
      on-profile: dev
server:
  port: 8080

---
spring:
  config:
    activate:
      on-profile: staging
server:
  port: 8081

---
spring:
  config:
    activate:
      on-profile: prod
server:
  port: 80

IDEs are one of the most common sources of “port already in use” errors during development. IntelliJ IDEA and Eclipse both have quirks that leave old processes running.

IntelliJ IDEA

Problem: You click the green “Run” button, but the previous run was never stopped. IntelliJ may or may not terminate the old process depending on your run configuration settings.

Fix: Open the Run tool window (usually at the bottom). Check if a previous instance is still listed as running. Click the red stop button to terminate it, then restart.

To prevent this from happening:

  1. Go to Run > Edit Configurations.
  2. In Before launch, check if Single instance only is enabled. If not, enable it. This ensures IntelliJ stops the old instance before starting a new one.

Alternatively, check Modify options > Allow multiple instances and make sure it is unchecked if you only want one instance running at a time.

Force-kill from IntelliJ: If the stop button does not work, open the terminal inside IntelliJ and use the OS-level kill commands from Fix 1.

Eclipse / Spring Tool Suite (STS)

Problem: Eclipse sometimes fails to terminate a Spring Boot app cleanly, especially when you use the “Relaunch” button.

Fix:

  1. Open the Console view.
  2. Click the Display Selected Console button (the monitor icon) to see all active consoles.
  3. Find the running Spring Boot console and click the red Terminate button.

If the process is stuck:

  1. Open Window > Show View > Debug.
  2. Right-click the lingering process and select Terminate and Remove.

VS Code with Spring Boot Extension

In VS Code, the Spring Boot Dashboard shows running applications. Stop them from the dashboard before restarting. If the dashboard does not show the process, fall back to terminal-based kill commands.

General IDE Tip

If your IDE consistently leaves zombie processes, add a shutdown hook to your application that logs the port being released. This helps you verify clean shutdowns:

@PreDestroy
public void onShutdown() {
    log.info("Application shutting down, releasing port");
}

When your application fails to start due to port conflicts and you also see errors about missing classes, check the ClassNotFoundException troubleshooting guide to rule out classpath issues that might cause partial startup failures.

Still Not Working?

If none of the fixes above resolved the error, check these less common causes:

Port Forwarding or VPN Conflicts

VPN software and SSH port forwarding can silently occupy ports. If you use SSH tunnels:

ssh -L 8080:remote-host:80 user@server

This binds local port 8080 to forward traffic. Close the SSH session or change the local port in your tunnel configuration.

Check for VPN-related bindings:

lsof -i :8080 | grep -i vpn

Firewall Blocking Port Release

On some systems, firewall rules or security software can hold ports in a TIME_WAIT state longer than expected. Check the port state:

netstat -an | grep 8080

If you see TIME_WAIT, the OS is holding the port for a short period after the previous process closed it. Wait 30-60 seconds and try again. To reuse the port immediately, enable SO_REUSEADDR in your server configuration:

server.tomcat.reuse-address=true

Cloud Deployment Port Configuration

Cloud platforms override or ignore server.port. Each platform has its own convention:

  • Heroku: Uses the PORT environment variable. Set server.port=${PORT:8080} in your properties.
  • AWS Elastic Beanstalk: Expects port 5000 by default. Set server.port=5000 or configure the load balancer.
  • Google Cloud Run: Injects PORT as an environment variable. Use server.port=${PORT:8080}.
  • Azure App Service: Uses port 80 or 8080 depending on the plan. Check the platform documentation.

If your app starts fine locally but fails in the cloud, the port configuration is almost certainly the issue. Cloud platforms often route traffic through a reverse proxy, so the port your app binds to must match what the platform expects.

For Spring Boot apps that start but show a blank error page instead, that is a different issue covered in the Whitelabel Error Page guide.

Ephemeral Port Exhaustion

On heavily loaded systems running many microservices, you may run out of ephemeral ports entirely. Check your system’s available port range:

cat /proc/sys/net/ipv4/ip_local_port_range

If the range is too narrow, expand it:

sudo sysctl -w net.ipv4.ip_local_port_range="1024 65535"

Check for Multiple Network Interfaces

If your machine has multiple network interfaces (common with Docker, VMs, or VPNs), Spring Boot binds to 0.0.0.0 by default (all interfaces). You can restrict it to a specific interface:

server.address=127.0.0.1

This binds only to localhost, which may avoid conflicts with services bound to a specific external IP on the same port.

macOS AirPlay Receiver on Port 5000

If you changed server.port to 5000 to avoid 8080 and now see the same error on macOS, the culprit is the system AirPlay Receiver. macOS Monterey and later enable AirPlay on port 5000 by default. Disable it under System Settings > General > AirDrop & Handoff > AirPlay Receiver and the port frees up.

Graceful Shutdown Timeout Set Too High

Spring Boot’s graceful shutdown (server.shutdown=graceful) waits up to spring.lifecycle.timeout-per-shutdown-phase (default 30 s) for in-flight requests. If a long-polling endpoint hangs, the JVM holds the port for the full timeout. When DevTools tries to restart inside that window, you get the conflict error. Lower the timeout for development:

spring.lifecycle.timeout-per-shutdown-phase=5s

Native-Image Builds Hold Sockets Differently

If you run a GraalVM native image of your app (Spring Boot 3.x native profile), the binary acquires the port via different OS calls than the JVM does. A native image left running in the background does not always show up under jps, so you may miss it. Use lsof -i :8080 (Unix) or Get-NetTCPConnection (PowerShell) instead of Java-specific tools to find it.

Spring Cloud Gateway Default Port

Spring Cloud Gateway-based services default to port 8080 just like the standard starter. Running Gateway and a regular Spring Boot app on the same machine without changing one of them produces the conflict immediately on second start. Set distinct ports per service in your application.yml.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles