Skip to content
ADevGuide Logo ADevGuide
Go back

An Easy Step-By-Step Guide to Changing Server Port in a Spring Boot Application [4 Ways]

Updated:

By Pratik Bhuite | 18 min read

Hub: Spring Boot / Core Guides

Series: Spring Boot Mastery Series

Last updated: Mar 11, 2026

Part 2 of 4 in the Spring Boot Mastery Series

Key Takeaways

On this page
Reading Comfort:

An Easy Step-By-Step Guide to Changing Server Port in a Spring Boot Application [4 Ways]

Spring Boot applications run on port 8080 by default, but production environments often require different port configurations. Whether you’re avoiding port conflicts, running microservices on the same server, or adhering to organizational standards, knowing how to change the server port is essential. This guide covers four distinct methods with practical examples and troubleshooting solutions.

Table of Contents

Open Table of Contents

Why Change the Default Port?

Spring Boot’s default port 8080 works fine for development, but real-world scenarios demand flexibility:

Common Use Cases

1. Port Conflicts When port 8080 is already occupied by another application (Apache Tomcat, Jenkins, or another Spring Boot app), you’ll encounter java.net.BindException: Address already in use.

2. Microservices Architecture Running multiple Spring Boot services on the same server requires each service to use a unique port:

  • User Service: 8081
  • Order Service: 8082
  • Payment Service: 8083

3. Production Standards Many organizations have port assignment policies:

  • Development: 8000-8999
  • Staging: 9000-9999
  • Production: Standard ports (80 for HTTP, 443 for HTTPS)

4. Security Requirements Running applications on non-standard ports can add a layer of security through obscurity, though this should never be your primary security measure.

Method 1: Using application.properties

The most straightforward method is configuring the port in your application.properties file located in src/main/resources/.

Basic Configuration

# application.properties
server.port=9090

This sets the server port to 9090 for all environments.

Real-World Example: Development vs Production

# application.properties (default for all environments)
server.port=8080
spring.application.name=customer-service

For development, you might want a different port:

# application-dev.properties
server.port=8081
logging.level.root=DEBUG

For production:

# application-prod.properties
server.port=80
logging.level.root=WARN
server.compression.enabled=true

Activate the profile when running:

java -jar customer-service.jar --spring.profiles.active=prod

Advantages

  • ✅ Simple and intuitive
  • ✅ Version-controlled with your code
  • ✅ Supports profile-specific configurations
  • ✅ No code changes required

Disadvantages

  • ❌ Requires application rebuild/redeploy to change
  • ❌ Less flexible for dynamic scenarios

Method 2: Using application.yml

If you prefer YAML syntax for better readability and hierarchical structure, use application.yml instead.

Basic Configuration

# application.yml
server:
  port: 9090

Advanced Configuration with Multiple Settings

# application.yml
server:
  port: 9090
  servlet:
    context-path: /api
  compression:
    enabled: true
    mime-types: application/json,application/xml,text/html,text/xml,text/plain
  error:
    include-message: always
    include-binding-errors: always

Profile-Specific Configuration

# application.yml
spring:
  application:
    name: inventory-service
  profiles:
    active: dev

server:
  port: 8080  # Default port

---
# Development profile
spring:
  config:
    activate:
      on-profile: dev

server:
  port: 8081

logging:
  level:
    root: DEBUG

---
# Production profile
spring:
  config:
    activate:
      on-profile: prod

server:
  port: 443
  ssl:
    enabled: true
    key-store: classpath:keystore.p12
    key-store-password: ${SSL_PASSWORD}
    key-store-type: PKCS12

logging:
  level:
    root: WARN

Advantages

  • ✅ Better readability for complex configurations
  • ✅ Supports multiple profiles in one file
  • ✅ Hierarchical structure reduces repetition
  • ✅ Industry-standard format

Disadvantages

  • ❌ YAML syntax can be error-prone (indentation matters)
  • ❌ Same deployment limitations as properties files

Method 3: Command-Line Arguments

Perfect for containerized environments, CI/CD pipelines, or when you need to override configuration without modifying files.

