Have you ever noticed application.yml bloating over time, with DB settings, Security settings, and feature flags all mixed together in a single file? Or hesitated over how to separate out just the secrets you don’t want in Git?

The spring.config.import property from the Config Data API, introduced in Spring Boot 2.4, makes these pain points much easier to handle. This article organizes the syntax and loading priority with concrete examples. The samples work with Spring Boot 2.4 and later, and the behavior is unchanged in Spring Boot 3.x (Java 17/21 environments) as well.

What is spring.config.import?

spring.config.import is a property used to load additional configuration sources from application.yml or application.properties. It was officially supported starting with the Config Data API in Spring Boot 2.4.

One thing to note: this is different from a remote configuration server like Spring Cloud Config Server. Think of it strictly as a mechanism for bundling together configuration files that live locally (on the classpath or file system).

Basic syntax

The simplest form looks like this.

# application.yml
spring:
  config:
    import: classpath:db.yml

You simply specify the file you want to load under the top-level spring.config.import. However, there’s a caveat: if the specified file isn’t found, startup itself fails by default.

org.springframework.boot.context.config.ConfigDataResourceNotFoundException:
  Config data resource 'class path resource [db.yml]' does not exist

Allowing an optional file with optional:

If you want to handle “a file that may not exist depending on the environment,” prefix it with optional:.

spring:
  config:
    import: optional:classpath:db.yml

Written this way, startup continues even if the file doesn’t exist. This is suited for settings specific to local development, or secret files that are optionally overridden.

Conversely, for cases where “this file is absolutely required,” it’s safer to leave out optional:. It’s much better to fail clearly at startup than to have a production incident caused by a missing file going unnoticed.

The difference between classpath: and file:

There’s one more prefix worth knowing.

  • classpath: … refers to locations inside a jar or under resources. Intended for configuration bundled with the app
  • file: … refers to a path on the file system at runtime. Intended for externalized configuration or secrets
spring:
  config:
    import: optional:file:./config/secrets.yml

When you write file: with a relative path, it’s resolved relative to the current working directory at runtime. This is a common source of trouble, since the current directory differs depending on whether you launch from an IDE or run the jar directly. More on this later.

Importing multiple files at once

There are also cases where you want to split configuration into files like db.yml, security.yml, and feature-flags.yml, and load them all at once. You can do this by listing them comma-separated.

spring:
  config:
    import: db.yml,security.yml,feature-flags.yml

You may have noticed that no classpath: or file: prefix is used here. When the prefix is omitted, Spring Boot tries to resolve the import relative to the same location as the file that declared the import. In the typical setup where application.yml sits at the root of the classpath (e.g., directly under src/main/resources), this ends up resolving to the same location as if classpath: had been specified. That said, the official documentation’s Importing Additional Data section also notes that this resolution rule can produce different results depending on your configuration, so when in doubt, it’s safer to explicitly specify classpath: or file:.

The loading order follows the order written, and files written later are loaded later, with later files winning when the same key appears in multiple files. For example, if both db.yml and security.yml have a property with the same name, the value from security.yml ends up taking effect. To avoid unintended overrides, it’s a good idea to clearly separate responsibilities between files.

Priority relative to profile-specific files

This is a subtle point that tends to trip people up in practice. For the basics of switching profiles itself, see How to Safely Switch Environment-Specific Configuration Using Spring Boot Profiles; here we’ll focus only on how it relates to file imports.

  • application-{profile}.yml is a profile-specific file that Spring Boot loads automatically
  • If a file explicitly imported via spring.config.import contains a section marked with spring.config.activate.on-profile separated by ---, that section is also activated for the same profile
  • Which one ultimately wins when both have the same key depends on the order in which the Config Data API stacks each location, and this can vary depending on how the import is written (path specification or order)

The official documentation’s Profile Specific Files section also explains the processing order in detail, so if there’s a chance that an important setting exists with the same key in both places, it’s strongly recommended to actually verify which one wins in your own setup before deploying to production. An easy way to check is to start the app with the --debug option and look at the Config Data loading logs, or to actually print out the value from the Environment.

The simplest way to avoid trouble altogether is to never write the same key in both application-{profile}.yml and an imported file. If you keep the roles separate, you don’t need to worry about priority at all.

Practical example: splitting into feature-specific files

A common real-world pattern is splitting files by concern. Since the prefix is omitted, the following files are assumed to live in the same location as application.yml (the classpath root).

# application.yml
spring:
  config:
    import: db.yml,security.yml,feature-flags.yml
# db.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/app
    username: app_user
# feature-flags.yml
app:
  features:
    new-checkout: true
    beta-dashboard: false

Splitting things up this way means you don’t need to touch db.yml when adding or removing feature flags. It also keeps diffs smaller during review, which quietly pays off in team development.

Secret files excluded from Git and the caveat about relative paths

If you don’t want passwords or API keys in the repository, combine optional:file: with an absolute path (or a path derived from an environment variable).

spring:
  config:
    import: optional:file:${user.home}/.app-secrets/secrets.yml

Exclude the target file via .gitignore, place it manually in local environments, and have CI/CD deploy the file at deploy time — this way, no secrets remain in the source code at all. If you want to go as far as encryption, combining this with How to Encrypt Spring Boot Configuration with Jasypt gives extra peace of mind.

Note that placeholders like ${user.home} are limited to values that can be resolved early in startup, such as system properties, environment variables, or command-line arguments. Referencing other keys within application.yml the same way will not resolve, so be careful.

The reason to use an absolute path is that relative paths are resolved against the current working directory. A reference like file:./config/secrets.yml from earlier works fine while you’re running from the project root in an IDE. But if you launch the jar in production from a different directory, such as /opt/app/, the current directory becomes that location, so the same relative path won’t be found, and it will fail with an exception like this:

org.springframework.boot.context.config.ConfigDataResourceNotFoundException:
  Config data resource 'file [config/secrets.yml]' does not exist

The exact wording of this exception message may vary slightly depending on the Spring Boot version, so there’s no need to worry if it doesn’t match exactly — it’s enough to confirm the basic fact that “the file isn’t being found.” The fix is to replace it with an absolute path that doesn’t depend on the launch location, such as ${user.home}. This way, it points to the same file regardless of where it’s run from, and adding optional: lets startup continue even in environments where the file doesn’t exist.

For those migrating from the bootstrap.yml era

If you’re used to Spring Cloud Config, you might feel that “externalizing configuration requires bootstrap.yml.” With the Config Data API, the special initialization phase where bootstrap.yml gets loaded is generally no longer necessary.

If your goal is simple splitting and externalization, spring.config.import is a perfectly sufficient substitute. Only consider a different approach if you genuinely need a remote configuration source like a Config Server.

Summary

Using spring.config.import, you can split application.yml by feature or environment, while also safely externalizing just the secrets. Keep three things in mind — whether or not to use optional:, the file loading order, and verifying priority against profile-specific files on an actual machine when you’re not sure — and you should run into far fewer unexpected overrides or incidents. For the basics of writing application.yml itself, also check out Spring Boot’s application.yml Configuration Guide.