# Spring Boot environment variables and profiles

> Spring Boot ranks OS environment variables above application.properties and maps them with relaxed binding. Profiles, config trees and Crunu explained.

URL: https://crunu.com/environment-variables/spring-boot
Language: en
Updated: 2026-10-30

Spring Boot collects configuration from many sources into one `Environment` and lets later sources override earlier ones. OS environment variables rank above every `application.properties` and `application-{profile}.properties` file, and are matched to property names by relaxed binding: `spring.datasource.password` is read from `SPRING_DATASOURCE_PASSWORD`. Java system properties, `SPRING_APPLICATION_JSON` and command-line arguments rank above environment variables. Spring Boot doesn't read `.env` files. Values are bound when the application context starts. This page follows the [Spring Boot externalized configuration reference](https://docs.spring.io/spring-boot/reference/features/external-config.html) for Spring Boot 4.1.2, checked October 30, 2026.

## Where environment variables rank

The reference lists fifteen property sources from lowest to highest priority. The ones that matter for secrets:

| Rank (low to high) | Source                                                                 |
| ------------------ | ---------------------------------------------------------------------- |
| 1                  | default properties set in code                                         |
| 2                  | `@PropertySource` annotations                                          |
| 3                  | config data: `application.properties`, YAML and profile-specific files |
| 4                  | `random.*` values                                                      |
| 5                  | **OS environment variables**                                           |
| 6                  | Java system properties, `-Dname=value`                                 |
| 7                  | JNDI attributes                                                        |
| 8, 9               | servlet init parameters                                                |
| 10                 | `SPRING_APPLICATION_JSON`                                              |
| 11                 | command-line arguments, `--name=value`                                 |
| 12 to 15           | test annotations and devtools global settings                          |

*Figure: Diagram of how Spring Boot loads environment variables. Env files are read in this order of priority, and the first one wins: application-{profile}.properties and application.properties. Values are read from the environment when the app starts. There is no public prefix: nothing reaches the browser unless your code sends it.*

Within config data, profile-specific files override plain ones, and files outside the jar override files packaged inside it. If `.properties` and YAML sit in the same place, `.properties` wins. None of this changes the main point: a real environment variable beats every file, and a `--spring.datasource.password=…` argument beats the environment variable. That last rule surprises people when an old start script still passes a password on the command line, where it's also visible in the process list.

## Relaxed binding: from property name to variable name

Environment variables can't contain dots, so Spring Boot converts names. The documented rules:

- Replace dots with underscores.
- Remove dashes.
- Use upper case.

| Property                       | Environment variable         |
| ------------------------------ | ---------------------------- |
| `spring.datasource.password`   | `SPRING_DATASOURCE_PASSWORD` |
| `spring.main.log-startup-info` | `SPRING_MAIN_LOGSTARTUPINFO` |
| `my.service[0].other`          | `MY_SERVICE_0_OTHER`         |

The dash rule is where most mistakes happen. `SPRING_MAIN_LOG_STARTUP_INFO` looks right and doesn't bind. For lists, surround the index with underscores. For maps, remember that Spring Boot lowercases the variable name before binding, so `MY_PROPS_VALUES_KEY=VALUE` produces the map entry `key` with the value `VALUE`. Only the name is lowercased.

Always refer to properties in their canonical kebab-case form in placeholders. `${demo.item-price}` picks up `DEMO_ITEMPRICE` from the environment, and so does `${demo.itemPrice}`, but the reference notes the two forms aren't equivalent for file-based sources.

## Profiles

Profiles select extra config files. `spring.profiles.active=prod` loads `application-prod.properties` on top of `application.properties`. As an environment variable it's `SPRING_PROFILES_ACTIVE`. With several profiles, such as `prod,live`, the last one wins where they overlap. Without an active profile, the `default` profile applies.

Profiles are good for non-secret differences, like connection pool sizes or feature flags per stage. Secrets are better kept out of profile files entirely, because those files usually live in the repository or inside the jar. Read secrets from environment variables, which outrank the files anyway.

## Other ways to bring secrets in

- **Config trees.** `spring.config.import=optional:configtree:/run/secrets/` turns each file in that directory into a property named after the file. That's how Kubernetes and Docker mount secrets, and values can bind to `String` or `byte[]`.
- **`SPRING_APPLICATION_JSON`.** A whole JSON document in one variable. It ranks above ordinary environment variables, and its `null` values can't override lower sources.
- **An environment prefix.** `SpringApplication.setEnvironmentPrefix("orders")` makes the app read `ORDERS_REMOTE_TIMEOUT` instead of `REMOTE_TIMEOUT`, which helps when several Spring Boot apps share one environment. The prefix applies only to environment variables.

