Spring Boot
WebFlux
Netty
Embedded Server
Server Configuration

How to prevent embedded netty server from starting with spring-boot-starter-webflux?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Spring Boot starts embedded Netty automatically when it detects a reactive web application. That default is useful for APIs, but it is unnecessary for jobs, workers, and CLI tools that only need WebClient or reactive pipelines. The clean solution is to keep WebFlux where needed and explicitly disable web application startup behavior.

Why Netty Starts With WebFlux

When spring-boot-starter-webflux is on the classpath, auto-configuration assumes your app is a reactive web app. Spring then creates a reactive web server application context and initializes Netty on the configured port. This behavior is classpath-driven, so dependency choices and application type flags both influence startup.

In many services this is desirable. In background processors, it adds startup cost, open ports, and unnecessary operational surface area.

Option 1: Force Non-Web Application Type

The most direct way is setting application type to none.

application.yml:

yaml
spring:
  main:
    web-application-type: none

Equivalent in application.properties:

properties
spring.main.web-application-type=none

This tells Spring Boot to build a non-web context even if WebFlux dependencies are present.

You can also set it programmatically:

java
1import org.springframework.boot.WebApplicationType;
2import org.springframework.boot.builder.SpringApplicationBuilder;
3
4public class App {
5    public static void main(String[] args) {
6        new SpringApplicationBuilder(App.class)
7            .web(WebApplicationType.NONE)
8            .run(args);
9    }
10}

This is useful when different deployment modes require different startup behavior.

Option 2: Keep WebClient Without Full Web Starter

If your app only performs outbound HTTP calls, consider depending on spring-webflux instead of full starter bundles that pull server auto-configuration transitively.

Maven example:

xml
1<dependency>
2  <groupId>org.springframework</groupId>
3  <artifactId>spring-webflux</artifactId>
4</dependency>

Then create a client bean:

java
1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.web.reactive.function.client.WebClient;
4
5@Configuration
6public class ClientConfig {
7    @Bean
8    WebClient webClient() {
9        return WebClient.builder()
10            .baseUrl("https://api.example.com")
11            .build();
12    }
13}

This pattern gives reactive HTTP client support without advertising the app as a server.

Option 3: Exclude Reactive Web Server Auto-Configuration

For mixed setups, you can explicitly exclude specific auto-config classes.

java
1import org.springframework.boot.autoconfigure.SpringBootApplication;
2import org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration;
3
4@SpringBootApplication(exclude = ReactiveWebServerFactoryAutoConfiguration.class)
5public class App {
6}

Use this approach carefully. Excluding core auto-config can impact related features, so validate the whole context startup after changes.

Verifying Netty Is Not Running

After configuration updates, verify behavior instead of assuming success.

Useful checks:

  • startup logs should not show Netty binding to a port
  • no listener on expected HTTP port
  • application context type should be non-web

Quick shell check on macOS or Linux:

bash
lsof -i :8080

If no process is listening and logs show non-web startup, server suppression worked.

Running Scheduled or Worker Logic Without Web Context

A common goal is running batch logic while still using reactive APIs.

java
1import org.springframework.boot.CommandLineRunner;
2import org.springframework.stereotype.Component;
3
4@Component
5public class WorkerRunner implements CommandLineRunner {
6    private final WebClient webClient;
7
8    public WorkerRunner(WebClient webClient) {
9        this.webClient = webClient;
10    }
11
12    @Override
13    public void run(String... args) {
14        String body = webClient.get()
15            .uri("/health")
16            .retrieve()
17            .bodyToMono(String.class)
18            .block();
19
20        System.out.println(body);
21    }
22}

This style is common for integration jobs and polling workers.

Profile-Specific Control

Sometimes one artifact should run as API in one environment and worker in another. Use profiles to switch behavior safely.

application-worker.yml:

yaml
spring:
  main:
    web-application-type: none

Run with worker profile:

bash
java -jar app.jar --spring.profiles.active=worker

This avoids maintaining separate binaries for small mode differences.

Common Pitfalls

  • Assuming removing a controller class is enough to stop Netty startup.
  • Using full WebFlux starter for client-only workloads without setting non-web mode.
  • Excluding auto-config classes broadly and breaking unrelated features.
  • Forgetting profile-specific overrides that re-enable web mode in some environments.
  • Skipping runtime verification and discovering open ports only after deployment.

Summary

  • Netty starts because Spring Boot detects a reactive web app on the classpath.
  • Set spring.main.web-application-type to none for worker-style apps.
  • Use lighter dependencies when you only need WebClient.
  • Prefer explicit verification with logs and port checks after configuration changes.
  • Use profiles when one application must support both server and non-server modes.

Course illustration
Course illustration

All Rights Reserved.