Deploying Spring Boot Applications on Heroku: A Comprehensive Cloud Deployment Guide
Transitioning a Spring Boot application from a local development environment (localhost) to a public cloud infrastructure is a critical step in software engineering. This guide walks through packaging, configuring, and deploying a Spring Boot application to Heroku, securing sensitive credentials using environment variables, and managing multi-environment API testing using Postman.
1. Why Cloud Deployment? The Problem with Localhost
During development, applications are typically run locally using an Integrated Development Environment (IDE) or local build tools, exposing endpoints on ports like localhost:8080 or localhost:8081.
The Limitation of Localhost
While localhost is perfect for rapid iteration, it is completely isolated from the outside world. If you want to showcase your application to recruiters, add it to your resume, or share it with collaborators, they cannot access it unless they clone your repository, set up a local database, and run the application on their own machine.
The Cloud Solution
Deploying your application to the cloud solves this problem by hosting your code on a remote server. The cloud provider assigns your application a public IP address or a Domain Name System (DNS) domain (e.g., your-app-name.herokuapp.com). This allows anyone across the globe to interact with your API endpoints.
2. The Core Mental Model: Local vs. Cloud Execution
To understand how cloud deployment works, we must look at what happens under the hood when we run a Spring Boot application.
Local Execution Workflow
When you click "Run" in IntelliJ or execute a run command, the following steps occur:
- Compilation: The source code is compiled into bytecode.
- Packaging: Build tools like Maven package the compiled code and resources into a runnable Java Archive (
.jar) file using the command:BASHmvn clean package - Execution: The
.jarfile is executed using the Java Virtual Machine (JVM):BASHjava -jar target/your-app-name.jar - Server Startup: Spring Boot starts its embedded Apache Tomcat server (typically on port
8080) and begins listening for incoming HTTP requests.
Cloud Execution Workflow
In a cloud environment, the exact same process must occur, but on a remote Virtual Machine (VM). Cloud providers like Amazon Web Services (AWS) offer Virtual Machines via EC2 (Elastic Compute Cloud) where you can manually install Java, pull your code, build it, and run it.
However, Platform-as-a-Service (PaaS) providers like Heroku simplify this entire pipeline by automating the provisioning, building, and execution steps.
3. Understanding Heroku & Git-Based CI/CD
Heroku acts as an abstraction layer over raw cloud infrastructure. Instead of manually configuring a Linux VM, installing Java, and setting up firewalls, Heroku uses a Git-based workflow to automate deployment.
Continuous Integration & Continuous Deployment (CI/CD)
CI/CD is an industry-standard practice that automates the integration of code changes from multiple developers and deploys them to production.
By linking your local Git repository to a Heroku-hosted Git remote, every time you push code to Heroku, it automatically:
- Detects the project type (e.g., Java/Maven).
- Downloads the correct JDK version.
- Runs the build command (
mvn clean package -DskipTests). - Launches the application using the embedded Tomcat server.
4. Prerequisites & Environment Setup
Before deploying, you must set up your local environment and cloud accounts.
Step 1: Install Git
Ensure Git is installed on your local machine. Verify the installation by running:
git --version
Step 2: Create a Heroku Account & Enable 2FA
- Go to Heroku's Signup Page and register. Select Java as your primary development language.
- Confirm your email and set a secure password.
- Multi-Factor Authentication (MFA): Heroku requires 2FA. Install an authenticator app (such as Google Authenticator or Microsoft Authenticator) on your mobile device, scan the QR code provided in your Heroku account settings, and enter the verification code to secure your account.
Step 3: Add a Payment Method
Heroku requires a verified payment method (credit/debit card) to run applications and prevent abuse.
Note: Heroku may perform a temporary pre-authorization charge (approximately $1 USD) to verify your card, which is subsequently refunded.
Step 4: Install the Heroku Command Line Interface (CLI)
The Heroku CLI allows you to manage your applications directly from your terminal.
- Windows: Download and run the 64-bit installer from the official Heroku CLI documentation.
- macOS: Install via Homebrew:
BASH
brew install heroku/brew/heroku
Verify the installation:
heroku --version
Step 5: Log In via CLI
Open your terminal (or Git Bash) and run:
heroku login
This command opens a browser window to authenticate your session. Once logged in, your terminal will display your account email.
5. Preparing the Codebase: Forking, Cloning, and the .gitignore Dilemma
To deploy your application, you need a clean Git repository.
Step 1: Fork and Clone the Repository
If you are working on a shared or tutorial repository, create a personal copy (fork) on your GitHub account:
- Navigate to the target GitHub repository.
- Click the Fork button in the top-right corner.
- Clone your forked repository to your local machine:
BASH
git clone https://github.com/your-username/journal-app.git cd journal-app
Step 2: Handling the application.yml File
In local development, configuration files containing database credentials, API keys, and mail server passwords (such as application.yml or application.properties) are often added to .gitignore to prevent sensitive data from being pushed to public repositories.
However, if Heroku does not have access to your configuration file, the build will fail because Spring Boot won't know how to connect to your database or configure its beans.
The Solution: Decouple Configuration from Code
Instead of hardcoding sensitive credentials inside application.yml and pushing them to Git, we use Environment Variables and Placeholders. This allows us to safely commit the application.yml file to GitHub while keeping our secrets secure.
6. Securing Secrets with Environment Variables (Placeholders)
Let's refactor a naive, insecure application.yml file into a production-ready, secure configuration.
Naive (Insecure) Configuration
# Insecure: Hardcoded credentials committed to Git
spring:
data:
mongodb:
uri: mongodb+srv://admin:SuperSecretPassword123@cluster0.mongodb.net/journaldb
mail:
host: smtp.gmail.com
port: 587
username: developer@gmail.com
password: my-email-password
Decoupled (Secure) Configuration
We replace the sensitive values with Spring placeholder syntax: ${ENVIRONMENT_VARIABLE_NAME:DEFAULT_VALUE}.
# Secure: Credentials injected dynamically at runtime
server:
port: ${PORT:8081}
spring:
data:
mongodb:
uri: ${MONGO_URI}
mail:
host: smtp.gmail.com
port: 587
username: ${JAVA_EMAIL_SENDER}
password: ${JAVA_EMAIL_PASSWORD}
redis:
host: ${REDIS_HOST:localhost}
port: ${REDIS_PORT:6379}
password: ${REDIS_PASSWORD:}
Modifying .gitignore
Now that application.yml contains no sensitive plain-text secrets, we can safely remove it from .gitignore so Git can track it:
- Open
.gitignore. - Comment out or delete the line excluding
application.ymlorapplication.properties:GIT# Remove or comment out this line: # src/main/resources/application.yml - Stage and commit the changes:
BASH
git add .gitignore src/main/resources/application.yml git commit -m "Refactor application.yml to use environment placeholders" git push origin master
7. Creating the Heroku App & Managing Remotes
With your code prepared, you can now initialize your Heroku application.
Step 1: Create the App
Run the following command inside your project's root directory:
heroku create
This command performs two key actions:
- It provisions a new application on Heroku with a randomly generated, unique name (e.g.,
shielded-ridge-12345). - It automatically adds a new Git remote named
herokuto your local repository.
Step 2: Verify Git Remotes
To see your configured remote repositories, run:
git remote -v
You should see output similar to this:
8. Configuring Environment Variables (Config Vars)
Before deploying the code, we must supply the values for the placeholders we defined in application.yml. Heroku calls these environment variables Config Vars.
Method A: Setting Config Vars via CLI
You can set variables individually using the heroku config:set command:
heroku config:set MONGO_URI="mongodb+srv://admin:SuperSecretPassword123@cluster0.mongodb.net/journaldb"
heroku config:set JAVA_EMAIL_SENDER="developer@gmail.com"
heroku config:set JAVA_EMAIL_PASSWORD="my-email-password"
To verify the variables set on your app:
heroku config
Method B: Setting Config Vars via Heroku Dashboard
- Log in to the Heroku Dashboard.
- Click on your newly created application (
shielded-ridge-12345). - Navigate to the Settings tab.
- Scroll down to the Config Vars section and click Reveal Config Vars.
- Add your key-value pairs matching the placeholders in your
application.yml:
Important Database Note: If you are using MongoDB Atlas, make sure your database network access settings allow connections from any IP address (
0.0.0.0/0). Because Heroku dynos use dynamic IP addresses that change on every restart, restricting access to a single IP will cause database connection timeouts in production.
9. Deploying the Application & Real-World Debugging (Logs)
With the configuration variables set, you are ready to deploy.
Step 1: Push Code to Heroku
Push your local branch (e.g., master or main) to the heroku remote:
git push heroku master
Heroku will receive the push, detect the Maven build system, download dependencies, compile the code, package the .jar, and start the application.
Step 2: Real-World Debugging with Logs
If the application fails to start or returns an "Application Error" page, you must inspect the runtime logs to diagnose the issue.
To stream live logs from your Heroku container directly to your terminal, run:
heroku logs --tail
Common Production Issues & Resolutions
- MongoDB Connection Timeout:
- Symptom: Logs show
MongoTimeoutException: Timed out after 30000 ms. - Fix: Ensure your MongoDB Atlas IP Access List is configured to allow access from anywhere (
0.0.0.0/0).
- Symptom: Logs show
- Missing Config Vars:
- Symptom: Logs show
IllegalArgumentException: Could not resolve placeholder 'MONGO_URI'. - Fix: Double-check your Heroku Config Vars for spelling errors or missing keys.
- Symptom: Logs show
- Port Binding Issues:
- Symptom: Logs show
Web process failed to bind to $PORT within 60 seconds of launch. - Fix: Ensure your
application.ymluses the dynamic port placeholder:port: ${PORT:8080}. Heroku dynamically assigns a port to your container at runtime via thePORTenvironment variable; you cannot hardcode a port like8080or8081in production.
- Symptom: Logs show
10. Optimizing Postman Workflows with Environments
Testing APIs across multiple environments (Local vs. Production) can become tedious if you have to manually change the base URL for every request. Postman Environments solve this problem.
Step-by-Step Postman Configuration
-
Create Environments:
- In Postman, click on Environments in the left sidebar.
- Click Create Environment and name it
Local. - Create another environment and name it
Production.
-
Define Variables:
- In the
Localenvironment, add a variable nameddomainwith the valuelocalhost:8081. - In the
Productionenvironment, add the same variabledomainwith your Heroku application URL (e.g.,shielded-ridge-12345.herokuapp.com).
- In the
-
Parameterize Requests:
- Update your API request URLs to use the variable syntax:
{{domain}}. - Example:
http://{{domain}}/health-checkorhttps://{{domain}}/journal.
- Update your API request URLs to use the variable syntax:
-
Switch Environments:
- Use the environment dropdown in the top-right corner of Postman to switch between
LocalandProduction. Postman will dynamically swap the base URL, allowing you to test local changes and production deployments seamlessly.
- Use the environment dropdown in the top-right corner of Postman to switch between
11. Custom Domains & DNS Redirection
By default, Heroku provides a subdomain under herokuapp.com. For professional projects or resumes, you may want to use a custom domain (e.g., yourname.com).
How DNS Redirection Works
To point a custom domain to Heroku, you must configure your domain registrar's (e.g., GoDaddy, Namecheap) DNS settings:
Basic Configuration Steps
- Add the domain to your Heroku app:
BASH
heroku domains:add www.yourname.com - Heroku will generate a DNS Target (e.g.,
your-app-name.herokudns.com). - Log in to your domain registrar's control panel.
- Navigate to the DNS Management section and add a CNAME record:
- Type:
CNAME - Host/Name:
www - Value/Target: The DNS Target provided by Heroku.
- Type:
- Save the record. (Note: DNS propagation can take anywhere from a few minutes to 24 hours).
12. Summary and Key Takeaways
- Public Accessibility: Deploying your application to the cloud transitions it from
localhostto a public domain, making it accessible to anyone worldwide. - Heroku's Git Workflow: Heroku acts as a PaaS, automating compilation, packaging, and execution (
java -jar) whenever you push code to theherokuremote. - Decoupled Configurations: Never commit plain-text passwords or API keys to Git. Use Spring placeholder syntax (
${VARIABLE_NAME:DEFAULT}) to inject values at runtime. - Config Vars: Manage production credentials securely using Heroku Config Vars via the CLI or the web dashboard.
- Dynamic Port Binding: Always map your server port to
${PORT:8080}in production, as Heroku dynamically assigns ports to running containers. - Live Log Streaming: Use
heroku logs --tailto monitor application health and debug runtime issues in real-time. - Postman Environments: Use Postman variables (
{{domain}}) to switch between local and production testing environments with a single click.