Home Java Spring Boot application.properties Explained — A Complete Beginner's Guide

Spring Boot application.properties Explained — A Complete Beginner's Guide

⚡ Quick Answer
Imagine your app is a new employee on their first day. They need to know which office building to report to, what their login credentials are, and how many minutes they get for lunch. The application.properties file is exactly that — it's the instruction sheet you hand your Spring Boot app before it starts work. Instead of baking those instructions permanently into the code, you write them in one tidy file and the app reads them at startup. Change the file, change the behaviour — no rewriting code required.

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.

application.properties · PROPERTIES
123456789101112131415161718192021222324252627282930
# ── 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
▶ Output
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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
⚠️
Watch Out: Never Commit Passwords to GitThat spring.datasource.password line is fine for local development, but the moment you push it to GitHub your credentials are public forever (even if you delete the file later — git history is permanent). Use environment variables or Spring's @Value with a default, and add application.properties to .gitignore for any file that contains secrets.

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.

BookstoreConfig.java · JAVA
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101
// ─────────────────────────────────────────────────────────────────────────────
// 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());
    }
}
▶ Output
=== Bookstore Configuration ===
Currency : GBP
Page size : 20
Payment URL : https://api.payments.example.com/v2
Timeout (sec): 30
⚠️
Pro Tip: Always Add a Default to @ValueWrite @Value("${bookstore.currency:GBP}") instead of @Value("${bookstore.currency}"). The colon and the value after it is a fallback default. Without it, if someone forgets to add the property to application.properties, Spring throws an IllegalArgumentException at startup with a cryptic 'Could not resolve placeholder' message. Defaults make your app resilient to missing config.

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.

application-prod.properties · PROPERTIES
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849
# ─────────────────────────────────────────────────────────────────────────────
# 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
▶ Output
# When you start the app with: java -jar bookstore-api.jar -Dspring.profiles.active=prod
#
# 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.
🔥
Interview Gold: Profile Loading OrderInterviewers love asking 'what happens if the same property exists in both application.properties and application-prod.properties?' The answer: the profile-specific file WINS. Spring loads the base file first, then overlays the profile file on top. Only the conflicting keys get overridden — everything else from the base file is still active. This lets you keep a minimal base file and only list the differences per environment.

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.

application.yml · YAML
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859
# ─────────────────────────────────────────────────────────────────────────────
# 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
▶ Output
# application.yml produces identical behaviour to application.properties.
# 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.
⚠️
Pro Tip: The Multi-Document YAML TrickYAML lets you put ALL your profile configurations in a single application.yml file by separating them with three dashes (---). This is a YAML-only superpower — you can't do it with .properties. It's handy for small projects where you don't want multiple files, but for large teams with many environments, separate files per profile are often easier to manage in pull requests.
Feature / Aspectapplication.propertiesapplication.yml
Format stylekey=value pairs, one per lineIndented YAML hierarchy
Readability (simple config)Excellent — instantly obviousGood, but extra keystrokes
Readability (nested config)Gets repetitive (same prefix repeated)Excellent — hierarchy is visual
Multiple profiles in one fileNot possible — one file per profileYes, using --- document separator
Risk of syntax errorsVery low — almost impossible to breakIndentation errors cause silent failures
IDE autocomplete supportFull support in IntelliJ and VS CodeFull support in IntelliJ and VS Code
Default in Spring InitializrYes (generated by default)Optional — select during project setup
Recommended for beginnersYes — simpler and harder to breakBetter once you understand indentation rules
Lists / arraysVerbose: key[0]=a, key[1]=bClean: use YAML list syntax (- item)
Team preference trendCommon in older codebasesIncreasingly 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.

🔥
Naren Founder & Author

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.

← PreviousJava Agent and InstrumentationNext →Spring Boot Project Structure
Forged with 🔥 at TheCodeForge.io — Where Developers Are Forged