Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 62

YAML & Environment Variables for Configuration

Recommended resources

Hello! Welcome to the first lesson in our new module, "Advanced Configuration, Caching, & Monitoring."

In the previous module, you built a comprehensive suite of tests for your Spring application and learned how to measure their effectiveness using code coverage with JaCoCo. Testing ensures your application is reliable and correct. Now, we shift our focus to making your application flexible, manageable, and ready for deployment in real-world scenarios.

Today's lesson addresses a fundamental practice in modern software development: externalizing configuration. Our goal is to learn how to use YAML (application.yml) and environment variables to separate configuration details—like server ports, database credentials, and API keys—from your application's source code. This is a critical skill for managing an application across different environments such as development, testing, and production.

1. What is Externalized Configuration?

At its core, externalized configuration is the practice of keeping configuration parameters outside of your application’s packaged code (the JAR file). Instead of hardcoding a database URL or a server port directly in your Java classes, you define these values in external files or sources that Spring Boot can read at startup.

This approach offers significant advantages, making your application more robust and easier to manage.

Spring Boot and the Magic of Externalized Configuration

To understand the core concepts and benefits, please read the introductory sections of the article "Spring Boot and the Magic of Externalized Configuration" by Eidan Khan.

Please read the sections "What is Externalized Configuration?" and "Why Use Externalized Configuration?". Focus on the four main benefits listed: flexibility, security, maintainability, and scalability.

As the article highlights, the key reasons to externalize configuration are:

  • Flexibility: You can run the exact same application code in different environments (development, staging, production) just by changing the external configuration.
  • Security: You can keep sensitive data like passwords and API keys out of your version control system (e.g., Git), preventing them from being accidentally exposed.
  • Maintainability: You can update configuration values without recompiling and redeploying your entire application.
  • Scalability: It simplifies managing settings for multiple running instances of your application.

2. Configuration Formats: properties vs. yml

Spring Boot supports several formats for configuration files. The two most common are Java Properties files (.properties) and YAML files (.yml).

application.properties

This is the traditional format. It's a simple list of key-value pairs. You've likely seen it used to set basic properties like the server port:

server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/mydb
spring.datasource.username=root
spring.datasource.password=secret

This format is straightforward but can become repetitive and hard to read for complex, hierarchical configurations.

application.yml

YAML (a recursive acronym for "YAML Ain't Markup Language") is a data serialization format that is often preferred for configuration files because its hierarchical structure is more readable. It uses indentation (spaces, not tabs!) to denote structure.

Here is the same configuration from above, but in YAML format:

server:
  port: 8080
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb
    username: root
    password: secret

Notice how spring.datasource.url becomes nested under spring and datasource. This structure makes complex configurations much easier to visualize. Due to its readability, YAML has become the de-facto standard for many modern Spring Boot applications.

Spring Boot application.yml Configuration Example
This image shows how an `application.yml` file is typically structured within a Spring Boot project's `src/main/resources` directory. It visually demonstrates the hierarchical nature of YAML for configuring server, database, and other application settings.

3. The Property Overriding Hierarchy

A powerful feature of Spring Boot is its well-defined hierarchy for loading properties. This means you can define a default value in a file but override it from a more specific source without changing the file.

Spring Boot and the Magic of Externalized Configuration

The article "Spring Boot and the Magic of Externalized Configuration" provides a great analogy to explain this hierarchy. Understanding this order is key to managing configurations effectively.

Read the section "The Hierarchy of Property Sources — The Financial Chain of Command". This short section outlines the order in which Spring Boot considers different configuration sources.

The key takeaway is that sources with higher priority can override those with lower priority. A simplified but crucial hierarchy is:

  1. Command-line arguments (e.g., java -jar app.jar --server.port=9000)
  2. OS Environment Variables
  3. Application configuration files (application.yml or application.properties)

This hierarchy is what allows you to set a default port in application.yml but override it with an environment variable when deploying your application to the cloud.


4. Using Environment Variables in application.yml

Using environment variables is the standard and most secure way to supply environment-specific or sensitive configuration to your application, especially in containerized and cloud environments. You can reference them directly from your application.yml file.

This is where you combine the readability of YAML with the flexibility of environment variables. The syntax is straightforward and allows for required variables, optional variables, and defaults.

Spring Boot Configuration: When to Require, Default, or ...

The article "Spring Boot Configuration: When to Require, Default, or ..." by Juan Jose Miranda is an excellent, concise guide on the different ways to reference environment variables in your YAML files.

Please read through the three main syntaxes presented: ${VARIABLE}, ${VARIABLE:}, and ${VARIABLE:default}. The summary table and the final example file are particularly useful for consolidating your understanding.

Let's summarize the three essential patterns from the article:

  1. Required Variable: ${VAR}
    Use this for critical settings. If the environment variable VAR is not set, the application will fail to start. This is a "fail-fast" approach that prevents your application from running with an invalid configuration.

    • Use Case: Database credentials, API keys for essential services.
    • Example:
      spring:
        datasource:
          password: ${DB_PASSWORD} # Application startup fails if DB_PASSWORD is not set
      
  2. Optional with Default: ${VAR:default_value}
    This is useful for settings that have a sensible default for local development but may need to be changed in other environments. If the VAR is not set, default_value is used instead.

    • Use Case: Server port, logging level.
    • Example:
      server:
        port: ${PORT:8080} # Uses 8080 if PORT environment variable is not set
      
  3. Optional, Empty Default: ${VAR:}
    Use this when a value is truly optional and your code is designed to handle its absence (i.e., it can treat it as null or an empty string).

    • Use Case: A configuration for a non-essential feature that can be disabled.
    • Example:
      analytics:
        tracking-id: ${ANALYTICS_TRACKING_ID:} # Becomes an empty string if not set
      
Test your understanding!

You are configuring your application.

  1. The database URL is mandatory for all environments and will be provided via an environment variable DB_URL.
  2. The server port should default to 9090 but can be overridden by an environment variable SERVER_PORT.
  3. A feature flag for an experimental search algorithm should be controlled by ENABLE_EXPERIMENTAL_SEARCH, which is optional and can be absent (in which case it should be treated as empty).

How would you write the application.yml entries for these three properties?

Show answer
spring:
  datasource:
    url: ${DB_URL}

server:
  port: ${SERVER_PORT:9090}

app:
  feature-flags:
    experimental-search: ${ENABLE_EXPERIMENTAL_SEARCH:}

Conclusion

In this lesson, you've taken the first step into advanced application configuration, a skill that's essential for deploying software professionally. By separating configuration from code, you make your application more secure, flexible, and maintainable.

Key Takeaways:

  • Externalized configuration allows you to run the same code across different environments by changing external properties.
  • YAML (application.yml) is a human-readable format for hierarchical configuration, often preferred over .properties files.
  • Spring Boot has a clear property override hierarchy, where environment variables and command-line arguments take precedence over configuration files.
  • You can reference environment variables directly in application.yml to inject sensitive or environment-specific data, using syntaxes like ${DB_PASS}, ${PORT:8080}, and ${OPTIONAL_KEY:} to enforce different policies.

What's Next?

While environment variables are powerful, managing dozens of them for multiple environments (dev, QA, production) can become cumbersome. In our next lesson, we will explore Spring Profiles. You'll learn how to use profiles to create distinct configuration files for different environments (e.g., application-dev.yml, application-prod.yml), allowing you to manage environment-specific settings in a much more organized and scalable way.