Spring Boot Advanced: Security and Production
Packaging and Deploying
In this lesson you will
- Build and run an executable jar
- Configure the app for production through the environment
- Containerise it and apply a production checklist
Introduction
A Spring Boot app is a normal Java program with its own web server, so deploying it can be as simple as copying one file and running it. Real deployments add configuration, containers, monitoring and safe shutdown. This lesson is written for Spring Boot 4.1.2. Commands and Dockerfile text were not run here, and hosting details vary by provider.
Build an executable jar
Terminal
./mvnw clean packageThis runs the tests and creates target/demo-0.0.1-SNAPSHOT.jar (the name comes from your artifactId and version). It is a fat jar with your code and all dependencies. Run it:
Terminal
java -jar target/demo-0.0.1-SNAPSHOT.jarYou need a matching Java runtime on the machine, Java 21 for the project generated in this course. To skip tests for a quick build you can add -DskipTests, but keep tests on in your release pipeline.
Configure through the environment
The same jar should run in every environment. Supply settings from outside:
Terminal
export SPRING_PROFILES_ACTIVE=prod
export SERVER_PORT=8080
export SPRING_DATASOURCE_URL=jdbc:postgresql://db.internal:5432/library
export SPRING_DATASOURCE_USERNAME=library_app
export SPRING_DATASOURCE_PASSWORD=...
java -jar app.jar- Environment variables override
application.properties. This is called relaxed binding. - Secrets come from the environment or a secrets manager, never from files in the repository.
- Do not use H2 or
ddl-auto=updatein production. Use a real database with migrations (Flyway or Liquibase). - Create a
prodprofile with production defaults, such as lower log levels.
Graceful shutdown
When an instance is stopped, requests in progress should finish. Enable graceful shutdown (the server.shutdown property exists in Boot 4.1.2):
Properties
server.shutdown=graceful
spring.lifecycle.timeout-per-shutdown-phase=30sWith readiness probes (previous lesson), the platform stops sending new traffic, and the app finishes current requests before exiting.
Containers
You can write a Dockerfile (not tested here):
Dockerfile
FROM eclipse-temurin:21-jre
WORKDIR /app
RUN useradd --create-home appuser
COPY target/demo-0.0.1-SNAPSHOT.jar app.jar
USER appuser
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]Notes:
- Use a JRE image that matches your Java version, from a trusted publisher, and keep it updated.
- Run as a non-root user.
- Pass configuration and secrets as environment variables at run time.
- Spring Boot can also build an image without a Dockerfile using Cloud Native Buildpacks (
./mvnw spring-boot:build-image), which requires Docker to be running. - Configure JVM memory to fit the container limits. Modern Java detects container limits, but check your settings, and watch memory.
- Containers are disposable, so keep state in databases and storage, not in the container.
Quick quiz
Why run the same jar in every environment and change behaviour through configuration?
Native images and other options
Spring Boot also supports compiling to a GraalVM native image, which starts faster and uses less memory, at the cost of longer builds and some restrictions. It suits some workloads, such as serverless. For most services, a normal jar is simpler.
Where to run
Options include virtual machines, container platforms, Kubernetes and platform-as-a-service providers. Free tiers exist but often have limits, such as slow wake-ups and capped usage, so read the terms. A managed database is usually better than running one yourself.
Production readiness checklist
- Tests pass in an automated build
- Configuration and secrets from the environment, and no secrets in the repository
- HTTPS, terminated by a proxy or platform
- Spring Security configured, deny by default, passwords hashed
- Actuator limited or on a private port, health probes configured
- Real database with migrations and backups
- Pagination, indexes and no N+1 queries on key paths
- Logs to standard output, no sensitive data, and metrics with alerts
- Graceful shutdown enabled
- Dependencies updated and security advisories monitored
- A rollback plan, and a way to deploy without downtime
Keep learning
Next steps: Flyway migrations, OAuth 2 with an identity provider, messaging and queues, distributed tracing and a CI/CD pipeline. Practise by building a small project end to end, deploying it and watching its metrics.
Try it yourself
Build the jar and run it with SERVER_PORT=9090 set in the environment. Create a prod profile that lowers the log level, and write the Dockerfile for your jar. If you have Docker, build and run it, and confirm /actuator/health responds.
Summary
- Build an executable jar with ./mvnw package and run it with java -jar, using Java that matches your build.
- Configure each environment through environment variables, use a real database with migrations, and enable graceful shutdown.
- Containerise with a non-root user and runtime secrets, and work through a production checklist before launch.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading