Skip to main content

Welcome to a Grasp lesson

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

Lesson 58

Runtime Configuration: Env Vars & CLI Args

Recommended resources

Hello! Let's dive into the next crucial aspect of deploying Spring Boot applications.

In our previous lesson, we learned how to package our application into a self-contained, executable JAR file. This gives us a portable artifact, but it raises an important question: how do we manage configuration—like database credentials, API keys, or server ports—that differs between development, staging, and production environments? Rebuilding the JAR for each environment is not a scalable or secure solution.

This lesson directly tackles that challenge. Our learning outcome is to externalize configuration at runtime using environment variables or command-line arguments. This is a fundamental practice in modern software engineering, essential for building flexible, secure, and easily deployable applications, especially in the microservices architectures you're familiar with.

1. The "Why": Configuration in the Environment

Before diving into the "how," it's vital to understand the "why." Your experience with system design will make the core principle here very intuitive. It's drawn from the Twelve-Factor App methodology, a set of best practices for building modern, cloud-native applications.

Factor III of this methodology states: "Store config in the environment." This means cleanly separating configuration from code. Your application's compiled artifact (the JAR file) should be immutable and identical across all deployment environments. The configuration, which varies, should be injected from the outside.

99% of Spring Boot Developers Get Configuration Wrong

To grasp this foundational concept, we'll read a guide that breaks down this principle and provides a clear mental model.

From this Medium article by Raju Methuku, please read the first two major sections: The Foundation: Understanding the 12-Factor App: Focus on the 'Config in environment = Good' rule and the 'Think Like a Chef' analogy. This sets the stage. The 4 Types of Configuration (And Where Each Goes): This is the most critical part. Pay close attention to the breakdown of Secrets, Infrastructure, Application Behavior, and Code-Coupled configuration. This framework will guide all your future configuration decisions.

As the article highlights, treating infrastructure details (like database hosts) and secrets (like passwords) as external configuration injected at runtime is not just a good practice—it's essential for security, flexibility, and scalability. It allows the same application artifact to be deployed anywhere, from your local machine to a multi-region cloud setup, simply by providing a different set of environment-specific values.


2. Externalizing Configuration with Environment Variables

Spring Boot is designed with the 12-Factor App principles in mind and makes it easy to consume configuration from the environment. The most common way to do this is with environment variables.

2.1. Referencing Environment Variables

You can reference environment variables directly within your application.properties or application.yml files using the ${VARIABLE_NAME} syntax.

Setting Environment Variables in Spring Boot Application (YouTube Thumbnail)
This image illustrates how to use placeholders like `${DB_URL}` and `${DB_USERNAME}` in a configuration file to read values from environment variables at runtime.

When the application starts, Spring Boot will look for an environment variable with that name and substitute its value. If the variable isn't found, the application will fail to start by default, which is a safe behavior that prevents running with a misconfigured setup.

Let's see this in action.

Spring Boot Environment Variables Explained (Secure Your API Keys!)

This video by Dan Vega provides a clear, hands-on demonstration of using environment variables in Spring Boot.

Watch the segments from 00:00 to 01:00, 01:00 to 04:31, and 05:17 to 08:28. The presenter will: Explain the benefits of externalizing configuration. Demonstrate accessing a hardcoded property. Replace the hardcoded value with a reference to an environment variable (${MY_API_KEY}). Show how to set this environment variable in the IDE's run configuration (a common practice for local development).

2.2. Relaxed Binding

You might notice that environment variables are often in UPPER_SNAKE_CASE (e.g., DATABASE_URL), while Spring properties are in kebab-case or camelCase (e.g., spring.datasource.url). Spring Boot handles this automatically through a feature called relaxed binding.

It converts property names according to a set of rules. For environment variables, it will:

  • Replace dots (.) with underscores (_).
  • Remove dashes (-).
  • Convert to uppercase.

This means an environment variable SPRING_DATASOURCE_URL will correctly map to the spring.datasource.url property. You can read more about these rules in the official Spring Boot documentation on Relaxed Binding.

