Spring Boot services commonly use application.yml, application-prod.yml, profile-specific files, and external configuration. These patterns are valid, but production secrets committed to a repository must be treated as exposed.
When to use this guide
- DB passwords, tokens, or signing keys are written directly in
application.ymlorapplication-prod.yml. - Spring Boot uses profile-specific configuration.
- You need to choose between
${ENV_VAR}references and a mounted configuration file. - Spring configuration is supplied externally through Kubernetes, VMs, or secure file delivery.
Examples
Pattern A: Inject values through ${ENV_VAR}
This is a simple option when the environment supports injection of environment variables.
When to choose this pattern
- There are relatively few secrets and they can be supplied as environment variables.
- Your deployment system supports this as a standard mechanism.
- You want to retain profiles while providing secret values externally.
Before
spring:
datasource:
url: jdbc:postgresql://prod-db.internal:5432/app
username: app_user
password: super-secret-password
jwt:
secret: hardcoded-jwt-secret
After
spring:
datasource:
url: ${DB_URL}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jwt:
secret: ${JWT_SECRET}
Spring code example
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;
import jakarta.validation.constraints.NotBlank;
@Validated
@ConfigurationProperties(prefix = "jwt")
public class JwtProperties {
@NotBlank
private String secret;
public String getSecret() {
return secret;
}
public void setSecret(String secret) {
this.secret = secret;
}
}
This is an excerpt for a Spring Boot environment using Jakarta Validation. Add the validation dependency and register the class through @ConfigurationPropertiesScan or @EnableConfigurationProperties. Stop startup if required values are missing or empty.
Pattern B: Inject a mounted application.yml
If operations require an application.yml or application-prod.yml file, supply the production file at runtime through a path such as /config/application.yml or /etc/app/application.yml.
When to choose this pattern
- You need to retain the profile-specific file structure.
- An external configuration path is part of your operational standard.
- Kubernetes Secret volumes, Vault Agent, or secure file delivery are available.
File to keep in the repository
spring:
datasource:
url: jdbc:postgresql://prod-db.internal:5432/app
username: app_user
password: "<runtime-provided>"
jwt:
secret: "<runtime-provided>"
Example runtime paths
/config/application.yml
/etc/app/application.yml
Spring configuration example
Use spring.config.import or Spring Boot's standard external configuration locations. Supply the actual secret at runtime instead of storing it in the repository.
spring:
config:
import: file:/etc/app/application.yml
This file is required, so the import does not use optional:. Do not commit production application.yml or application-prod.yml files to Git.
Developer tasks
- Remove actual secret values from
application.ymlandapplication-prod.yml. - Choose environment references or external file loading to suit the service.
- Configure
@ConfigurationPropertiesor the existing loader to fail closed when required values are missing. - Check that logs, exceptions, debug output, and Actuator endpoints do not expose secrets.
Infrastructure and platform tasks
- Configure environment-variable injection for the environment-based pattern.
- Standardize the Secret volume, secure file delivery, or external file path for the file-based pattern.
- Prevent actual secrets from being stored in Helm values, Docker images, or ConfigMaps.
- Document restart or reload steps for secret rotation.
Verification
- Check that production files such as
application-prod.ymlare absent from the repository. - Confirm that
${ENV_VAR}values or mounted files are supplied in the deployed environment. - Check
/actuator/env, debug logs, and exception responses for secret exposure. - Confirm that startup fails when a required secret is missing.
Common mistakes
- Committing
application-prod.ymlto the repository - Storing secrets directly in Helm values
- Baking production configuration into a Docker image
- Exposing values through
/actuator/env, debug logs, or exception stacks - Using empty defaults that allow startup without required secrets
Example instructions for the owner
- “Remove actual secrets from Spring Boot configuration and replace them with
${ENV_VAR}references or externally supplied runtime files.” - “Even if you need
application-prod.yml, do not commit it. Supply it at runtime through a path such as/config/application.ymlor/etc/app/application.yml.” - “Make
@ConfigurationPropertiesor the existing loader fail immediately when a required secret is missing, and check/actuator/envand debug logs for exposure.”