A production deployment fails for simple reasons more often than complex ones. The wrong Java version, missing environment variable, blocked port, or untested database connection can stop a release.
To deploy spring boot app code safely, build one repeatable path from source commit to running artifact. Choose the hosting model, externalize configuration, test the packaged app, protect management endpoints, and define rollback before the first production release.
1. CHOOSE THE DEPLOYMENT TARGET
Spring Boot gives you several valid deployment paths. The right choice depends on your team, traffic, operating budget, and need for infrastructure control.
Use a virtual machine for direct control
A virtual machine works well when you need direct access to the operating system, system packages, private networking, or a long-running process. You install Java, copy the JAR, configure a service manager, and place a reverse proxy in front of the application.
This model gives you control, but your team owns patching, monitoring, firewall rules, process restarts, and server capacity.
Use a managed platform for less server work
Platforms such as AWS Elastic Beanstalk, Google Cloud Run, Azure App Service, Render, and Railway can run Spring Boot applications with less infrastructure management. You usually provide a JAR, container image, build command, or start command.
The platform controls deployment mechanics. You still control application configuration, database access, authentication, logging, and data protection.
Spring Boot’s official cloud deployment guidance covers executable JARs, traditional WAR files, and several platform patterns. Provider commands and supported runtimes change, so check both the Spring Boot release documentation and the platform documentation before deployment.
2. VERIFY JAVA AND SPRING BOOT COMPATIBILITY
Your local Java version must match the runtime used in production. Do not build with Java 21 and deploy on an unverified Java 17 image without testing that combination.
At the time of writing, Spring Boot 4.1.1 requires Java 17 or newer and supports Java versions through Java 26. It also requires Spring Framework 7.0.9 or newer. Confirm the requirements for your own release in the Spring Boot system requirements.
Check your local tools first:
java -version
./mvnw -version
For Gradle projects, use:
./gradlew --version
Then record the selected Java version in your build configuration. A Maven project can use the following property:
<properties>
<java.version>21</java.version>
</properties>
A Gradle project can use:
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
Use Java 17, 21, or another supported version based on your Spring Boot line and hosting provider. The important point is consistency. Use the same major version in local testing, CI, the container image, and the production runtime.
3. PREPARE THE APPLICATION FOR PRODUCTION
A deployment package is only one part of the release. The application must also start correctly outside your development machine.
Check these areas before building:
- Database URLs and credentials come from environment variables or a secret manager.
- The application listens on the port supplied by the hosting platform.
- Database migrations run through an approved process.
- File storage does not depend on the local disk unless the platform provides persistent storage.
- Logs go to standard output or the provider’s supported logging system.
- CORS, cookies, and allowed origins use production values.
- Debug settings are disabled.
- Outbound calls use production endpoints and timeouts.
Add a platform-friendly port setting to application.yml:
server:
port: ${PORT:8080}
The application uses the PORT value when the provider supplies one. It uses port 8080 locally when the variable is absent.
Do not place passwords, API keys, private certificates, or database credentials in Git. Use approved secret storage. Environment variables are useful for deployment configuration, but a managed secret service is a better choice for sensitive production values.
Spring Boot supports properties files, YAML, environment variables, and command-line arguments through its externalized configuration system.
4. BUILD AND TEST THE RELEASE ARTIFACT
Spring Boot’s executable JAR includes the application and its embedded server. This lets you run the application with a standard Java command instead of installing Tomcat separately.
For Maven, build the package with:
./mvnw clean package
For Gradle, use:
./gradlew clean bootJar
Run the generated file locally:
java -jar target/myapp-0.0.1-SNAPSHOT.jar
The Gradle artifact usually appears under build/libs.
Test the exact artifact that you plan to deploy. Do not test only through your IDE. Your IDE may provide environment variables, a different classpath, local services, or a different Java version.
Run a production-profile test when your configuration uses profiles:
SPRING_PROFILES_ACTIVE=prod
java -jar target/myapp-0.0.1-SNAPSHOT.jar
Then test the application:
curl -i http://localhost:8080/actuator/health
Also test one real application path, such as a login request, read operation, or controlled database query. A successful process start does not prove that the application can reach its database or required services.
5. EXTERNALIZE CONFIGURATION AND SECRETS
Keep deployable code separate from environment-specific values. The same artifact should move through testing and production while each environment supplies its own configuration.
A production process might receive these variables:
SPRING_PROFILES_ACTIVE=prod
SPRING_DATASOURCE_URL=jdbc:postgresql://db.internal:5432/orders
SPRING_DATASOURCE_USERNAME=orders_app
SPRING_DATASOURCE_PASSWORD=provided-by-secret-storage
Never replace the password value above with a real credential in a script, repository, ticket, prompt, or deployment document. Store the secret in your cloud secret manager, CI secret store, or an approved encrypted configuration system.
Use separate credentials for each environment. Production database access should not use the same account as local development. Give the application only the permissions it needs, and keep schema-changing permissions separate when possible.
A useful configuration split looks like this:
application.yml
application-dev.yml
application-prod.yml
Keep safe defaults in application.yml. Put production overrides in application-prod.yml or provider configuration. Avoid putting sensitive values in either file.
6. CHOOSE BETWEEN A JAR AND A CONTAINER
An executable JAR is usually the shortest path for a managed Java platform or a virtual machine. A container is useful when you want the runtime, operating system layer, and startup command packaged together.
Deploy the executable JAR
The basic startup command is:
java -jar myapp.jar
Add JVM options through the platform’s configuration rather than hard-coding them into application code:
java -XX:MaxRAMPercentage=75
-jar myapp.jar
The correct memory settings depend on the platform’s container or process limits. Start with conservative values, monitor memory use, and adjust after observing real traffic.
Build a container image
A simple Dockerfile can look like this:
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/myapp.jar app.jar
USER 10001
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
Build and run it locally:
docker build -t myapp:local .
docker run --rm -p 8080:8080
-e SPRING_PROFILES_ACTIVE=prod
myapp:local
Use a non-root user where the base image and platform support it. Keep the image small, scan it for vulnerabilities, and avoid copying .env files or local credential files into the build context.
Docker provides additional Spring Boot containerization guidance. Image tags, Java images, and recommended build methods can change, so verify the current image documentation before standardizing your Dockerfile.
7. DEPLOY TO A VIRTUAL MACHINE WITH A SERVICE
A VM deployment needs a process manager. Do not rely on an SSH session that starts java -jar and then disconnects.
A systemd service gives the application a defined owner, startup command, restart policy, and log location. A basic unit file can look like this:
[Unit]
Description=My Spring Boot application
After=network.target
[Service]
User=spring
WorkingDirectory=/opt/myapp
ExecStart=/usr/bin/java -jar /opt/myapp/myapp.jar
Restart=on-failure
RestartSec=10
EnvironmentFile=/etc/myapp/myapp.env
[Install]
WantedBy=multi-user.target
After placing the file in /etc/systemd/system/myapp.service, reload and start it:
sudo systemctl daemon-reload
sudo systemctl enable myapp
sudo systemctl start myapp
sudo systemctl status myapp
View logs with:
sudo journalctl -u myapp -f
Put Nginx, Apache, or the provider’s load balancer in front of the application when you need TLS termination, a public hostname, request limits, or multiple instances. Keep port 8080 private when the reverse proxy handles public traffic.
8. DEPLOY TO A CLOUD PLATFORM
Cloud platforms use different deployment inputs. The application concepts stay similar, but the exact commands, port rules, health checks, and Java support vary.
AWS Elastic Beanstalk
Spring Boot’s cloud documentation shows Java SE deployment with an artifact configuration similar to this:
deploy:
artifact: target/myapp-0.0.1-SNAPSHOT.jar
The same guidance uses port 5000 in its Elastic Beanstalk example:
server.port=5000
Use the platform’s current Java platform documentation before copying this setting. If the environment supplies a PORT variable, prefer:
server:
port: ${PORT:8080}
Then confirm the provider’s expected port for your selected environment.
Container-based platforms
Cloud Run, Azure App Service for Containers, Render, Railway, and similar services generally need a container image, registry location, start command, or build command. The common deployment flow is:
- Build the JAR.
- Build the container image.
- Push the image to a registry.
- Configure environment variables and secrets.
- Set the service port.
- Add a health check.
- Deploy one instance.
- Review logs before increasing traffic.
Do not copy a command from an older tutorial without checking the provider’s current Java and container documentation. CLI syntax, buildpacks, supported Java releases, and required port behavior can change.
9. CONFIGURE HEALTH CHECKS AND ACTUATOR SAFELY
Spring Boot Actuator provides health, metrics, environment, and other operational endpoints. Add it with the Actuator starter:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Expose only the endpoints your platform or monitoring system needs:
management:
endpoints:
web:
exposure:
include: health,info
The Actuator endpoint documentation explains exposure and security behavior. Do not expose env, configprops, beans, or shutdown endpoints on a public interface without a clear security design.
A health endpoint should answer whether the process can accept traffic. A readiness check should also account for required dependencies when your deployment platform supports that distinction.
Use a separate management port only when your network and security model support it:
management:
server:
port: 8081
A separate port is not automatically private. Firewall it, restrict the load balancer, and require authentication where needed. Review the HTTP monitoring guidance before exposing management traffic.
A green process check does not prove that your database, queue, payment service, or required downstream API is working.
10. RELEASE WITH A ROLLBACK PLAN
Deploy one small release before increasing traffic. Confirm startup, health checks, logs, database access, authentication, and one important user action.
Use a versioned artifact name or immutable container tag. Avoid deploying a file called latest when you need to identify exactly what is running.
Record each release in a deployment log:
- Git commit or image digest.
- Build number and Java version.
- Database migration version.
- Person responsible for the deployment.
- Deployment time and provider environment.
- Health check result.
- Rollback decision and result.
Your runbook must name the person responsible, the place where the result is recorded, and the method used to identify the last trusted run. For example, the release owner records the result in the deployment log, and the last trusted run is the latest entry marked verified with a commit SHA and passing smoke test.
If the new version fails, stop the write step where possible. Route traffic to the previous artifact or redeploy the previous image. Do not guess which version was stable.
A rollback does not reverse a database migration automatically. Test backward compatibility between application versions and schema changes before using migrations in production.
11. TROUBLESHOOT COMMON DEPLOYMENT FAILURES
The process exits immediately
Check the first startup error in the platform log. Common causes include an unsupported Java version, missing environment variables, invalid YAML, failed database authentication, or a port mismatch.
Run the same packaged artifact locally with the same non-secret configuration shape. This usually reveals errors faster than repeated cloud deployments.
The platform reports an unhealthy service
Confirm that the application listens on the port the platform assigned. Then check whether the health path returns HTTP 200 without authentication.
A health endpoint may fail because the database is unavailable. That can be useful when database access is required for safe traffic, but it can also cause a full outage if the check includes an optional dependency. Define readiness rules based on the application’s actual traffic requirements.
Requests time out after deployment
Check firewall rules, security groups, reverse proxy routes, DNS, TLS certificates, and outbound network access. A running Java process does not prove that the public route reaches it.
Also check startup time. A platform may terminate an instance before Spring Boot finishes initializing if the health check timeout is too short.
The application loses uploaded files
Many cloud instances use temporary local storage. Store user uploads in object storage or a managed file service. Keep only temporary processing files on the application disk.
12. USE A PRODUCTION CHECKLIST
Before you deploy a Spring Boot app to production, confirm these items:
- The Java version is supported by the exact Spring Boot release.
- CI builds the same artifact that will run in production.
- Tests pass against the packaged JAR or production container.
- Secrets come from approved storage.
- The application uses the provider’s assigned port.
- Database migrations have an owner and rollback plan.
- Logs contain enough detail without exposing credentials or personal data.
- Only required Actuator endpoints are exposed.
- Health checks match the platform’s expected path and response.
- The previous trusted artifact is available.
- The release owner and deployment record are identified.
- A manual fallback exists if the deployment platform fails.
If your team needs a second review of the deployment process before a customer-facing release, you can Book A Call.
CONCLUSION
To deploy spring boot app code reliably, treat deployment as a controlled release process rather than a single java -jar command. Build a tested artifact, match Java versions, externalize secrets, configure the correct port, and protect operational endpoints.
The most useful safeguard is a clear last-known-good version. Record who deployed it, where the result lives, and which commit or image digest passed verification. When a release fails, that record gives you a direct path back to a trusted application.