2.3. Providing Default Values

Sometimes, you want your application to run even if an environment variable isn't set, using a sensible default. You can specify a default value directly in the placeholder:

${VARIABLE_NAME:default_value}

For example:
server.port=${PORT:8080}

This tells Spring Boot: "Use the value of the PORT environment variable. If it's not set, use 8080." This is useful for non-critical settings or for providing a standard value for local development.

Spring Boot Environment Variables Explained (Secure Your API Keys!)

Let's return to Dan Vega's video to see how to implement default values and what happens when a variable is missing.

Watch the segment from 08:28 to 12:24. This part demonstrates: The Could not resolve placeholder error when a variable is missing and no default is provided. How to add a default value (e.g., ${DB_URL:jdbc:postgresql...}). How the application successfully starts and uses the default value when the environment variable is not set.


3. Externalizing Configuration with Command-Line Arguments

Another powerful way to supply configuration at runtime is through command-line arguments. This is especially useful for one-off overrides or for scripting deployments.

You pass these arguments when you run the JAR file, using a double-dash syntax:

java -jar my-app.jar --property.name=value

For example, to override the server port:

java -jar my-app.jar --server.port=9000

This method is direct and highly visible. It's often used in startup scripts for services.

Spring Boot Environment Variables Explained (Secure Your API Keys!)

The end of Dan Vega's video demonstrates exactly how to pass a property as a command-line argument when running the executable JAR.

Watch from 12:24 to 14:35. The key part is the java -jar command where he passes my.api.key=999-999-999 directly on the command line, overriding any other configuration.


4. Configuration Precedence

You've now seen several ways to provide configuration: in application.properties inside the JAR, via environment variables, and via command-line arguments. What happens if you define the same property in multiple places?

Spring Boot has a well-defined order of precedence. Later sources in the list override earlier ones. A simplified (but very common) version of this hierarchy is:

  1. Command-line arguments (highest priority)
  2. OS Environment variables
  3. application.properties file located outside your packaged JAR (in the same directory)
  4. application.properties file packaged inside your JAR (lowest priority)

This hierarchy is extremely powerful. It allows you to:

  • Ship default values inside your JAR.
  • Provide environment-specific overrides with an external properties file.
  • Let platform-managed environment variables (like in a cloud environment) take precedence.
  • Perform one-time overrides for debugging or testing using command-line arguments.

Using external property sources with Spring Boot - Microservice configuration with Spring Boot [04]

The Java Brains channel has an excellent video demonstrating this layering of configuration sources.

Watch from 01:49 to 05:29. This segment demonstrates: How an external application.properties file overrides the one inside the JAR. How a command-line argument (--my.greeting=...) then overrides both the internal and external files. This provides a concrete example of the precedence hierarchy in action.

The full, detailed list of property sources is available in the official Spring Boot documentation.


Conclusion

In this lesson, you've learned the principles and practices of externalizing configuration, a cornerstone of building robust, deployable microservices. By separating configuration from your code, you create a single, immutable artifact that can run anywhere.

Key Takeaways:

  • The 12-Factor App: The guiding principle is to "Store config in the environment," separating what changes (config) from what doesn't (code).
  • Configuration Types: A helpful mental model is to categorize config into Secrets, Infrastructure, Application Behavior, and Code-Coupled, each with a designated place. Secrets and infrastructure belong in the environment.
  • Environment Variables: Use ${VAR_NAME} or ${VAR_NAME:default_value} in property files to consume values. Spring's relaxed binding helps map UPPER_SNAKE_CASE variables to camelCase properties.
  • Command-Line Arguments: Use java -jar app.jar --property.name=value to provide configuration with the highest precedence, perfect for overrides.
  • Precedence Matters: Command-line arguments override environment variables, which override external property files, which override internal property files.

You can now build a portable JAR and configure it at runtime. The natural next step is to package this JAR into a standardized, isolated unit for deployment.

In our next lesson, we will create a Dockerfile to containerize a Spring Boot application, where you'll put today's knowledge of environment variables to immediate and practical use.