I solved the Problem with a custom PropertySource that loads values from Files.
So your .properties file would look like this:
spring.datasource.password=${file(/run/secrets/db-password)}
or it is also possible to load the file url from an environment variable like this:
spring.datasource.password=${file(${SPRING_DATASOURCE_PASSWORD_FILE})}
To resolve the ${file(...)}
placeholder you need a custom PropertySource:
(Kotlin)
class FilePropertySource : PropertySource<File>("file") {
override fun getProperty(value: String): Any? =
Regex("file\\(([^\\s]*)\\)").matchEntire(value)
?.let { Files.newBufferedReader(Path.of(it.groupValues[1])).use(BufferedReader::readLine) }
}
(Java)
public class FilePropertySource extends PropertySource<File> {
private static final Log logger = LogFactory.getLog(FilePropertySource.class);
public FilePropertySource() { super("file"); }
@Override
public Object getProperty(String value) {
Matcher m = Pattern.compile("^file\\(([^\\s]*)\\)$").matcher(value);
if (!m.matches()) return null;
String path = m.toMatchResult().group(1);
try (BufferedReader br = Files.newBufferedReader(Path.of(path))) {
return br.readLine();
} catch (IOException e) {
logger.trace("Failed to read file for property: " + value);
}
return null;
}
}
And to activate this custom PropertySource it needs to be registered in the main method.
(Kotlin)
fun main(args: Array<String>) {
runApplication<Application>(*args) {
addInitializers({
it.environment.propertySources.addLast(FilePropertySource())
})
}
}
(Java)
public class Application {
public static void main(String[] args) {
SpringApplication sa = new SpringApplication(Application.class);
sa.addInitializers((ConfigurableApplicationContext applicationContext) -> {
applicationContext.getEnvironment().getPropertySources().addLast(new FilePropertySource());
});
sa.run(args);
}
}