Basic Usage

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

Multiple Arguments

java -jar myapp.jar \
  --server.port=9090 \
  --spring.profiles.active=prod \
  --logging.level.root=INFO

Real-World Example: Docker Container

When deploying Spring Boot applications using Docker, you can override the port dynamically:

Dockerfile:

FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/myapp.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Run with custom port:

# Override port at runtime
docker run -p 9090:9090 myapp --server.port=9090

# Using environment variable (recommended)
docker run -e SERVER_PORT=9090 -p 9090:9090 myapp --server.port=${SERVER_PORT}

Docker Compose:

version: '3.8'
services:
  user-service:
    image: myapp:latest
    ports:
      - "8081:8081"
    command: ["--server.port=8081"]
  
  order-service:
    image: myapp:latest
    ports:
      - "8082:8082"
    command: ["--server.port=8082"]

Kubernetes Deployment

apiVersion: apps/v1
kind: Deployment
metadata:
  name: customer-service
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: app
        image: customer-service:1.0
        args:
          - "--server.port=8080"
          - "--spring.profiles.active=prod"
        ports:
        - containerPort: 8080
        env:
        - name: SERVER_PORT
          valueFrom:
            configMapKeyRef:
              name: app-config
              key: server.port

Advantages

  • ✅ Dynamic port assignment without code changes
  • ✅ Perfect for containers and cloud deployments
  • ✅ Overrides file-based configuration
  • ✅ No rebuild required

Disadvantages

  • ❌ Must be specified every time you run the app
  • ❌ Not documented in code repository
  • ❌ Can be forgotten in deployment scripts

Method 4: Programmatic Configuration

For advanced scenarios where you need conditional logic, runtime calculations, or integration with external configuration systems.

Using WebServerFactoryCustomizer

package com.example.config;

import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ServerConfig {

    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 
           webServerFactoryCustomizer() {
        return factory -> factory.setPort(9090);
    }
}

Dynamic Port Based on Environment

package com.example.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DynamicPortConfig {

    @Value("${ENVIRONMENT:dev}")
    private String environment;

    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 
           customizer() {
        return factory -> {
            int port = calculatePort(environment);
            factory.setPort(port);
            System.out.println("Starting server on port: " + port);
        };
    }

    private int calculatePort(String env) {
        return switch (env.toLowerCase()) {
            case "dev" -> 8081;
            case "staging" -> 9090;
            case "prod" -> 80;
            default -> 8080;
        };
    }
}

Port Assignment from External Config Server

package com.example.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class ExternalConfigPortSetup {

    @Autowired
    private RestTemplate restTemplate;

    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 
           externalConfigCustomizer() {
        return factory -> {
            try {
                // Fetch port from external configuration service
                String configUrl = "http://config-server:8888/port-assignment";
                Integer port = restTemplate.getForObject(configUrl, Integer.class);
                
                if (port != null && port > 0 && port <= 65535) {
                    factory.setPort(port);
                } else {
                    factory.setPort(8080); // Fallback to default
                }
            } catch (Exception e) {
                System.err.println("Failed to fetch port from config server: " + e.getMessage());
                factory.setPort(8080); // Fallback to default
            }
        };
    }

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

Advantages

  • ✅ Full control over port selection logic
  • ✅ Can integrate with external systems
  • ✅ Supports complex conditional logic
  • ✅ Useful for multi-tenant applications

Disadvantages

  • ❌ More complex to implement and maintain
  • ❌ Harder to debug port assignment issues
  • ❌ Overkill for simple use cases

Environment-Specific Port Configuration

Modern applications run in multiple environments. Here’s how to manage ports across dev, staging, and production:

Using Spring Profiles

application.properties:

# Default configuration
server.port=8080
spring.application.name=payment-service

application-dev.properties:

server.port=8081
spring.datasource.url=jdbc:mysql://localhost:3306/dev_db

application-staging.properties:

server.port=9090
spring.datasource.url=jdbc:mysql://staging-db:3306/staging_db

application-prod.properties:

server.port=443
server.ssl.enabled=true
spring.datasource.url=jdbc:mysql://prod-db:3306/prod_db

Run with specific profile:

# Development
java -jar payment-service.jar --spring.profiles.active=dev

# Production
java -jar payment-service.jar --spring.profiles.active=prod

Using Environment Variables

Set the port via environment variable (works across all methods):

# Linux/Mac
export SERVER_PORT=9090
java -jar myapp.jar

# Windows
set SERVER_PORT=9090
java -jar myapp.jar

# Docker
docker run -e SERVER_PORT=9090 -p 9090:9090 myapp

Spring Boot automatically maps SERVER_PORT to server.port property.

Random Port Assignment

Useful for testing environments or when running multiple instances:

Configuration

# application.properties
server.port=0

Or in YAML:

server:
  port: 0

Spring Boot will assign a random available port.

Retrieving the Assigned Port

package com.example.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class InfoController {

    @LocalServerPort
    private int port;

    @GetMapping("/port")
    public String getPort() {
        return "Application is running on port: " + port;
    }
}

Use in Integration Tests

package com.example;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.boot.test.web.server.LocalServerPort;

import static org.assertj.core.api.Assertions.assertThat;

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

    @LocalServerPort
    private int port;

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    void testRandomPortAssignment() {
        String response = restTemplate.getForObject(
            "http://localhost:" + port + "/port", 
            String.class
        );
        assertThat(response).contains("running on port: " + port);
    }
}

