Getting Started on Heroku with Java
Introduction
Complete this tutorial to deploy a sample Java (Maven) app to Cedar, the legacy generation of the Heroku platform. To deploy the app to the Fir generation, only available to Heroku Private Spaces, follow this guide instead.
The tutorial assumes that you have:
- A verified Heroku Account
- An Eco dynos plan subscription (recommended)
- OpenJDK 17 (or newer) installed locally
- Postgres installed locally
Using dynos and databases to complete this tutorial counts towards your usage. We recommend using our low-cost plans to complete this tutorial. Eligible students can apply for platform credits through our new Heroku for GitHub Students program.
Set Up
Install the Heroku Command Line Interface (CLI). Use the CLI to manage and scale your app, provision add-ons, view your logs, and run your app locally.
The Heroku CLI requires Git, the popular version control system. If you don’t already have Git installed, complete the following before proceeding:
Download and run the installer for your platform:
Download the appropriate installer for your Windows installation:
You can find more installation options for the Heroku CLI here.
After installation, you can use the heroku command from your command shell.
To log in to the Heroku CLI, use the heroku login command:
$ heroku login
heroku: Press any key to open up the browser to login or q to exit:
Opening browser to https://cli-auth.heroku.com/auth/cli/browser/***
heroku: Waiting for login...
Logging in... done
Logged in as me@example.com
This command opens your web browser to the Heroku login page. If your browser is already logged in to Heroku, click the Log In button on the page.
This authentication is required for the heroku and git commands to work correctly.
If you have any problems installing or using the Heroku CLI, see the main Heroku CLI article for advice and troubleshooting steps.
If you’re behind a firewall that uses a proxy to connect with external HTTP/HTTPS services, set the HTTP_PROXY or HTTPS_PROXY environment variables in your local development environment before running the heroku command.
Clone the Sample App
If you’re new to Heroku, it’s recommended that you complete this tutorial using the Heroku-provided sample application.
To deploy an existing application, follow this article instead.
Clone the sample application to get a local version of the code. Execute these commands in your local command shell or terminal:
$ git clone https://github.com/heroku/java-getting-started
$ cd java-getting-started
You now have a functioning Git repository that contains a simple application. It includes a pom.xml file, which is used by Maven, a Java build tool.
Create Your App
Using a dyno and a database to complete this tutorial counts towards your usage. Delete your app, and database as soon as you’re done to control costs.
By default, apps use Eco dynos if you’re subscribed to Eco. Otherwise, it defaults to Basic dynos. The Eco dynos plan is shared across all Eco dynos in your account. It’s recommended if you plan on deploying many small apps to Heroku. Learn more here. Eligible students can apply for platform credits through our Heroku for GitHub Students program.
Create an app on Heroku to prepare the platform to receive your source code:
$ heroku create --stack heroku-26
Creating app... done, powerful-refuge-16953, stack is heroku-26
https://powerful-refuge-16953-20eaf18160c9.herokuapp.com/ | https://git.heroku.com/powerful-refuge-16953.git
When you create an app, a Git remote named heroku is also created and added to your local repository configuration. Git remotes are versions of your repository that live on other servers. You can deploy your app by pushing code to that special Heroku-hosted remote associated with your app.
Heroku generates a random name for your app, in this case, powerful-refuge-16953. You can specify your own app name.
Provision a Database
The sample app requires a database. Provision a Heroku Postgres database, an add-on available through the Elements Marketplace. Add-ons are cloud services that provide out-of-the-box additional services for your application, such as logging, monitoring, databases, and more.
An essential-0 Postgres size costs $5 a month, prorated to the minute. At the end of this tutorial, we prompt you to delete your app and its database to minimize costs.
$ heroku addons:create heroku-postgresql:essential-0
Creating heroku-postgresql:essential-0 on powerful-refuge-16953... ~$0.007/hour (max $5/month)
Database should be available soon
postgresql-vertical-54832 is being created in the background. The app will restart when complete...
Run heroku addons:info postgresql-vertical-54832 to check creation progress.
Run heroku addons:docs heroku-postgresql to view documentation.
You can wait for the database to provision by running this command:
$ heroku pg:wait
After that command exits, your Heroku app can access the Postgres database. The DATABASE_URL environment variable stores your credentials. For applications that use the JVM an additional environment variable JDBC_DATABASE_URL is available. It contains a JDBC compatible connection string. You can see all the add-ons provisioned with the addons command:
$ heroku addons
Add-on Plan Price Max Price State
----------------------------------------------------------------------------------------------
heroku-postgresql (postgresql-vertical-54832) essential-0 ~$0.007/hour $5/month created
└─ as DATABASE
The table above shows add-ons and the attachments to the current app (powerful-refuge-16953) or other apps.
Deploy the App
Deploy your code. This command pushes the main branch of the sample repo to your heroku remote, which then deploys to Heroku:
$ git push heroku main
remote: Updated 26 paths from c441817
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Building on the Heroku-26 stack
remote: -----> Determining which buildpack to use for this app
remote: -----> Java app detected
remote: -----> Installing Azul Zulu OpenJDK 17.0.20.1
remote: -----> Executing Maven
remote: $ ./mvnw -DskipTests clean dependency:list install
remote: [INFO] Scanning for projects...
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/4.1.1/spring-boot-starter-parent-4.1.1.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/4.1.1/spring-boot-starter-parent-4.1.1.pom (14 kB at 62 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom (141 kB at 4.7 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/activemq/activemq-bom/6.2.9/activemq-bom-6.2.9.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/activemq/activemq-bom/6.2.9/activemq-bom-6.2.9.pom (7.9 kB at 358 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/artemis/artemis-bom/2.53.0/artemis-bom-2.53.0.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/artemis/artemis-bom/2.53.0/artemis-bom-2.53.0.pom (18 kB at 767 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/artemis/artemis-project/2.53.0/artemis-project-2.53.0.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/artemis/artemis-project/2.53.0/artemis-project-2.53.0.pom (55 kB at 2.0 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/apache/37/apache-37.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/apache/37/apache-37.pom (26 kB at 1.2 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/assertj/assertj-bom/3.27.7/assertj-bom-3.27.7.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/assertj/assertj-bom/3.27.7/assertj-bom-3.27.7.pom (3.3 kB at 133 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/reporter2/zipkin-reporter-bom/3.5.3/zipkin-reporter-bom-3.5.3.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/io/zipkin/reporter2/zipkin-reporter-bom/3.5.3/zipkin-reporter-bom-3.5.3.pom (6.1 kB at 254 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/io/zipkin/brave/brave-bom/6.3.1/brave-bom-6.3.1.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/io/zipkin/brave/brave-bom/6.3.1/brave-bom-6.3.1.pom (11 kB at 467 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/cassandra/java-driver-bom/4.19.3/java-driver-bom-4.19.3.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/cassandra/java-driver-bom/4.19.3/java-driver-bom-4.19.3.pom (5.5 kB at 248 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/groovy/groovy-bom/5.0.8/groovy-bom-5.0.8.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/groovy/groovy-bom/5.0.8/groovy-bom-5.0.8.pom (27 kB at 1.3 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/io/grpc/grpc-bom/1.83.1/grpc-bom-1.83.1.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/io/grpc/grpc-bom/1.83.1/grpc-bom-1.83.1.pom (6.3 kB at 317 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-bom/16.1.4/infinispan-bom-16.1.4.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/infinispan/infinispan-bom/16.1.4/infinispan-bom-16.1.4.pom (20 kB at 724 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-bom/2.21.5/jackson-bom-2.21.5.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-bom/2.21.5/jackson-bom-2.21.5.pom (20 kB at 963 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-parent/2.21/jackson-parent-2.21.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/fasterxml/jackson/jackson-parent/2.21/jackson-parent-2.21.pom (6.9 kB at 382 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/oss-parent/75/oss-parent-75.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/fasterxml/oss-parent/75/oss-parent-75.pom (24 kB at 1.2 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/tools/jackson/jackson-bom/3.1.5/jackson-bom-3.1.5.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/tools/jackson/jackson-bom/3.1.5/jackson-bom-3.1.5.pom (20 kB at 685 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/com/fasterxml/oss-parent/79/oss-parent-79.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/com/fasterxml/oss-parent/79/oss-parent-79.pom (24 kB at 1.0 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/glassfish/jersey/jersey-bom/4.0.2/jersey-bom-4.0.2.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/glassfish/jersey/jersey-bom/4.0.2/jersey-bom-4.0.2.pom (24 kB at 1.1 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/eclipse/ee4j/project/1.0.9/project-1.0.9.pom
...
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-tree/9.7/asm-tree-9.7.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-shade-plugin/3.6.0/maven-shade-plugin-3.6.0.jar (150 kB at 1.6 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/jdom/jdom2/2.0.6.1/jdom2-2.0.6.1.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.5.1/plexus-utils-3.5.1.jar (269 kB at 2.6 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/commons-io/commons-io/2.16.1/commons-io-2.16.1.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-commons/9.7/asm-commons-9.7.jar (73 kB at 693 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/vafer/jdependency/2.10/jdependency-2.10.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/ow2/asm/asm/9.7/asm-9.7.jar (125 kB at 1.1 MB/s)
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/jdom/jdom2/2.0.6.1/jdom2-2.0.6.1.jar (328 kB at 2.8 MB/s)
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/ow2/asm/asm-tree/9.7/asm-tree-9.7.jar (52 kB at 433 kB/s)
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/vafer/jdependency/2.10/jdependency-2.10.jar (416 kB at 3.2 MB/s)
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/commons-io/commons-io/2.16.1/commons-io-2.16.1.jar (509 kB at 3.9 MB/s)
remote: [INFO] Replacing main artifact /tmp/build_2d948604/target/java-getting-started-1.0.0-SNAPSHOT.jar with repackaged archive, adding nested dependencies in BOOT-INF/.
remote: [INFO] The original artifact has been renamed to /tmp/build_2d948604/target/java-getting-started-1.0.0-SNAPSHOT.jar.original
remote: [INFO]
remote: [INFO] --- install:3.1.4:install (default-install) @ java-getting-started ---
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-util/1.9.22/maven-resolver-util-1.9.22.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-util/1.9.22/maven-resolver-util-1.9.22.pom (2.2 kB at 131 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver/1.9.22/maven-resolver-1.9.22.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver/1.9.22/maven-resolver-1.9.22.pom (23 kB at 1.4 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-api/1.9.22/maven-resolver-api-1.9.22.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-api/1.9.22/maven-resolver-api-1.9.22.pom (2.2 kB at 123 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/4.0.1/plexus-utils-4.0.1.pom
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/4.0.1/plexus-utils-4.0.1.pom (7.8 kB at 435 kB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-util/1.9.22/maven-resolver-util-1.9.22.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-util/1.9.22/maven-resolver-util-1.9.22.jar (196 kB at 6.3 MB/s)
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-api/1.9.22/maven-resolver-api-1.9.22.jar
remote: [INFO] Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/4.0.1/plexus-utils-4.0.1.jar
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/resolver/maven-resolver-api/1.9.22/maven-resolver-api-1.9.22.jar (157 kB at 6.8 MB/s)
remote: [INFO] Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/4.0.1/plexus-utils-4.0.1.jar (193 kB at 8.4 MB/s)
remote: [INFO] Installing /tmp/build_2d948604/pom.xml to /tmp/codon/tmp/cache/.m2/repository/com/heroku/java-getting-started/1.0.0-SNAPSHOT/java-getting-started-1.0.0-SNAPSHOT.pom
remote: [INFO] Installing /tmp/build_2d948604/target/java-getting-started-1.0.0-SNAPSHOT.jar to /tmp/codon/tmp/cache/.m2/repository/com/heroku/java-getting-started/1.0.0-SNAPSHOT/java-getting-started-1.0.0-SNAPSHOT.jar
remote: [INFO] ------------------------------------------------------------------------
remote: [INFO] BUILD SUCCESS
remote: [INFO] ------------------------------------------------------------------------
remote: [INFO] Total time: 15.085 s
remote: [INFO] Finished at: 2026-10-16T12:34:07Z
remote: [INFO] ------------------------------------------------------------------------
remote: -----> Discovering process types
remote: Procfile declares types -> web
remote:
remote: -----> Compressing...
remote: Done: 87.1M
remote: -----> Launching...
remote: Released v4
remote: https://powerful-refuge-16953-20eaf18160c9.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/powerful-refuge-16953.git
* [new branch] main -> main
Visit the app at the URL shown in the logs. As a shortcut, you can also open the website as follows:
$ heroku open
Understanding the Procfile
Use a Procfile, a text file in the root directory of your application, to explicitly declare what command to execute to start your app.
The Procfile in the example app looks like this:
web: java -jar target/java-getting-started-1.0.0-SNAPSHOT.jar
This Procfile declares a single process type, web, and the command needed to run it. The name web is important here. It declares that this process type is attached to Heroku’s HTTP routing stack and receives web traffic when deployed.
A Procfile can contain additional process types. For example, you can declare a background worker process that processes items off a queue.
View Logs
Heroku treats logs as streams of time-ordered events, aggregated from the output streams of all your app and Heroku components. Heroku provides a single stream for all events.
View information about your running app by using one of the logging commands, heroku logs --tail:
$ heroku logs --tail
2026-10-16T12:34:15.965328+00:00 heroku[web.1]: Starting process with command `java -jar target/java-getting-started-1.0.0-SNAPSHOT.jar`
2026-10-16T12:34:16.759021+00:00 app[web.1]: Setting JAVA_TOOL_OPTIONS defaults based on dyno size. Custom settings will override them.
2026-10-16T12:34:16.762017+00:00 app[web.1]: Picked up JAVA_TOOL_OPTIONS: -Dfile.encoding=UTF-8 -XX:MaxRAM=536870912 -Xmx300m -Xss512k -XX:CICompilerCount=2
2026-10-16T12:34:17.430914+00:00 app[web.1]: _ _ _
2026-10-16T12:34:17.430928+00:00 app[web.1]: | | | | | |
2026-10-16T12:34:17.430929+00:00 app[web.1]: | |__| | ___ _ __ ___ | | ___ _
2026-10-16T12:34:17.430929+00:00 app[web.1]: | __ |/ _ \ '__/ _ \| |/ / | | |
2026-10-16T12:34:17.430954+00:00 app[web.1]: | | | | __/ | | (_) | <| |_| |
2026-10-16T12:34:17.430954+00:00 app[web.1]: |_| |_|\___|_| \___/|_|\_\\__,_|
2026-10-16T12:34:17.430955+00:00 app[web.1]:
2026-10-16T12:34:17.430955+00:00 app[web.1]: :: Built with Spring Boot :: 4.1.1
2026-10-16T12:34:17.430956+00:00 app[web.1]:
2026-10-16T12:34:17.510252+00:00 app[web.1]: 2026-10-16T12:34:17.506Z INFO 2 --- [ main] c.heroku.java.GettingStartedApplication : Starting GettingStartedApplication v1.0.0-SNAPSHOT using Java 17.0.20.1 with PID 2 (/app/target/java-getting-started-1.0.0-SNAPSHOT.jar started by u57376 in /app)
2026-10-16T12:34:17.511394+00:00 app[web.1]: 2026-10-16T12:34:17.511Z INFO 2 --- [ main] c.heroku.java.GettingStartedApplication : No active profile set, falling back to 1 default profile: "default"
2026-10-16T12:34:18.362167+00:00 app[web.1]: 2026-10-16T12:34:18.361Z INFO 2 --- [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat initialized with port 30964 (http)
2026-10-16T12:34:18.372850+00:00 app[web.1]: 2026-10-16T12:34:18.372Z INFO 2 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-10-16T12:34:18.373002+00:00 app[web.1]: 2026-10-16T12:34:18.372Z INFO 2 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/11.0.24]
2026-10-16T12:34:18.399337+00:00 app[web.1]: 2026-10-16T12:34:18.399Z INFO 2 --- [ main] b.w.c.s.WebApplicationContextInitializer : Root WebApplicationContext: initialization completed in 835 ms
2026-10-16T12:34:18.495818+00:00 app[web.1]: 2026-10-16T12:34:18.495Z INFO 2 --- [ main] o.s.b.w.a.WelcomePageHandlerMapping : Adding welcome page template: index
2026-10-16T12:34:18.744361+00:00 app[web.1]: 2026-10-16T12:34:18.744Z INFO 2 --- [ main] o.s.boot.tomcat.TomcatWebServer : Tomcat started on port 30964 (http) with context path '/'
2026-10-16T12:34:18.752293+00:00 app[web.1]: 2026-10-16T12:34:18.752Z INFO 2 --- [ main] c.heroku.java.GettingStartedApplication : Started GettingStartedApplication in 1.613 seconds (process running for 1.99)
2026-10-16T12:34:19.002668+00:00 heroku[web.1]: State changed from starting to up
2026-10-16T12:34:22.584275+00:00 app[web.1]: 2026-10-16T12:34:22.584Z INFO 2 --- [io-30964-exec-3] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2026-10-16T12:34:22.587738+00:00 app[web.1]: 2026-10-16T12:34:22.584Z INFO 2 --- [io-30964-exec-3] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2026-10-16T12:34:22.589222+00:00 app[web.1]: 2026-10-16T12:34:22.589Z INFO 2 --- [io-30964-exec-3] o.s.web.servlet.DispatcherServlet : Completed initialization in 1 ms
2026-10-16T12:34:22.798869+00:00 heroku[router]: at=info method=GET path="/" host=powerful-refuge-16953-20eaf18160c9.herokuapp.com request_id=1634714c-4611-1d8c-f3b9-7e08ac02199b fwd="123.456.789.0" dyno=web.1 connect=0ms service=232ms status=200 bytes=9931 protocol=http2.0 tls=true tls_version=tls1.3
To generate more log messages, refresh the app in your browser.
To stop streaming the logs, press Control+C.
Scale the App
After deploying the sample app, it automatically runs on a single web dyno. Think of a dyno as a lightweight container that runs the command specified in the Procfile.
You can check how many dynos are running by using the ps command:
$ heroku ps
=== web (Basic): java -jar target/java-getting-started-1.0.0-SNAPSHOT.jar (1)
web.1: up 2026/10/16 12:34:18 +0000 (~ 10s ago)
Scaling an app on Heroku is equivalent to changing the number of running dynos. Scale the number of web dynos to zero:
$ heroku ps:scale web=0
$ heroku ps:wait
Access the app again by hitting refresh in your browser, or heroku open to open it in a web tab. You get an error message because you no longer have web dynos available to serve requests.
Scale it up again:
$ heroku ps:scale web=1
$ heroku ps:wait
By default, apps use Eco dynos if you’re subscribed to Eco. Otherwise, it defaults to Basic dynos. The Eco dynos plan is shared across all Eco dynos in your account and is recommended if you plan on deploying many small apps to Heroku. Eco dynos sleep if they don’t receive any traffic for half an hour. This sleep behavior causes a few seconds delay for the first request upon waking. Eco dynos consume from a monthly, account-level quota of eco dyno hours. As long as you haven’t exhausted the quota, your apps can continue to run.
To avoid dyno sleeping, upgrade to a Basic or higher dyno type as described in the Dyno Types article. Upgrading to at least Standard dynos allows you to scale up to multiple dynos per process type.
Declare App Dependencies
Heroku recognizes an app as a Java (Maven) app by the existence of a pom.xml file in the root directory.
The demo app you deployed already has a pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<name>java-getting-started</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>4.1.1</version>
...
The pom.xml file specifies the dependencies to install with your application.
Run ./mvnw install in your local directory to install the dependencies, preparing your system for running the app locally:
$ ./mvnw install
[INFO] Scanning for projects...
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/4.1.1/spring-boot-starter-parent-4.1.1.pom
Progress (1): 1.1 kB
Progress (1): 1.2 kB
Progress (1): 3.5 kB
Progress (1): 8.0 kB
Progress (1): 11 kB
Progress (1): 14 kB
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-starter-parent/4.1.1/spring-boot-starter-parent-4.1.1.pom (14 kB at 78 kB/s)
Downloading from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom
Progress (1): 987 B
Progress (1): 1.1 kB
Progress (1): 2.9 kB
Progress (1): 5.2 kB
Progress (1): 6.5 kB
Progress (1): 8.9 kB
Progress (1): 11 kB
Progress (1): 13 kB
Progress (1): 22 kB
Progress (1): 31 kB
Progress (1): 37 kB
Progress (1): 43 kB
Progress (1): 53 kB
Progress (1): 58 kB
Progress (1): 73 kB
Progress (1): 94 kB
Progress (1): 109 kB
Progress (1): 120 kB
Progress (1): 127 kB
Progress (1): 132 kB
Progress (1): 139 kB
Progress (1): 141 kB
Downloaded from central: https://repo.maven.apache.org/maven2/org/springframework/boot/spring-boot-dependencies/4.1.1/spring-boot-dependencies-4.1.1.pom (141 kB at 2.3 MB/s)
Push Local Changes
In this step, you propagate a local change to the application to Heroku.
Modify pom.xml to include an additional dependency for the jscience.
In file pom.xml, on line 44 add:
<dependency>
<groupId>org.jscience</groupId>
<artifactId>jscience</artifactId>
<version>4.3.1</version>
</dependency>
Add the import statements for the library.
In file src/main/java/com/heroku/java/GettingStartedApplication.java, on line 2 add:
import org.jscience.physics.amount.Amount;
import org.jscience.physics.model.RelativisticModel;
import javax.measure.unit.SI;
Add a new convert method.
In file src/main/java/com/heroku/java/GettingStartedApplication.java, on line 27 add:
@GetMapping("/convert")
String convert(Map<String, Object> model) {
RelativisticModel.select();
var energy = Amount.valueOf("12 GeV");
model.put("result", "E=mc^2: " + energy + " = " + energy.to(SI.KILOGRAM));
return "convert";
}
Add a new template.
In file src/main/resources/templates/convert.html write:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'hello')}">
<body>
<div class="container">
<p th:text="${result}"/>
</div>
</body>
</html>
Now test locally:
$ ./mvnw clean install
$ heroku local --port=5006
Visit your application’s /convert path at http://localhost:5006/convert. If your changes worked, you see scientific conversions:
E=mc^2: 12 GeV = (2.139194076302506E-26 ± 1.4E-42) kg
Now deploy this local change to Heroku.
Almost every deploy to Heroku follows this same pattern. First, add the modified files to the local Git repository:
$ git add .
Now commit the changes to the repository:
$ git commit -m "Add convert endpoint"
[main ee5ce1b] Add convert endpoint
3 files changed, 26 insertions(+)
create mode 100644 src/main/resources/templates/convert.html
Now deploy as before:
$ git push heroku main
Finally, check that everything is working:
$ heroku open /convert
Provision a Logging Add-on
Add-ons are third-party cloud services that provide out-of-the-box additional services for your application, from persistence through logging to monitoring and more.
By default, Heroku stores 1500 lines of logs from your application, but the full log stream is available as a service. Several add-on providers have logging services that provide things such as log persistence, search, and email and SMS alerts.
In this step, you provision one of these logging add-ons, Papertrail, using its free plan.
Provision the Papertrail logging add-on:
$ heroku addons:create papertrail
Creating papertrail on powerful-refuge-16953... free
Provisioning has been successful
Created papertrail-opaque-90256
Run heroku addons:docs papertrail to view documentation.
The add-on is now deployed and configured for your app. You can list add-ons for your app with this command:
$ heroku addons
Add-on Plan Price Max Price State
----------------------------------------------------------------------------------------------
heroku-postgresql (postgresql-vertical-54832) essential-0 ~$0.007/hour $5/month created
└─ as DATABASE
papertrail (papertrail-opaque-90256) choklad free free created
└─ as PAPERTRAIL
The table above shows add-ons and the attachments to the current app (powerful-refuge-16953) or other apps.
To see this particular add-on in action, visit your application’s Heroku URL a few times. Each visit generates more log messages, which get routed to the Papertrail add-on. Visit the Papertrail console to see the log messages:
$ heroku addons:open papertrail
Your browser opens up a Papertrail web console, showing the latest log events. The interface lets you search and set up alerts.
Start a One-off Dyno
You can run a command, typically scripts and applications that are part of your app, in a one-off dyno using the heroku run command. You can also run a one-off command like java -version:
$ heroku run "java -version"
Running java -version on powerful-refuge-16953... starting, run.4978
Running java -version on powerful-refuge-16953... connecting, run.4978
Running java -version on powerful-refuge-16953... up, run.4978
openjdk version "17.0.20.1" 2026-08-18 LTS
OpenJDK Runtime Environment Zulu17.68+203-CA (build 17.0.20.1+1-LTS)
OpenJDK 64-Bit Server VM Zulu17.68+203-CA (build 17.0.20.1+1-LTS, mixed mode, sharing)
If you receive an error, Error connecting to process, configure your firewall.
Let’s try another example. Run the bash command to open up a shell on a one-off dyno. You can then execute commands there.
$ heroku run bash
Running bash on powerful-refuge-16953... starting, run.7447
Running bash on powerful-refuge-16953... connecting, run.7447
Running bash on powerful-refuge-16953... up, run.7447
~ $ ls -lah
total 104K
drwx------ 10 u19909 dyno 4.0K Oct 16 12:36 .
drwxr-xr-x 11 root root 4.0K Oct 7 22:07 ..
-rw------- 1 u19909 dyno 14 Oct 16 12:33 .env
drwx------ 2 u19909 dyno 4.0K Oct 16 12:33 .github
-rw------- 1 u19909 dyno 398 Oct 16 12:33 .gitignore
drwx------ 6 u19909 dyno 4.0K Oct 16 12:33 .heroku
drwx------ 8 u19909 dyno 4.0K Oct 16 12:33 .jdk
drwx------ 2 u19909 dyno 4.0K Oct 16 12:34 .m2
drwx------ 3 u19909 dyno 4.0K Oct 16 12:33 .mvn
drwx------ 2 u19909 dyno 4.0K Oct 16 12:33 .profile.d
-rw------- 1 u19909 dyno 5.0K Oct 16 12:33 CODE_OF_CONDUCT.md
-rw------- 1 u19909 dyno 1.2K Oct 16 12:33 LICENSE
-rw------- 1 u19909 dyno 62 Oct 16 12:33 Procfile
-rw------- 1 u19909 dyno 2.3K Oct 16 12:33 README.md
-rw------- 1 u19909 dyno 407 Oct 16 12:33 SECURITY.md
-rw------- 1 u19909 dyno 155 Oct 16 12:33 app.json
-rwx------ 1 u19909 dyno 12K Oct 16 12:33 mvnw
-rw------- 1 u19909 dyno 7.8K Oct 16 12:33 mvnw.cmd
-rw------- 1 u19909 dyno 1.8K Oct 16 12:33 pom.xml
drwx------ 3 u19909 dyno 4.0K Oct 16 12:33 src
-rw------- 1 u19909 dyno 103 Oct 16 12:33 system.properties
drwx------ 6 u19909 dyno 4.0K Oct 16 12:34 target
~ $
~ $ exit
exit
Type exit to exit the shell.
Define Config Vars
Heroku lets you externalize configuration by storing data such as encryption keys or external resource addresses in config vars.
At runtime, we expose config vars as environment variables to the application.
For example, modify GettingStartedApplication.java so that the method obtains an energy value from the ENERGY environment variable:
@GetMapping("/convert")
String convert(Map<String, Object> model) {
RelativisticModel.select();
final var result = java.util.Optional
.ofNullable(System.getenv().get("ENERGY"))
.map(Amount::valueOf)
.map(energy -> "E=mc^2: " + energy + " = " + energy.to(SI.KILOGRAM))
.orElse("ENERGY environment variable is not set!");
model.put("result", result);
return "convert";
}
The heroku local command automatically sets up the environment based on the contents of the .env file in your local directory. In the top level directory of your sample project, there’s already a .env file that contains:
ENERGY=20 GeV
Rebuild the app with ./mvnw install. Then run the app with heroku local --port=5006 and visit http://localhost:5006/convert to see the conversion value for 20 GeV.
To set the config var on Heroku, execute the following:
$ heroku config:set ENERGY="20 GeV"
Setting ENERGY and restarting powerful-refuge-16953... done, v5
ENERGY: 20 GeV
View the app’s config vars using heroku config:
$ heroku config
ENERGY: 20 GeV
...
To see this change in action, deploy your changed application to Heroku.
Use a Database
Listing the config vars for your app displays the URL that your app uses to connect to the database, DATABASE_URL:
$ heroku config
DATABASE_URL: postgres://xx:yyy@host:5432/d8slm9t7b5mjnd
...
Heroku also provides a pg command that shows a lot more information:
$ heroku pg
=== DATABASE_URL
Plan: essential-0
Status: Available
Connections: 0/20
PG Version: 18.4
Created: 2026-10-16 11:04
Data Size: 7.91 MB / 1 GB (0.77%) (In compliance)
Tables: 0/4000 (In compliance)
Fork/Follow: Unsupported
Rollback: Unsupported
Continuous Protection: On
Add-on: postgresql-vertical-54832
The example app you deployed already has database functionality. You can visit the page by appending /database to your app’s URL.
Database Output
* Read from DB: 2024-11-27 13:07:53.002632
* Read from DB: 2024-11-27 13:07:54.965283
* Read from DB: 2024-11-27 13:07:55.620596
If you have Postgres installed locally, you can also interact directly with the database. For example, here’s how to connect to the database using psql and execute a query:
$ heroku pg:psql -c "SELECT * FROM ticks"
--> Connecting to postgresql-vertical-54832
tick
----------------------------
2026-10-16 12:36:45.895293
(1 row)
Read more about Heroku PostgreSQL.
Delete Your App
Remove the app from your account. We only charge you for the resources you used.
This action permanently deletes your application and any add-ons attached to it.
$ heroku apps:destroy
You can confirm that your app is gone with this command:
$ heroku apps --all
Next Steps
You now know how to configure and deploy a Java app, view logs, and start a console.
To learn more, see: