Spring Boot application.properties Explained — A Complete Beginner's Guide
Every real-world application needs configuration. A database URL, a server port, an API key, a timeout value — these details change depending on whether your app is running on your laptop, a test server, or live in production. If you hardcode those values directly into your Java classes, you have to recompile and redeploy every time something changes. That's slow, risky, and the kind of thing that causes 2 AM incidents.
Spring Boot solves this with a single, centrally-located file called application.properties (or its close cousin application.yml). Your app reads this file at startup and uses the values inside to configure itself — the database it connects to, the port it listens on, how verbose its logging should be, and hundreds of other things. You never touch the Java source code to make those changes. You just update the file.
By the end of this article you'll know exactly what application.properties is, how to write your own custom properties, how to inject those values into your Java classes, how to switch between development and production configurations using profiles, and the exact mistakes beginners make that cause silent failures. You'll also have three real interview questions with model answers ready to go.
What application.properties Actually Is and Where It Lives
When Spring Boot starts up, one of the very first things it does is go looking for configuration. By convention — and Spring Boot is all about sensible conventions — it looks in a specific place: src/main/resources/application.properties. If the file exists, Spring Boot reads every line and loads the key-value pairs into something called the Environment. From that moment on, any part of your application can ask the Environment for a value by its key.
The format is beautifully simple. Every line is a key on the left, an equals sign in the middle, and a value on the right. No quotes needed around strings. No commas between entries. Just one property per line.
Spring Boot also ships with hundreds of built-in property keys that control its own behaviour — things like server.port (which port the embedded web server listens on) and spring.datasource.url (where your database lives). You don't have to memorise all of them. The official Spring Boot documentation has a full reference list, and your IDE will autocomplete them for you. What matters right now is understanding the mechanic: key equals value, one per line, file in src/main/resources.
# ── Server Configuration ────────────────────────────────────────── # Tell the embedded Tomcat server to listen on port 8081 # (default is 8080, but 8080 is often already taken on dev machines) server.port=8081 # ── Application Identity ────────────────────────────────────────── # A human-readable name for your application # Spring Boot displays this in startup logs spring.application.name=bookstore-api # ── Database Connection ─────────────────────────────────────────── # The JDBC URL tells Spring where your database lives spring.datasource.url=jdbc:postgresql://localhost:5432/bookstore_db # The database username (never use 'root' in production) spring.datasource.username=bookstore_user # The database password # We'll learn how to keep this out of source control shortly spring.datasource.password=securepassword123 # ── JPA / Hibernate ─────────────────────────────────────────────── # Print every SQL statement to the console — great for debugging, # turn this OFF in production (it floods your logs) spring.jpa.show-sql=true # ── Logging Level ───────────────────────────────────────────────── # Set the log level for your own packages to DEBUG so you can see # detailed output while you're developing logging.level.com.thecodeforge.bookstore=DEBUG
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.2.0)
INFO - Starting bookstore-api on port 8081
INFO - Connected to PostgreSQL at localhost:5432/bookstore_db
INFO - Started BookstoreApiApplication in 2.341 seconds
Injecting Your Own Custom Properties Into Java Classes
Built-in Spring Boot properties are convenient, but the real power comes from defining your own. Imagine you're building a bookstore API and you want to control how many results appear per page, or store the URL for a third-party payment service. You don't want those values buried in your code. You want them in application.properties so you can change them without touching Java.
You can inject a property value into any Spring-managed bean using the @Value annotation. You give it the property key wrapped in ${ }, and Spring will look up the value at startup and assign it to the field. If the key doesn't exist, Spring throws an exception immediately — which is actually a good thing. It means misconfiguration fails loudly and fast, not silently halfway through a request.
For more complex configuration — like a group of related properties that belong together — Spring Boot offers @ConfigurationProperties, which maps a whole prefix of properties onto a plain Java object. This is cleaner than having ten separate @Value annotations scattered around a class. Both approaches work; @Value is fine for one or two values, @ConfigurationProperties is the professional choice when you have a logical group.
Let's see both in action with a realistic bookstore example.
// ───────────────────────────────────────────────────────────────────────────── // FILE 1: application.properties (in src/main/resources) // ───────────────────────────────────────────────────────────────────────────── // // bookstore.page-size=20 // bookstore.currency=GBP // bookstore.payment.api-url=https://api.payments.example.com/v2 // bookstore.payment.timeout-seconds=30 // // ───────────────────────────────────────────────────────────────────────────── // FILE 2: BookstoreProperties.java // Using @ConfigurationProperties to map a whole group of related properties // onto a single, type-safe Java object. This is the recommended approach // for anything more than a single isolated value. // ───────────────────────────────────────────────────────────────────────────── package com.thecodeforge.bookstore.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; // @ConfigurationProperties(prefix = "bookstore") means: // "Go find every property that starts with 'bookstore.' and map it onto // the fields of this class." The prefix is stripped — so 'bookstore.currency' // maps to the field named 'currency'. @Component @ConfigurationProperties(prefix = "bookstore") public class BookstoreProperties { // Maps to bookstore.page-size=20 in application.properties // Spring Boot auto-converts kebab-case (page-size) to camelCase (pageSize) private int pageSize; // Maps to bookstore.currency=GBP private String currency; // Spring Boot maps nested properties to nested objects automatically. // bookstore.payment.api-url maps to payment.apiUrl below. private Payment payment = new Payment(); // ── Getters and setters are REQUIRED for @ConfigurationProperties ───────── // Spring uses them to inject the values. Lombok's @Data works too. public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } public String getCurrency() { return currency; } public void setCurrency(String currency) { this.currency = currency; } public Payment getPayment() { return payment; } public void setPayment(Payment payment) { this.payment = payment; } // Nested static class for the 'bookstore.payment.*' group of properties public static class Payment { private String apiUrl; private int timeoutSeconds; public String getApiUrl() { return apiUrl; } public void setApiUrl(String apiUrl) { this.apiUrl = apiUrl; } public int getTimeoutSeconds() { return timeoutSeconds; } public void setTimeoutSeconds(int timeoutSeconds) { this.timeoutSeconds = timeoutSeconds; } } } // ───────────────────────────────────────────────────────────────────────────── // FILE 3: BookSearchService.java // Demonstrates BOTH @Value (for a single property) and injecting // the BookstoreProperties object (for the full group) // ───────────────────────────────────────────────────────────────────────────── package com.thecodeforge.bookstore.service; import com.thecodeforge.bookstore.config.BookstoreProperties; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @Service public class BookSearchService { // @Value injects a single property directly. // The ":GBP" part is a DEFAULT VALUE — if 'bookstore.currency' is missing // from application.properties, Spring uses "GBP" instead of crashing. @Value("${bookstore.currency:GBP}") private String currency; // Injecting the whole BookstoreProperties object via constructor injection // (preferred over field injection — easier to test) private final BookstoreProperties bookstoreProperties; public BookSearchService(BookstoreProperties bookstoreProperties) { this.bookstoreProperties = bookstoreProperties; } public void printConfiguration() { System.out.println("=== Bookstore Configuration ==="); System.out.println("Currency : " + currency); System.out.println("Page size : " + bookstoreProperties.getPageSize()); System.out.println("Payment URL : " + bookstoreProperties.getPayment().getApiUrl()); System.out.println("Timeout (sec): " + bookstoreProperties.getPayment().getTimeoutSeconds()); } }
Currency : GBP
Page size : 20
Payment URL : https://api.payments.example.com/v2
Timeout (sec): 30
Spring Boot Profiles — Running Different Configs for Dev, Test and Production
Here's a problem every developer hits quickly: your local database is on localhost, but production is on a cloud server with a completely different URL. You can't have both in the same application.properties at the same time.
Spring Boot solves this with Profiles. A profile is just a label — 'dev', 'test', 'prod', whatever you like. You create a separate properties file for each profile following the naming pattern application-{profilename}.properties. When your app starts with a particular profile active, Spring Boot loads application.properties first (as a baseline) and then loads the profile-specific file on top, overriding anything that conflicts.
Think of it like a recipe with variations. The base recipe (application.properties) defines everything that's the same across all environments — the app name, the page size, the log format. The variation sheet (application-prod.properties) just lists what's different in production — the real database URL, a quieter log level, SSL enabled.
You activate a profile in three ways: by setting spring.profiles.active in the base application.properties (convenient for dev), by passing a JVM argument when you launch the app (-Dspring.profiles.active=prod), or by setting an environment variable (SPRING_PROFILES_ACTIVE=prod). In real deployments the environment variable approach is most common because it keeps the active profile entirely outside of the code.
# ───────────────────────────────────────────────────────────────────────────── # FILE 1: application.properties (the shared baseline — every environment uses this) # ───────────────────────────────────────────────────────────────────────────── # # spring.application.name=bookstore-api # bookstore.page-size=20 # bookstore.currency=GBP # # Set which profile is active during LOCAL development. # In CI/CD pipelines and production servers, NEVER set this here. # Use an environment variable instead: SPRING_PROFILES_ACTIVE=prod # spring.profiles.active=dev # # ───────────────────────────────────────────────────────────────────────────── # FILE 2: application-dev.properties (only loaded when profile 'dev' is active) # ───────────────────────────────────────────────────────────────────────────── # # Local developer machine settings # # server.port=8081 # spring.datasource.url=jdbc:h2:mem:bookstore_dev_db # spring.datasource.username=sa # spring.datasource.password= # spring.jpa.show-sql=true # logging.level.com.thecodeforge.bookstore=DEBUG # # ───────────────────────────────────────────────────────────────────────────── # FILE 3: application-prod.properties (only loaded when profile 'prod' is active) # ───────────────────────────────────────────────────────────────────────────── # Production runs on the standard HTTPS port server.port=443 # Real PostgreSQL database in the cloud. # Notice the URL, username and password are DIFFERENT from dev. spring.datasource.url=jdbc:postgresql://prod-db.us-east-1.rds.amazonaws.com:5432/bookstore_prod spring.datasource.username=bookstore_prod_user # In real production you'd reference an environment variable here: # spring.datasource.password=${DB_PASSWORD} spring.datasource.password=${DB_PASSWORD} # Turn OFF SQL logging in production — it's a security risk and noise spring.jpa.show-sql=false # Only show WARN and ERROR in production logs — DEBUG is too verbose logging.level.com.thecodeforge.bookstore=WARN logging.level.root=WARN
#
# You'll see this in the startup log:
#
# INFO - The following 1 profile is active: "prod"
# INFO - Starting bookstore-api
# INFO - Connected to PostgreSQL at prod-db.us-east-1.rds.amazonaws.com:5432
# INFO - Tomcat started on port(s): 443 (https)
# INFO - Started BookstoreApiApplication in 3.102 seconds
#
# Notice: NO debug SQL statements, NO debug log lines — only WARN+ logs appear.
application.properties vs application.yml — Which One Should You Use?
You'll often see Spring Boot projects using application.yml instead of application.properties. Both do exactly the same job — they're just different formats for writing the same key-value configuration. Spring Boot supports both out of the box, and you can even have both files in the same project (though that's messy and not recommended).
The .properties format is dead simple: one key=value per line. It's been around since the early days of Java and everyone can read it instantly. The .yml (YAML) format uses indentation to represent hierarchy, which means you don't have to repeat the prefix on every line. Instead of writing spring.datasource.url, spring.datasource.username, spring.datasource.password three times, you write spring: then datasource: once, and nest the rest underneath.
For simple apps, properties is fine. For apps with deeply nested configuration — especially microservices with lots of grouped settings — YAML is noticeably cleaner. The gotcha with YAML is that indentation is meaningful. A single wrong space breaks the entire file with a confusing parse error. Properties never has that problem.
Most professional teams pick one and stick to it. The Spring Boot community has shifted toward YAML for new projects, but properties is in no way deprecated or going anywhere.
# ───────────────────────────────────────────────────────────────────────────── # SAME CONFIGURATION as the application.properties examples above, # but written in YAML format. # ───────────────────────────────────────────────────────────────────────────── # Notice how hierarchy replaces repeated prefixes. # 'spring.datasource.url' becomes: # spring: # datasource: # url: ... # Each level of indentation = one dot in the properties key. spring: application: name: bookstore-api # Same as: spring.application.name=bookstore-api datasource: url: jdbc:postgresql://localhost:5432/bookstore_db username: bookstore_user password: securepassword123 jpa: show-sql: true # Same as: spring.jpa.show-sql=true server: port: 8081 # Same as: server.port=8081 bookstore: page-size: 20 # Same as: bookstore.page-size=20 currency: GBP payment: api-url: https://api.payments.example.com/v2 timeout-seconds: 30 logging: level: com.thecodeforge.bookstore: DEBUG # ───────────────────────────────────────────────────────────────────────────── # Profile-specific YAML blocks — bonus YAML-only feature! # You can define multiple profiles in ONE yml file using '---' as a separator. # This is impossible with .properties files (each profile needs its own file). # ───────────────────────────────────────────────────────────────────────────── --- spring: config: activate: on-profile: prod # Everything below only applies to 'prod' profile datasource: url: jdbc:postgresql://prod-db.amazonaws.com:5432/bookstore_prod password: ${DB_PASSWORD} # Read from environment variable server: port: 443 logging: level: root: WARN
# The startup log looks the same — Spring Boot doesn't tell you which format
# it read. You can verify your config loaded correctly by enabling:
#
# management.endpoints.web.exposure.include=env
# management.endpoint.env.enabled=true
#
# Then hit: GET http://localhost:8081/actuator/env
# And you'll see every resolved property value in a JSON response.
| Feature / Aspect | application.properties | application.yml |
|---|---|---|
| Format style | key=value pairs, one per line | Indented YAML hierarchy |
| Readability (simple config) | Excellent — instantly obvious | Good, but extra keystrokes |
| Readability (nested config) | Gets repetitive (same prefix repeated) | Excellent — hierarchy is visual |
| Multiple profiles in one file | Not possible — one file per profile | Yes, using --- document separator |
| Risk of syntax errors | Very low — almost impossible to break | Indentation errors cause silent failures |
| IDE autocomplete support | Full support in IntelliJ and VS Code | Full support in IntelliJ and VS Code |
| Default in Spring Initializr | Yes (generated by default) | Optional — select during project setup |
| Recommended for beginners | Yes — simpler and harder to break | Better once you understand indentation rules |
| Lists / arrays | Verbose: key[0]=a, key[1]=b | Clean: use YAML list syntax (- item) |
| Team preference trend | Common in older codebases | Increasingly preferred in new projects |
🎯 Key Takeaways
- application.properties lives in src/main/resources and is automatically read by Spring Boot at startup — no setup code required. Every line is a key=value pair that configures some aspect of your application.
- Use @Value("${key:default}") for single property injection and always provide a default value after the colon — this prevents your app from crashing if the property is accidentally omitted.
- @ConfigurationProperties(prefix = "bookstore") is the professional choice for groups of related settings — it gives you a strongly-typed Java object instead of scattered @Value annotations, and your IDE can validate the values.
- Profile-specific files (application-dev.properties, application-prod.properties) override the base file for conflicting keys only. Activate profiles via environment variables in real deployments — never hardcode spring.profiles.active=prod in shared config files.
⚠ Common Mistakes to Avoid
- ✕Mistake 1: Putting a space before the equals sign — writing 'server.port =8081' instead of 'server.port=8081' — Spring Boot silently ignores the key and the port stays at 8080, leaving you confused why your config change had no effect. Fix it: Remove all whitespace around the equals sign. 'key=value' is correct. 'key = value' technically works but is inconsistent; 'key =value' does not.
- ✕Mistake 2: Forgetting to enable @ConfigurationProperties scanning — you write a beautiful @ConfigurationProperties class, run the app, and all the fields are null. This happens because without either @Component on the class or @EnableConfigurationProperties(YourClass.class) on a @Configuration class, Spring never processes it. Fix it: Add @Component directly to your @ConfigurationProperties class (easiest option) or add @EnableConfigurationProperties(BookstoreProperties.class) to your main application class.
- ✕Mistake 3: Activating the wrong profile by hardcoding spring.profiles.active=prod in application.properties and committing it — your CI pipeline deploys with prod settings to a dev environment and wipes test data. Fix it: Never set spring.profiles.active in the base application.properties file. Instead, set it as an environment variable (SPRING_PROFILES_ACTIVE=prod) on the server, or pass it as a JVM argument at startup (-Dspring.profiles.active=prod). The base file should only contain environment-agnostic defaults.
Interview Questions on This Topic
- QWhat is the difference between @Value and @ConfigurationProperties, and when would you choose one over the other?
- QHow does Spring Boot's profile mechanism work? Walk me through exactly which files get loaded and in what order when you start an app with spring.profiles.active=prod.
- QIf a property is defined in both application.properties and application-prod.properties, which value wins and why? What if you also pass the same property as a command-line argument — what happens then?
Frequently Asked Questions
Where exactly does application.properties go in a Spring Boot project?
It goes in the src/main/resources folder. When Maven or Gradle builds your project, anything in src/main/resources gets copied to the root of the output JAR, and Spring Boot knows to look there automatically. You don't need to register the file anywhere — Spring Boot finds it by convention.
Can I have both application.properties and application.yml in the same project?
Technically yes, Spring Boot loads both, but it's strongly discouraged. If the same key appears in both files, the .properties file takes precedence over .yml. Having two files for the same purpose creates confusion about which one is the source of truth. Pick one format for your project and stick with it.
How do I use an environment variable inside application.properties?
Use the ${VARIABLE_NAME} syntax directly in the file. For example: spring.datasource.password=${DB_PASSWORD}. When Spring Boot resolves that property, it looks up the DB_PASSWORD environment variable from the OS. You can also add a fallback: ${DB_PASSWORD:defaultpassword}. This is the standard way to keep secrets out of your codebase while still referencing them in configuration.
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.