skip to content

How do you obtain the actual port under RANDOM_PORT, and how does @LocalServerPort differ from server.port?

level: middleimportance: should knowfreq 52%

answer

  1. @LocalServerPort = ${local.server.port}
  2. RANDOM_PORT binds server.port=0
  3. server.port = requested; local.server.port = actual
  4. @LocalManagementPort for Actuator port

basics

~10 s

Inject an int field annotated with @LocalServerPort (or read the ${local.server.port} property). It holds the real random port the embedded server bound to at runtime, which under RANDOM_PORT differs from any configured server.port.

solid answer

~40 s

Under WebEnvironment.RANDOM_PORT the embedded container binds to a random free port chosen at startup. You read it via `@LocalServerPort` on an `int` field or constructor parameter — it's an alias for the property `local.server.port`, populated once the server is up. This is distinct from `server.port`, the *requested* configuration value: RANDOM_PORT effectively sets `server.port=0` ('pick any free port'), so `server.port` doesn't tell you the real bound port — only `local.server.port`/`@LocalServerPort` does. With TestRestTemplate you usually don't even need the number because it resolves relative URLs against the live port; you need `@LocalServerPort` mainly for building absolute URLs, WebSocket/other-protocol clients, or logging. There's also `@LocalManagementPort` for the Actuator management port when it runs separately.

code

java · 11 lines
java
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class PortTest {
    @LocalServerPort int port;        // actual bound port, e.g. 54123
    @Autowired TestRestTemplate rest; // could also just use relative URLs

    @Test
    void absoluteUrlWorks() {
        String url = "http://localhost:" + port + "/hello";
        assertThat(rest.getForObject(url, String.class)).isEqualTo("hi");
    }
}

go deeper

for a junior

Knows to inject @LocalServerPort to get the port.

for a middle

Distinguishes local.server.port (actual) from server.port (requested) and knows relative URLs often suffice.

for a senior

Explains when the port is actually needed and mentions @LocalManagementPort.

for a principal

Advises RANDOM_PORT + injection over fixed ports for CI stability and sets conventions.

## The problem RANDOM_PORT creates With `@SpringBootTest(webEnvironment = RANDOM_PORT)`, Boot starts the real embedded server but tells it to bind to **port 0**, meaning 'let the OS give me any free port.' You therefore don't know the port until the server has started. ## Getting the port: `@LocalServerPort` - `org.springframework.boot.test.web.server.LocalServerPort` (older path: `...web.server.LocalServerPort`) is a convenience annotation equivalent to `@Value("${local.server.port}")`. - Apply it to an `int` field or a constructor parameter: ```java @LocalServerPort int port; ``` - Boot publishes `local.server.port` into the `Environment` after the `WebServerInitializedEvent` fires, so the value is available by the time your test runs. ## `@LocalServerPort` vs `server.port` - `server.port` is the **input/config** value. Under RANDOM_PORT it's effectively `0`; under DEFINED_PORT it's the fixed value you set. Reading `server.port` under RANDOM_PORT gives you `0` (or the default), **not** the real port. - `local.server.port` (`@LocalServerPort`) is the **actual runtime port** the connector bound to. This is what you must use to reach the server. - Related: `@LocalManagementPort` / `local.management.port` exposes the **Actuator management port** when it's configured on a separate port from the main app. ## Do you even need it? - With **`TestRestTemplate`** (auto-configured under RANDOM_PORT) you typically pass **relative** paths (`"/api/x"`); the template already knows the port. Same idea with a properly bound `WebTestClient`. - You need `@LocalServerPort` explicitly when: constructing **absolute URLs** by hand, driving a **non-HTTP-RestTemplate client** (WebSocket, raw socket, a differently-configured client), or **logging** the port. ## Gotchas - `@LocalServerPort` is meaningless under **MOCK** and **NONE** — no server, so nothing binds; the property won't be set. - Don't hardcode a port and use DEFINED_PORT just to make `@LocalServerPort` predictable — that reintroduces port-clash risk. Prefer RANDOM_PORT + inject.

  • If you're using TestRestTemplate, do you still need @LocalServerPort?
    Usually not for plain HTTP calls — TestRestTemplate resolves relative URLs against the live port automatically. You need it for absolute URLs, non-HTTP or custom clients, WebSocket, or logging.
  • What does reading server.port return under RANDOM_PORT?
    It reflects the requested config (effectively 0/unset), not the real bound port. Only local.server.port / @LocalServerPort gives the actual runtime port.

saying these in an interview costs you the question

  • Reading server.port to find the real port under RANDOM_PORT
  • Using @LocalServerPort under MOCK/NONE and expecting a value
  • Thinking @LocalServerPort sets the port rather than reports it

context