Spring Boot application.properties Explained — A Complete Beginner's Guide
- application.properties lives in src/main/resources and is loaded by Spring Boot at startup by convention — no registration required. Defining spring.datasource.url in this file triggers full auto-configuration of the DataSource, connection pool, and JPA stack.
- Use @Value("${key:default}") for single property injection and always include the colon-default fallback — without it, a missing property crashes the application at startup with a generic error message that does not name the missing key clearly.
- @ConfigurationProperties with @Validated is the production standard for grouped settings — it gives you a typed POJO, Relaxed Binding for all naming conventions, JSR-303 validation with clear field-specific startup errors, and IDE autocomplete with the configuration-processor dependency.
- application.properties is the externalized instruction sheet Spring Boot reads at startup — separate logic from environment without recompiling
- Spring loads properties in priority order: command-line args > OS env vars > profile-specific files > base application.properties
- @Value("${key:default}") injects single properties with a fallback — without the colon default, a missing key crashes the app at startup with no grace
- @ConfigurationProperties groups related settings into a type-safe POJO with Relaxed Binding — kebab-case, snake_case, and UPPER_SNAKE_CASE all map to camelCase fields automatically
- Profile files (application-prod.properties) overlay the base file — only conflicting keys are overridden, everything else is inherited
- Add @Validated to your @ConfigurationProperties class to fail-fast at startup with clear error messages when required properties are missing or malformed
- spring.config.import (Spring Boot 2.4+) replaces spring.config.additional-location for importing external config files and Vault/Config Server integration
- The biggest mistake: hardcoding spring.profiles.active=prod in the base file — your CI pipeline deploys prod settings everywhere and wipes test data
Need to see what properties are actually loaded at runtime
curl -s http://localhost:8080/actuator/env | jq '.propertySources[] | select(.name | contains("application"))'curl -s http://localhost:8080/actuator/env/server.port | jq .Need to verify which profile is active on a running instance
curl -s http://localhost:8080/actuator/env | jq '.activeProfiles'printenv SPRING_PROFILES_ACTIVEYAML file causing parse errors or wrong values at startup
cat -A src/main/resources/application.yml | grep '^I'python3 -c "import yaml; yaml.safe_load(open('src/main/resources/application.yml'))"Database connection failing — need to verify the resolved datasource URL
curl -s http://localhost:8080/actuator/env/spring.datasource.url | jq '.property'curl -s http://localhost:8080/actuator/health/db | jq .Production Incident
Production Debug GuideWhen Spring Boot configuration behaves unexpectedly, here is how to go from observable symptom to resolution.
In professional software engineering, hardcoding is a cardinal sin. Embedding your database password or server port directly into Java source code creates a fragile system that requires a full recompile just to change a timeout value. This is where externalized configuration becomes critical.
Spring Boot's application.properties serves as the control center for your entire application. It separates the logic of your code from the environment it runs in. Whether deploying to a local Docker container or a Kubernetes cluster, the code stays the same — only the properties change.
Misconfiguring properties causes production failures that are silent and genuinely hard to diagnose. A misplaced space around the equals sign silently ignores your config change. A missing environment variable crashes the app at 2 AM when the fallback was never defined. Using tabs instead of spaces in a YAML file misparses your entire datasource configuration without throwing a startup error. These are not theoretical edge cases — they are the class of incidents that generate post-mortems.
This guide covers property injection patterns, environment profiles, the fail-fast validation strategy that catches misconfiguration at startup rather than at runtime, and the configuration management approaches used in production systems that handle secrets without committing them to version control.
What application.properties Actually Is and Where It Lives
Spring Boot follows a strict priority order for loading configuration. By default, it looks in src/main/resources/application.properties. When your build tool packages your app, this file is moved to the root of the classpath inside your JAR. Spring Boot finds it by convention — you do not register it anywhere.
The syntax is a flat key=value format. Spring uses these keys to auto-configure beans. Defining spring.datasource.url does not just store a string — it triggers Spring Boot's auto-configuration to create a DataSource bean, a HikariCP connection pool, and wire the entire JPA stack. You provide the parameters; Spring Boot writes the plumbing.
The priority chain matters more than most developers realize. A value in application.properties can be overridden by application-prod.properties, which can be overridden by an OS environment variable, which can be overridden by a JVM argument, which can be overridden by a command-line argument. Every environment variable your Kubernetes pod injects wins over anything in any file. Understanding this chain is what lets you debug configuration mismatches in under five minutes instead of four hours.
One critical detail the official docs bury: as of Spring Boot 2.4, the way additional config files are imported changed. The old spring.config.additional-location property still works, but the new spring.config.import is the forward-compatible approach and is required for Spring Cloud Config Server and Vault integration.
# ── io.thecodeforge: Standard Server Config ─────────────────────── server.port=8081 server.servlet.context-path=/api/v1 # ── Application Identity ────────────────────────────────────────── spring.application.name=forge-bookstore-service # ── Database Connection (PostgreSQL) ────────────────────────────── # ${DB_PASSWORD} resolves from OS environment variable. # The :default_dev_pass fallback only applies locally — never in prod. # In prod, DB_PASSWORD must be set explicitly or startup fails at datasource creation. spring.datasource.url=jdbc:postgresql://localhost:5432/forge_db spring.datasource.username=forge_admin spring.datasource.password=${DB_PASSWORD:default_dev_pass} spring.datasource.driver-class-name=org.postgresql.Driver # ── Connection Pool (HikariCP) ──────────────────────────────────── # These defaults are tuned for a small service — adjust for your load profile. # Too small: connection starvation under load. # Too large: DB server thread exhaustion. spring.datasource.hikari.maximum-pool-size=10 spring.datasource.hikari.minimum-idle=2 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000 # ── JPA / Hibernate ─────────────────────────────────────────────── spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect # validate: checks schema matches entities at startup — use in prod # update: modifies schema automatically — never use in prod # none: no schema management — use in prod with external migrations (Flyway/Liquibase) spring.jpa.hibernate.ddl-auto=validate # ── Logging Configuration ───────────────────────────────────────── logging.level.root=INFO logging.level.io.thecodeforge=DEBUG # ── Importing Additional Config (Spring Boot 2.4+) ──────────────── # Use spring.config.import to pull in extra config files or external sources. # For Vault: spring.config.import=vault:// # For Config Server: spring.config.import=configserver: # For local extra file: spring.config.import=optional:classpath:extra-config.properties # 'optional:' prefix means do not fail if the file does not exist. # spring.config.import=optional:classpath:extra-config.properties
INFO - HikariPool-1 - Starting with pool size 10
DEBUG - io.thecodeforge.repository: Executing SQL: SELECT * FROM books
INFO - Schema validation: 8 tables validated successfully
Injecting Properties Into Java Classes: @Value vs @ConfigurationProperties
Spring provides two mechanisms for getting property values into your Java code. @Value is the quick option for a single value. @ConfigurationProperties is the production option for any group of related settings.
@Value("${key:default}") injects a single property directly into a field. The colon-default syntax is not optional in production — without it, a missing property crashes the application at startup with IllegalArgumentException: Could not resolve placeholder. That crash at 2 AM during deployment is avoidable.
@ConfigurationProperties binds an entire property prefix to a typed Java class. It supports Relaxed Binding, which means forge.bookstore.page-size in the properties file maps to private int pageSize in your Java class automatically — no exact key matching required. It also supports JSR-303 validation via @Validated, which gives you explicit startup failures with clear messages when required properties are missing or out of range.
The IDE tooling difference is real and worth mentioning: if you add the spring-boot-configuration-processor dependency to your build, IntelliJ and VS Code generate autocomplete suggestions, type hints, and deprecation warnings for every field in your @ConfigurationProperties class. Without the processor, you are typing property keys blind. With it, you get the same IDE experience for custom properties that you get for Spring's built-in ones.
package io.thecodeforge.bookstore.config; import jakarta.validation.Valid; import jakarta.validation.constraints.Min; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.NotNull; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import org.springframework.validation.annotation.Validated; import lombok.Data; /** * io.thecodeforge: Type-safe configuration with validation. * * @Validated: activates JSR-303 validation at startup. * If any constraint fails, the app refuses to start with a clear error message. * This is the fail-fast pattern — catch misconfiguration at startup, not at runtime. * * Relaxed Binding examples (all resolve to the same field): * forge.bookstore.page-size=50 (kebab-case in .properties) * forge.bookstore.page_size=50 (snake_case) * FORGE_BOOKSTORE_PAGE_SIZE=50 (env var / UPPER_SNAKE_CASE) * forge.bookstore.pageSize=50 (camelCase) */ @Data @Component @Validated @ConfigurationProperties(prefix = "forge.bookstore") public class ForgeBookstoreProperties { // Default values apply when the property is not defined in any config file. // These should be safe, conservative defaults — not production tuning values. @Min(value = 1, message = "forge.bookstore.page-size must be at least 1") private int pageSize = 10; @NotBlank(message = "forge.bookstore.default-currency must not be blank") private String defaultCurrency = "USD"; // @Valid cascades validation into nested configuration objects. // Without @Valid here, the constraints inside Security are never evaluated. @Valid @NotNull(message = "forge.bookstore.security configuration block is required") private Security security = new Security(); @Data public static class Security { // App refuses to start if forge.bookstore.security.api-key is missing or blank. // No more null API keys silently reaching production. @NotBlank(message = "forge.bookstore.security.api-key must be configured") private String apiKey; @Min(value = 5, message = "Session timeout must be at least 5 minutes") private int sessionTimeoutMinutes = 30; // Pattern validation — api-key must match expected format // Uncomment when you have a known format to enforce: // @Pattern(regexp = "^[A-Za-z0-9\\-]{32,64}$", // message = "forge.bookstore.security.api-key must be 32-64 alphanumeric chars") } }
// forge.bookstore.page-size=50
// forge.bookstore.default-currency=EUR
// forge.bookstore.security.api-key=${BOOKSTORE_API_KEY}
// forge.bookstore.security.session-timeout-minutes=20
// If forge.bookstore.security.api-key is missing at startup:
// APPLICATION FAILED TO START
// Description: Binding to target forge.bookstore failed:
// Field error in object 'forge.bookstore' on field 'security.apiKey':
// rejected value [null]; codes [...]; default message
// [forge.bookstore.security.api-key must be configured]
//
// This error fires at startup — not on the first API call that uses the key.
// That is the difference between the fail-fast pattern and silent production failures.
Spring Boot Profiles — Environment-Specific Configuration Without Code Changes
Every application runs in multiple environments: your laptop, a CI runner, staging, and production. The database URLs differ. The log levels differ. The external service endpoints differ. Profiles let you express those differences in configuration files without touching the Java code or maintaining separate build artifacts.
Spring Boot loads files in this order: application.properties first as the baseline, then application-{profile}.properties as the overlay. Only keys that appear in both files are overridden — everything in the base file that is not mentioned in the profile file is inherited as-is. This means your profile files should be small and specific to what actually changes per environment.
Multiple profiles can be active simultaneously. SPRING_PROFILES_ACTIVE=prod,monitoring activates both the prod profile and a monitoring profile. Spring loads both overlay files. If the same key appears in both profile files, the last one wins in alphabetical order — which is why relying on multiple overlapping profiles for the same key is a maintenance trap.
The profile activation question is one of the most consistently asked in Spring Boot interviews: if the same property key appears in application.properties, application-prod.properties, AND an OS environment variable, what wins? The environment variable wins unconditionally. The full priority chain from highest to lowest is: command-line arguments, Java system properties (-D flags), OS environment variables, profile-specific property files, base application.properties.
# ── io.thecodeforge: Production Overrides ───────────────────────── # This file contains ONLY what differs in production. # Everything not listed here is inherited from application.properties. # Keeping this file small is a feature — it makes prod differences explicit. # Production runs on standard HTTPS port behind a load balancer server.port=8080 # Production RDS instance — URL comes from environment variable for security # SPRING_DATASOURCE_URL is set in the Kubernetes Secret and injected as env var spring.datasource.url=${SPRING_DATASOURCE_URL} spring.datasource.username=${SPRING_DATASOURCE_USERNAME} spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} # Production connection pool — sized for actual production load spring.datasource.hikari.maximum-pool-size=25 spring.datasource.hikari.minimum-idle=5 # Schema validation (not creation) — production schema is managed by Flyway spring.jpa.hibernate.ddl-auto=validate # No SQL logging in production — performance cost and log volume are not acceptable logging.level.root=WARN logging.level.io.thecodeforge=INFO spring.jpa.show-sql=false # Stricter session timeout in production forge.bookstore.security.session-timeout-minutes=15
INFO - HikariPool-1 - Starting with pool size 25
INFO - Connected to: jdbc:postgresql://rds.thecodeforge.io:5432/prod_db
INFO - Schema validation: 8 tables validated successfully
application.properties vs application.yml — Syntax, Trade-offs, and Failure Modes
Both formats configure the same Spring Boot beans. The properties file is a flat key=value list. The YAML file is a hierarchical structure that eliminates prefix repetition at the cost of indentation sensitivity. Neither is objectively better — each has a specific failure mode that the other does not.
The properties file failure mode: almost none. A trailing space on a key makes it unrecognizable, but the app starts and you notice the value is wrong. A missing equals sign causes a parse error with a clear line number. It is hard to silently corrupt a properties file.
The YAML failure mode: silent misconfiguration. A tab character instead of two spaces causes the YAML parser to misparse the structure. Properties end up under the wrong parent key. Spring may create a bean with a null URL. The app starts, no parse error is thrown, and every database call fails at runtime. This class of failure is significantly harder to diagnose than a startup crash.
YAML has one genuine superpower: multi-document support. Using --- as a separator, you can define multiple Spring profiles in a single application.yml file. In Spring Boot 2.4 and later, use spring.config.activate.on-profile inside the document block. This is a YAML-only feature — you cannot do it with properties files.
The practical advice: use properties files for simple, flat configuration where correctness matters more than readability. Use YAML for complex nested configuration in microservices where the visual hierarchy genuinely helps. If you choose YAML, add a CI linting step that catches tab characters before they reach any server.
# io.thecodeforge: YAML configuration reference # Equivalent to the application.properties example above, plus multi-profile demo. spring: application: name: forge-bookstore-service datasource: url: jdbc:postgresql://localhost:5432/forge_db username: forge_admin # Environment variable resolution works identically in YAML password: ${DB_PASSWORD:default_dev_pass} hikari: maximum-pool-size: 10 minimum-idle: 2 connection-timeout: 30000 jpa: hibernate: ddl-auto: validate database-platform: org.hibernate.dialect.PostgreSQLDialect show-sql: false server: port: 8081 servlet: context-path: /api/v1 logging: level: root: INFO io.thecodeforge: DEBUG forge: bookstore: page-size: 10 default-currency: USD security: session-timeout-minutes: 30 # ── Multi-document YAML: profile-specific overrides in one file ──── # The --- separator starts a new YAML document. # spring.config.activate.on-profile is the Spring Boot 2.4+ syntax. # Older spring.profiles syntax is deprecated as of 2.4 — do not use it. --- spring: config: activate: on-profile: dev server: port: 9090 logging: level: root: DEBUG org.hibernate.SQL: TRACE --- spring: config: activate: on-profile: prod server: port: 8080 spring: datasource: url: ${SPRING_DATASOURCE_URL} hikari: maximum-pool-size: 25 logging: level: root: WARN
# server.port resolves to: 9090 (from dev document)
# logging.level.root resolves to: DEBUG (from dev document)
# spring.application.name resolves to: forge-bookstore-service (inherited from base)
# Active profile: prod
# server.port resolves to: 8080 (from prod document)
# spring.datasource.url resolves to: value of SPRING_DATASOURCE_URL env var
# spring.application.name resolves to: forge-bookstore-service (inherited from base)
# No profile active:
# All base document values apply — port 8081, DEBUG logging for io.thecodeforge
Secrets Management and External Configuration: Beyond the Properties File
For applications beyond the hobby scale, storing secrets in properties files — even with environment variable placeholders — is not sufficient. Rotating a database password means updating an environment variable on every host, restarting every instance, and hoping nothing was cached. A secret manager like HashiCorp Vault, AWS Secrets Manager, or Spring Cloud Config Server provides centralized secret storage, automatic rotation, audit logs, and fine-grained access control.
Spring Boot 2.4 introduced spring.config.import as the standardized way to pull configuration from external sources. A single line in application.properties or application.yml pulls the entire secret set from Vault or Config Server at startup. If the external source is unreachable and the import is not marked optional, the application refuses to start — which is exactly the fail-fast behavior you want rather than starting with missing secrets.
For teams not ready for Vault or Config Server, Jasypt (Java Simplified Encryption) provides a middle ground: properties files that store AES-encrypted values instead of plaintext. The encryption key is injected at runtime via environment variable. This is not equivalent security to Vault, but it prevents credentials from being readable in any file committed to version control or stored on disk in plaintext.
The environment variable approach used throughout this guide is the minimum acceptable standard for production. The approaches in this section are what teams with actual security requirements implement after the environment variable baseline is working.
# ── io.thecodeforge: External Configuration Import Examples ─────── # --- Spring Cloud Config Server --- # Imports all properties from Config Server at startup. # Config Server URL is typically set via SPRING_CONFIG_URI env var. # If Config Server is unreachable, startup fails immediately (fail-fast). spring.config.import=configserver: # --- HashiCorp Vault --- # Imports secrets from Vault path secret/forge-bookstore. # Requires spring-cloud-starter-vault-config dependency. # spring.config.import=vault:// # spring.cloud.vault.uri=http://vault.internal:8200 # spring.cloud.vault.token=${VAULT_TOKEN} # spring.cloud.vault.kv.enabled=true # spring.cloud.vault.kv.default-context=forge-bookstore # --- Optional file import --- # Import an additional file if it exists — do not fail if absent. # Useful for local developer overrides that are .gitignored. # spring.config.import=optional:file:./local-override.properties # --- AWS Parameter Store (Spring Cloud AWS) --- # spring.config.import=aws-parameterstore: # spring.cloud.aws.parameterstore.enabled=true # ── Jasypt Encryption (middle-ground secret management) ─────────── # Encrypted values are stored as ENC(encryptedString). # The encryption key is injected at runtime — never stored in the file. # jasypt.encryptor.password=${JASYPT_ENCRYPTOR_PASSWORD} # # Example encrypted datasource password: # spring.datasource.password=ENC(Gb3V+Kj7Mc2VzXP4q8Rn5A==) # Jasypt decrypts this at startup using the key from JASYPT_ENCRYPTOR_PASSWORD. # Without the key, the encrypted string is meaningless — safe to commit.
INFO - Fetching config from server at: http://config-server:8888
INFO - Located environment: name=forge-bookstore-service, profiles=[prod], label=main
INFO - 12 properties loaded from Config Server
INFO - Starting forge-bookstore-service with config from Config Server
# If Config Server is unreachable and import is NOT optional:
APPLICATION FAILED TO START
Description: Could not locate PropertySource and the resource is not optional
Action: Check that Config Server is accessible at http://config-server:8888
| Feature / Aspect | application.properties | application.yml |
|---|---|---|
| Format style | Flat key=value pairs, one per line. spring.datasource.url=... repeated for every datasource key. | Indented YAML hierarchy. spring.datasource is written once; url, username, password nest beneath it. |
| Readability — simple config | Excellent — the format is immediately obvious to any developer regardless of YAML experience. | Slightly more verbose for simple flat configs — the hierarchy adds keystrokes without adding clarity. |
| Readability — nested config | Gets repetitive fast. Five datasource keys means writing spring.datasource five times. | Excellent — hierarchy is visual and matches the logical grouping of the settings. |
| Multiple profiles in one file | Not possible — you must use separate files: application-dev.properties, application-prod.properties. | Yes, using the --- document separator with spring.config.activate.on-profile (Spring Boot 2.4+ syntax). |
| Risk of silent misconfiguration | Very low — trailing space on key is detectable. Parse errors report a clear line number. | Real risk — a tab character silently misparses the structure. Properties end up under wrong parent keys with no startup error. |
| Lists and arrays | Indexed: my.list[0]=a, my.list[1]=b. Comma-separated: my.list=a,b,c also works for simple lists. | Clean YAML list syntax: use hyphen-prefixed items under the key. More readable for multi-item lists. |
| IDE autocomplete support | Full support in IntelliJ and VS Code with spring-boot-configuration-processor on the classpath. | Full support in IntelliJ and VS Code with spring-boot-configuration-processor on the classpath. |
| Default in Spring Initializr | Yes — generated by default when creating a new project. | Optional — select during project setup on start.spring.io. |
| Recommended for beginners | Yes — simpler syntax and far harder to break silently. | Better once you understand indentation rules and have a tab-detection step in your workflow. |
| Spring Boot 2.4+ multi-profile syntax | Not applicable — profile activation is always per-file. | Use spring.config.activate.on-profile. The old spring.profiles syntax is deprecated in 2.4 and removed in Spring Boot 3.0. |
| Team preference trend | Common in mature codebases and teams prioritizing configuration correctness over visual hierarchy. | Increasingly preferred in new microservice projects where nested configuration is the norm. |
🎯 Key Takeaways
- application.properties lives in src/main/resources and is loaded by Spring Boot at startup by convention — no registration required. Defining spring.datasource.url in this file triggers full auto-configuration of the DataSource, connection pool, and JPA stack.
- Use @Value("${key:default}") for single property injection and always include the colon-default fallback — without it, a missing property crashes the application at startup with a generic error message that does not name the missing key clearly.
- @ConfigurationProperties with @Validated is the production standard for grouped settings — it gives you a typed POJO, Relaxed Binding for all naming conventions, JSR-303 validation with clear field-specific startup errors, and IDE autocomplete with the configuration-processor dependency.
- Profile files overlay the base file for conflicting keys — keep them small and specific to what actually changes per environment. Never hardcode spring.profiles.active in the base file. Set it via SPRING_PROFILES_ACTIVE environment variable on each server.
- Spring Boot's property resolution hierarchy is: command-line arguments > Java system properties > OS environment variables > profile-specific files > base application.properties. Environment variables always win over properties files — this is by design, not a quirk.
- YAML is indentation-sensitive — a single tab character silently misparses your entire configuration with no startup error. Properties files are nearly impossible to break silently. If you use YAML, add tab detection to your CI linting step.
- spring.config.import (Spring Boot 2.4+) is the forward-compatible way to load external configuration from Config Server, Vault, and AWS Parameter Store. Mark imports as optional: only in local development — in production, an unreachable secret source should crash startup, not allow a running application with no credentials.
- Add spring-boot-configuration-processor as an optional compile dependency when using @ConfigurationProperties — it generates IDE autocomplete metadata that catches property name typos before they become null-value debugging sessions.
⚠ Common Mistakes to Avoid
Interview Questions on This Topic
- QWhat is the difference between @Value and @ConfigurationProperties? When would you choose one over the other?Mid-levelReveal
- QHow does Spring Boot resolve properties? If the same key is in application.properties and a system environment variable, which one takes priority?Mid-levelReveal
- QA Spring Boot application is not picking up changes in application-prod.properties. What are the first three things you would check?Mid-levelReveal
- QExplain Relaxed Binding in Spring Boot and give an example of how it applies to configuration properties.Mid-levelReveal
- QHow can you use @Validated with @ConfigurationProperties to ensure your application fails to start if a required property is missing or malformed?SeniorReveal
- QWhat changed about external configuration loading in Spring Boot 2.4, and what is the difference between spring.config.import and spring.config.additional-location?SeniorReveal
Frequently Asked Questions
Where exactly does application.properties go in a Spring Boot project?
It goes in src/main/resources. When Maven or Gradle builds the project, everything in src/main/resources is copied to the root of the output JAR. Spring Boot knows to look there by convention — you do not register the file anywhere. If you need a developer-local override that should not be committed to Git, you can create application-local.properties in the same directory, add it to .gitignore, and activate it with SPRING_PROFILES_ACTIVE=local. Only properties that differ locally need to be in that file.
Can I have both application.properties and application.yml in the same project?
Technically yes, Spring Boot loads both. If the same key appears in both files, the .properties file takes precedence over .yml. This precedence is non-obvious and creates a class of debugging problems where a YAML change has no effect because the properties file is silently overriding it. Pick one format for your project, delete the other, and document the choice in your project README. The only exception is during a migration from one format to the other — but that migration should be a single commit that removes the old format entirely.
How do I use an environment variable inside application.properties?
Use ${VARIABLE_NAME} syntax directly: spring.datasource.url=${DB_URL}. Spring resolves this against OS environment variables at startup. You can add a fallback for local development: spring.datasource.url=${DB_URL:jdbc:h2:mem:testdb}. The fallback after the colon is used when the environment variable is not set. In production, the environment variable should always be set — if DB_URL is absent and you have no fallback, the application fails at startup, which is the correct behavior for a missing production credential.
Can I use Spring Boot Actuator to view my current properties at runtime?
Yes. Enable /actuator/env in your exposure list and hit the endpoint. It shows every resolved property value and, critically, which source provided it — applicationConfig:[prod], systemEnvironment, commandLineArgs, and so on. This is the fastest way to diagnose configuration mismatches: if the value is wrong, the source field tells you exactly which configuration layer to fix. Secure this endpoint with ADMIN role in your SecurityFilterChain — it can expose database URLs and other sensitive configuration paths to anyone who can reach it.
What happens if I use a tab instead of spaces in application.yml?
YAML requires spaces for indentation — tabs are not valid YAML syntax. A tab character causes the YAML parser to misparse the document structure. Depending on where the tab appears, properties end up under the wrong parent key, are silently ignored, or the entire document fails to parse. The particularly dangerous outcome is when the parser does not throw an error — the application starts, the datasource URL is null under the wrong key, and every database call fails at runtime rather than at startup. Always use 2-space indentation and run cat -A application.yml | grep '^I' to detect tabs before committing.
How do I set a profile without changing application.properties?
Three ways, from most to least preferred: (1) Set the environment variable SPRING_PROFILES_ACTIVE=prod on the server, in Docker via -e, or in Kubernetes via env in the pod spec — this is the standard for production deployments. (2) Pass it as a JVM argument: java -Dspring.profiles.active=prod -jar app.jar — useful for local testing of profile behavior. (3) Pass it as a command-line argument: java -jar app.jar --spring.profiles.active=prod — highest priority, overrides everything. Never set spring.profiles.active in the base application.properties file — it short-circuits environment variable control and affects every environment that loads the base file.
What is the difference between spring.config.import and spring.config.additional-location?
spring.config.additional-location (pre-2.4) loads additional file-based configuration sources and adds them to the property source list. It works only with files and classpath resources. spring.config.import (Spring Boot 2.4+) is the replacement and works with files, URL sources, and registered ConfigDataLoader implementations — including Spring Cloud Config Server (configserver:), HashiCorp Vault (vault://), and AWS Parameter Store. For any integration with external secret managers or configuration servers, spring.config.import is required. The optional: prefix controls fail-fast behavior: without it, startup fails if the source is unreachable — which is the correct production behavior.
Developer and founder of TheCodeForge. I built this site because I was tired of tutorials that explain what to type without explaining why it works. Every article here is written to make concepts actually click.