Configuration, profiles and environment variables
Deploying means running the same application in a different environment — production — with different configuration: a real database, real secrets, stricter settings, less logging. This lesson is how to configure a Spring Boot application per environment cleanly, so the same build runs everywhere and the production-specific values come from outside the code. It reprises the best-practices configuration lesson from the deployment angle: what actually changes when you go live.
The same build, different configuration
The foundational deployment principle: you build the application once and run that same artifact in every environment, feeding in different configuration each time. You do not build a "production version" of the code — you build one jar and give it production values. This is what makes deployments reproducible and rollbacks trivial: the artifact tested in staging is the exact one that runs in production, differing only in the configuration fed to it.
Spring Boot reads configuration from application.properties/.yml and lets environment variables and
command-line arguments override those, in a defined precedence (env vars and CLI args win). So the file
holds sensible defaults, and production overrides the per-environment and secret values from the
environment. The code never changes between environments.
Profiles for per-environment settings
Profiles give each environment its own configuration file:
application.properties # shared defaults
application-dev.properties # local development
application-prod.properties # production
Activate with SPRING_PROFILES_ACTIVE=prod (an environment variable, the usual production mechanism) or
--spring.profiles.active=prod. Spring loads application.properties plus the active profile's file, the
profile overriding. So production settings live in application-prod.properties:
# application-prod.properties
spring.jpa.hibernate.ddl-auto=validate # NOT update/create — Flyway owns the schema
spring.datasource.url=${DATABASE_URL} # from the environment
logging.level.root=INFO # less noise than dev's DEBUG
server.error.include-stack-trace=never # never leak stack traces to clients
The values that typically change for production: the database (a real PostgreSQL, from DATABASE_URL),
ddl-auto=validate (Flyway manages the schema — the migrations lesson), log levels (INFO not DEBUG), and
error verbosity (no stack traces to clients). Profiles keep these in one place per environment, with no
conditional code in the application.
Secrets from the environment, never committed
The security-critical rule (the best-practices lesson), restated for deployment: secrets come from the environment, never the committed files. The database password, the JWT signing key, API keys — these are injected as environment variables (or from a secrets manager) at deploy time and referenced in properties:
spring.datasource.password=${DB_PASSWORD}
dakiya.jwt.secret=${DAKIYA_JWT_SECRET}
The repository holds the references (${DB_PASSWORD}), the deployment environment holds the values. A
secret committed to application-prod.properties is a breach the moment it is pushed and lives in git
history forever. Set the real values as environment variables in your platform (or a secrets manager); keep
the committed files free of them.
The deployment configuration checklist
Before deploying, confirm the production configuration:
spring.profiles.active=prodset, so the production profile loads.- Database — a real PostgreSQL via
DATABASE_URL/credentials from the environment;ddl-auto=validate; Flyway enabled to manage the schema. - Secrets —
SECRETs and passwords from environment variables / a secrets manager, none committed. - Logging — appropriate levels (INFO+), and errors going to logs (the next lesson), not verbose to clients.
- Error responses —
include-stack-trace=never; clients get genericProblemDetails (the error-handling and security lessons). - Server — the port and any reverse-proxy/forward-header settings your platform needs.
Getting these right is the difference between a deploy that runs safely and one that leaks stack traces,
tries to auto-mutate the production schema, or fails to find its database. The principle throughout: one
build, environment-driven configuration, secrets from the environment, validate not update, and the
production profile carrying the differences — so going to production is a configuration change, not a
code change.
Check your work
One build, different config. Build once, run the same artifact everywhere with different fed-in values;
env vars / CLI args override application.properties (they win). Reproducible, easy rollback.
Profiles. application-{profile}.properties overrides the base for the active profile
(SPRING_PROFILES_ACTIVE=prod); production typically changes the database (real Postgres),
ddl-auto=validate, log levels (INFO), and error verbosity — no conditional code.
Secrets. From environment variables / a secrets manager, referenced as ${...} in properties, never
committed (a committed secret is a breach, forever in history).
The checklist. Prod profile active; real DB + validate + Flyway; secrets externalised; sensible
logging; no stack traces to clients; correct server/proxy settings.
Practice
- Create
application-prod.propertiesoverridingddl-autotovalidate, the datasource to a${DATABASE_URL}, and root logging to INFO; run withprodactive and confirm the values load. - Override a base property with an environment variable and confirm the env var wins.
- Reference a secret as
${DAKIYA_JWT_SECRET}and supply it via an environment variable; confirm no secret is in the committed files. - Set
server.error.include-stack-trace=never, trigger an error, and confirm no stack trace reaches the client. - Reason about why building one artifact and feeding config (rather than a "prod build") makes rollback trivial.
- Walk the deployment configuration checklist against a Dakiya
application-prod.properties.
Official documentation
- Spring Boot — Externalized Configuration — Property sources, precedence, environment overrides.
- Spring Boot — Profiles — Per-environment configuration.
- Spring Boot — Deployment — Running the built artifact in production.
Next: Actuator, health checks and useful logging.
Stuck on this lesson?
Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.
About the internship