Common Errors and Solutions

Error 1: Port Already in Use

Error Message:

***************************
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.

Full Exception:

org.springframework.boot.web.server.PortInUseException: Port 8080 is already in use
    at org.springframework.boot.web.embedded.tomcat.TomcatWebServer.start(TomcatWebServer.java:226)
Caused by: java.net.BindException: Address already in use

Solution 1: Find and Kill the Process

Linux/Mac:

# Find process using port 8080
lsof -i :8080

# Output example:
# COMMAND  PID   USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
# java    12345 user   123u  IPv6 123456      0t0  TCP *:8080 (LISTEN)

# Kill the process
kill -9 12345

Windows:

# Find process using port 8080
netstat -ano | findstr :8080

# Output example:
# TCP    0.0.0.0:8080    0.0.0.0:0    LISTENING    12345

# Kill the process
taskkill /PID 12345 /F

Solution 2: Change to Different Port

# application.properties
server.port=9090

Solution 3: Use Random Port

server.port=0

Error 2: Invalid Port Number

Error Message:

java.lang.IllegalArgumentException: Port must be between 0 and 65535
    at org.springframework.boot.web.server.AbstractConfigurableWebServerFactory.setPort

Common Invalid Configurations:

# ❌ Negative port
server.port=-1

# ❌ Port exceeds limit
server.port=70000

# ❌ Non-numeric value
server.port=eight-zero-eight-zero

Solution:

Use valid port range (1-65535, or 0 for random):

# ✅ Valid configurations
server.port=8080
server.port=443
server.port=0

Note: Ports 1-1023 are privileged ports and require root/administrator access on Unix systems.

Error 3: Configuration Not Taking Effect

Problem: You’ve set the port in application.properties but the app still starts on 8080.

Common Causes:

1. Multiple Configuration Files

Check for multiple configuration files:

flowchart TD
  R[src/main/resources] --> P[application.properties server.port 9090]
  R --> Y[application.yml server.port 8080 takes precedence]

Solution: Spring Boot loads configurations in this order (later overrides earlier):

  1. application.properties
  2. application.yml
  3. Profile-specific properties (application-{profile}.properties)
  4. Profile-specific YAML (application-{profile}.yml)
  5. Command-line arguments (highest priority)

Remove duplicate configurations or use command-line to override:

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

2. Wrong File Location

# ❌ Wrong location
src/resources/application.properties

# ✅ Correct location
src/main/resources/application.properties

3. Typos in Property Name

# ❌ Wrong
server.port=9090
Server.Port=9090

