Ktor server port precedence between application.conf and PORT env var
0 reputation · 17 Jan 2021, 18:03 UTC
0 reputation · 17 Jan 2021, 18:03 UTC
Diagnosing a Ktor deployment that fails health checks often requires knowing which port the embedded server actually bound at startup. Configuration is loaded from application.conf HOCON, environment variables, and command-line arguments, with programmatic configure blocks also available via embeddedServer factories.
The documented merging of these sources is ambiguous. It is unclear when a plain port = 8080 in HOCON is overridden by a PORT environment variable, and whether ${?PORT} substitution is required for env var fallback to work. ApplicationEngineEnvironment merges sources, but the merging strategy for nested objects is not formally specified.
Different server engines may log the bound address at different lifecycle stages, and structured logging formats may omit the effective port unless explicitly added to the log context.
Which configuration source takes precedence for the server port in Ktor 2.x and 3.x when both application.conf and an environment variable are present? Does the embeddedServer configure block always override HOCON and env vars, and is the effective port reliably emitted in startup logs? What is the intended precedence model for containerized deployments?
35275 reputation · 18 Jan 2021, 00:45 UTC
In Ktor 2.x and 3.x, an explicitly supplied port in embeddedServer(..., port = ...) wins when it is present. Otherwise, a bare PORT environment variable does not automatically override ktor.deployment.port in application.conf. Environment variables enter Ktor configuration only through HOCON substitution, typically ${?PORT} for an optional fallback or ${PORT} for a required value.
Confirmed: Ktor does not auto-map PORT to ktor.deployment.port. A plain port = 8080 inside ktor.deployment { port = 8080 } remains effective unless HOCON substitutes an environment variable or code passes a port argument.
Likely: configuration resolution follows system properties over application.conf over environment variables, with the programmatic port parameter applied after environment loading. This nested merge order is not formally specified, so treat it as version-dependent until verified against your build.
application.conf for ktor.deployment.port.printenv PORT in the deployment shell to confirm whether the variable exists.ktor.deployment.port = ${?PORT}. Without it, PORT is ignored.embeddedServer(Netty, port = 9090) { ... }, remove or condition the explicit argument when you want environment configuration to control the port.embeddedServer.start(), log application.environment.port to capture the effective port.Set PORT in the container and reference it with optional fallback:
ktor.deployment.port = ${?PORT}Provide a default in the same block if needed:
ktor.deployment.port = ${?PORT, 8080}Do not rely on automatic detection. ${PORT} without the question mark fails at startup when the variable is missing.
Effective port emission is engine-dependent. Netty, Jetty, and Tomcat may log the bound address at different lifecycle points, and structured logging can omit it unless the port is added to the log context. Verify by opening a connection to the reported port or by reading application.environment.port programmatically.
Assumption: behavior is consistent between Ktor 2.x and 3.x. This draft should be reviewed against your exact Ktor version because the nested merge strategy is not formally specified.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.