## Moving secrets out of properties files

Most Spring Boot projects start with a password or two typed straight into `application.properties` or `application-prod.yml`. Moving them out is mostly deletion, because environment variables already outrank the files:

1. **Find every secret in config data.** Search all `application*.properties` and YAML files, including ones under `src/test`, for passwords, tokens, keys and connection strings with credentials in them.
2. **Pick a name for each.** Either the relaxed binding name, such as `SPRING_DATASOURCE_PASSWORD`, or a shorter name of your own referenced by a placeholder: `spring.datasource.password=${DB_PASSWORD}`. Placeholders resolve against the whole `Environment`, so the variable fills them.
3. **Delete the value from the file.** Leave the property out, or leave the placeholder. A missing variable then fails at startup instead of silently using an old password.
4. **Reissue what was committed.** A value that sat in a file in Git is in the history of every clone. Moving it doesn't make it secret again; replacing it at the provider does.

Non-secret settings can stay in the files, where they're reviewed with the code.

## Run Spring Boot with values from Crunu

`crunu run` decrypts a Crunu environment in memory and starts your command with those variables in its process environment. Spring Boot sees them as OS environment variables, rank 5, above every properties file. No `.env` support is needed, and the names simply follow the relaxed binding rules.

1. Store values under their environment names, such as `SPRING_DATASOURCE_URL`, `SPRING_DATASOURCE_PASSWORD` and `SPRING_PROFILES_ACTIVE`, in Crunu environments like `orders/development` and `orders/production`.
2. Develop with `crunu run --env orders/development --inherit-env -- ./mvnw spring-boot:run`, or the Gradle equivalent `./gradlew bootRun`.
3. Build the jar in CI without secrets, since Spring Boot binds properties at startup rather than at build time.
4. On a Linux server with the agent, start `crunu run --env orders/production --restart on-change -- java -jar /opt/orders/orders.jar`.

Why `--inherit-env` in step 2? By default `crunu run` gives the program a short allowlist from your shell, such as `PATH`, `HOME` and `LANG`, plus the Crunu variables. `JAVA_HOME`, `MAVEN_OPTS` and `GRADLE_USER_HOME` are not on it, and the Maven and Gradle wrappers may depend on them on your laptop. `--inherit-env` passes your whole shell. On a server, prefer the clean environment and put `JAVA_HOME` or JVM options into the production environment explicitly, so the service doesn't depend on whoever's shell started it.

With `--restart on-change`, `crunu` sends `SIGTERM` after a value changes, waits up to 30 seconds for the JVM to shut down, and starts it again. If the application uses graceful shutdown, in-flight requests can finish within that window. Make sure shutdown completes in time, or raise `--stop-timeout`.

Two habits to drop when you move: passing secrets as `--spring...` arguments, which outrank the environment and show up in the process list, and keeping a local `application-local.properties` with real passwords, which tends to get committed sooner or later.

## Related reading

- [Go](https://crunu.com/environment-variables/go) for a runtime with no configuration layers at all.
- [Docker Compose](https://crunu.com/environment-variables/docker-compose) for config trees from Compose secrets.
- [Render](https://crunu.com/environment-variables/render) and [Railway](https://crunu.com/environment-variables/railway) for platform-held variables.
- [How to share .env files with your team](https://crunu.com/guides/share-env-files-with-a-team) and [crunu run](https://crunu.com/docs/cli/run).

## FAQ

### Does an environment variable override application.properties in Spring Boot?

Yes. OS environment variables rank above all config data files, including profile-specific ones inside and outside the jar. Java system properties, SPRING_APPLICATION_JSON and command-line arguments rank higher still.

### How do I set spring.datasource.password with an environment variable?

Replace dots with underscores, remove dashes and use upper case: SPRING_DATASOURCE_PASSWORD.

### How do I choose a profile with an environment variable?

Set SPRING_PROFILES_ACTIVE, the environment form of spring.profiles.active. With several profiles, the last one wins where they define the same property.

### What is a config tree?

A directory where each file name is a property name and each file's content is its value, as Kubernetes and Docker mount secrets. Import it with spring.config.import and the configtree prefix, for example for /run/secrets.

## Sources

- [Spring Boot: Externalized configuration](https://docs.spring.io/spring-boot/reference/features/external-config.html) (accessed 2026-10-30)