# ✅ Correct
server.port=9090

4. Active Profile Overriding

# application.properties has server.port=8080
# application-dev.properties has server.port=9090

# This will use port 9090 (dev profile overrides default)
java -jar myapp.jar --spring.profiles.active=dev

Debugging Tips:

Enable debug logging to see which configuration files are loaded:

logging.level.org.springframework.boot=DEBUG

Or run with debug flag:

java -jar myapp.jar --debug

Look for lines like:

INFO: Loaded config file 'file:/path/to/application.properties'
INFO: Loaded config file 'file:/path/to/application-dev.properties'

Error 4: Permission Denied (Ports < 1024 on Linux)

Error Message:

java.net.SocketException: Permission denied

Problem: Trying to use port 80 or 443 without proper permissions.

Solution 1: Run with sudo (not recommended)

sudo java -jar myapp.jar

Solution 2: Use authbind (recommended)

# Install authbind
sudo apt-get install authbind

# Allow binding to port 80
sudo touch /etc/authbind/byport/80
sudo chmod 500 /etc/authbind/byport/80
sudo chown yourusername /etc/authbind/byport/80

# Run application
authbind --deep java -jar myapp.jar

Solution 3: Use reverse proxy (best practice)

Use nginx or Apache as reverse proxy on port 80/443, forwarding to your Spring Boot app on 8080:

# /etc/nginx/sites-available/myapp
server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://localhost:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
}

Best Practices

1. Use Profile-Specific Configuration

Don’t hardcode ports for different environments in the same file:

# ✅ Good: application.yml
server:
  port: ${SERVER_PORT:8080}  # Environment variable with fallback

---
# application-dev.yml
server:
  port: 8081

---
# application-prod.yml
server:
  port: 443

2. Document Port Assignments

Maintain a ports.md file in your repository:

# Port Assignments

| Service          | Dev  | Staging | Production |
|------------------|------|---------|------------|
| API Gateway      | 8080 | 9000    | 443        |
| User Service     | 8081 | 9001    | 8081       |
| Order Service    | 8082 | 9002    | 8082       |
| Payment Service  | 8083 | 9003    | 8083       |
| Notification     | 8084 | 9004    | 8084       |

3. Use Environment Variables in Production

Avoid hardcoding ports in production properties:

# application-prod.yml
server:
  port: ${SERVER_PORT}  # Required environment variable

Set via deployment platform:

# Kubernetes ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:
  SERVER_PORT: "8080"

4. Health Check on Custom Ports

When using custom ports, update health check endpoints:

# application.yml
server:
  port: 9090

management:
  server:
    port: 9091  # Separate port for actuator endpoints
  endpoints:
    web:
      exposure:
        include: health,info,metrics

Access health endpoint:

curl http://localhost:9091/actuator/health

5. Test Port Conflicts in CI/CD

Include port conflict tests in your pipeline:

#!/bin/bash
# check-port.sh

PORT=8080
if lsof -Pi :$PORT -sTCP:LISTEN -t >/dev/null ; then
    echo "Port $PORT is already in use. Please free it before running tests."
    exit 1
fi

echo "Port $PORT is available"
./mvnw clean test

Real-World Example: Microservices Architecture

Let’s see how Netflix-style microservices architecture manages ports across multiple services.

Project Structure

flowchart TD
  E[ecommerce-platform] --> G[api-gateway port 8080]
  E --> U[user-service port 8081]
  E --> PR[product-service port 8082]
  E --> O[order-service port 8083]
  E --> PA[payment-service port 8084]
  E --> N[notification-service port 8085]
  E --> D[docker-compose.yml]

Service Configuration

user-service/application.yml:

spring:
  application:
    name: user-service

server:
  port: ${USER_SERVICE_PORT:8081}

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true
    instance-id: ${spring.application.name}:${server.port}

order-service/application.yml:

spring:
  application:
    name: order-service

server:
  port: ${ORDER_SERVICE_PORT:8083}

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

Docker Compose Configuration

version: '3.8'

services:
  eureka-server:
    image: eureka-server:latest
    ports:
      - "8761:8761"
    networks:
      - microservices-net

  api-gateway:
    image: api-gateway:latest
    ports:
      - "8080:8080"
    environment:
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    depends_on:
      - eureka-server
    networks:
      - microservices-net

  user-service:
    image: user-service:latest
    ports:
      - "8081:8081"
    environment:
      - SERVER_PORT=8081
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    depends_on:
      - eureka-server
    networks:
      - microservices-net

  order-service:
    image: order-service:latest
    ports:
      - "8083:8083"
    environment:
      - SERVER_PORT=8083
      - USER_SERVICE_URL=http://user-service:8081
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    depends_on:
      - eureka-server
      - user-service
    networks:
      - microservices-net

  payment-service:
    image: payment-service:latest
    ports:
      - "8084:8084"
    environment:
      - SERVER_PORT=8084
      - EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
    depends_on:
      - eureka-server
    networks:
      - microservices-net

networks:
  microservices-net:
    driver: bridge

Running Multiple Instances

For load balancing, run multiple instances of the same service on different ports:

# Instance 1
java -jar user-service.jar --server.port=8081

# Instance 2
java -jar user-service.jar --server.port=8091

# Instance 3
java -jar user-service.jar --server.port=8101

Or with Docker:

docker run -e SERVER_PORT=8081 -p 8081:8081 user-service
docker run -e SERVER_PORT=8091 -p 8091:8091 user-service
docker run -e SERVER_PORT=8101 -p 8101:8101 user-service

Conclusion

Changing the server port in Spring Boot is a fundamental configuration that every developer should master. This guide covered four distinct methods:

  1. application.properties - Simple, version-controlled, profile-aware
  2. application.yml - Better readability, hierarchical structure
  3. Command-line arguments - Dynamic, perfect for containers and cloud
  4. Programmatic configuration - Full control, conditional logic support

Choose the method that best fits your deployment model and organizational requirements. For most applications, using application.yml with Spring profiles provides the right balance of simplicity and flexibility. For containerized deployments, command-line arguments or environment variables offer maximum flexibility.

Remember these key takeaways:

  • Always document your port assignments
  • Use environment-specific profiles
  • Test for port conflicts in CI/CD
  • Prefer environment variables in production
  • Use reverse proxies for privileged ports
  • Monitor port availability before startup

With this knowledge, you can confidently manage server ports across development, staging, and production environments, whether you’re running a single monolith or a complex microservices architecture.

References

  1. Spring Boot Reference Documentation - Application Properties
    https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.server

  2. Spring Boot Server Configuration Guide
    https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.webserver

  3. Externalized Configuration in Spring Boot
    https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config

  4. Baeldung - Configure Spring Boot Web Server
    https://www.baeldung.com/spring-boot-configure-tomcat

  5. Spring Boot Docker Documentation
    https://spring.io/guides/gs/spring-boot/

YouTube Videos

  1. “Spring Boot Configuration - Properties vs YAML” by Java Brains
    https://www.youtube.com/watch?v=Y6Ev8GIlbxc

  2. “Spring Boot Profiles Explained” by Amigoscode
    https://www.youtube.com/watch?v=xDuwrtwYHu8

  3. “Dockerizing Spring Boot Applications” by Daily Code Buffer
    https://www.youtube.com/watch?v=NtMvNh0WFVM

  4. “Spring Boot Microservices with Docker Compose” by Programming Techie
    https://www.youtube.com/watch?v=VAmntTPebKE

  5. “Spring Boot Application Properties Deep Dive” by Telusko
    https://www.youtube.com/watch?v=bUHFg8CZFws


Share this post on:

Next in Series

Continue through the Spring Boot Mastery Series with the next recommended article.

Related Posts

Keep Learning with New Posts

Subscribe through RSS and follow the project to get new series updates.

Was this guide helpful?

Share detailed feedback

Previous Post
Perforce MCP Server: AI-Powered Version Control for AI Agents
Next Post
Monolith vs Microservices: Pros, Cons, and When to Choose