How to host a Spring Boot app on your own domain

A Spring Boot app is one jar with its web server inside. Hosting it means building that jar, running it with java -jar on the port the host gives you, and telling it about the proxy in front. Here is each part.

7 min readIntermediateUpdated Examples tested with Spring Boot 4.1 on Java 21

This uses Spring Boot 3 or 4 with Spring Web (Tomcat), which need Java 17 or newer.

1. Build one runnable jar

Spring Boot's build plugin packs your code, its libraries and Tomcat into a single jar that runs with java -jar:

Terminal
# Maven: the jar is target/demo-0.0.1-SNAPSHOT.jar
./mvnw package

# Gradle: the jar is in build/libs
./gradlew bootJar

Maven also leaves a small .jar.original next to it; ignore it. Gradle's build task makes a second jar ending in -plain.jar, which holds only your classes and will not start. To stop Gradle making it, add tasks.named("jar") { enabled = false } to build.gradle.kts.

Try the jar before uploading it: java -jar target/demo-0.0.1-SNAPSHOT.jar, then open http://localhost:8080.

2. The port

Spring Boot treats an environment variable named SERVER_PORT as its server.port setting, so on a host that sets SERVER_PORT, as SnowServers does, the app listens on the right port with no configuration at all. It also listens on every interface by default, which a server needs. On a host that uses PORT instead, add this to application.properties:

Text
server.port=${PORT:8080}

3. Behind HTTPS: the proxy headers

When HTTPS is handled in front of your app, Spring receives plain HTTP from the proxy. Out of the box it ignores the proxy's X-Forwarded-* headers: request.getScheme() is http, redirects and links it builds go to http://, and every visitor's address is the proxy's. One setting fixes it:

Text
server.forward-headers-strategy=native

With native, Tomcat believes those headers only from private network addresses, which is where a proxy in front of a container connects from. framework also works and believes them from anyone. On SnowServers, your server's own address and port also answer plain HTTP to anyone who has them, so do not use the visitor address from those headers for anything that matters, such as allow-lists.

4. Settings and secrets, beside the jar

Passwords and API keys do not belong in the jar or in Git. Spring Boot reads an application.properties from the folder it is started in, on top of the one packed in the jar, so put the secret settings there on the server:

Text
# application.properties next to the jar, never in Git
spring.datasource.url=jdbc:mysql://DB_HOST:3306/DB_NAME
spring.datasource.username=DB_USER
spring.datasource.password=DB_PASSWORD

Every setting can also come from an environment variable, written in capitals with underscores: SPRING_DATASOURCE_URL sets spring.datasource.url, the same way SERVER_PORT sets the port. For MySQL, add the com.mysql:mysql-connector-j dependency.

5. Memory

A JVM decides its maximum heap from the machine's memory, and in a container that guess is either too small or too large, so hosts usually set it as a share of the plan with -XX:MaxRAMPercentage; SnowServers uses 75%. The rest is for the JVM itself, threads and the operating system.

In our test a small Spring Web app ran in about 150 to 200 MB with the heap limited the way a 1 GB plan limits it, so 1 GB is comfortable for a modest app. If you see OutOfMemoryError: Java heap space, look for large caches and unbounded lists before buying more memory; a bigger plan raises the heap with it.

Hosting it on SnowServers

  1. Choose a plan on the website plans page and pick A website and Java at checkout. Java 21 is the default; pick at least the version you built with (17, 21 and 25 are offered, under Startup, Docker Image).
  2. Build the jar on your computer or in GitHub Actions: the server runs jars and does not build them. Upload it to the top folder in Files, or over SFTP. A single jar there, or in target or build/libs, is found by itself; with more than one, set Start file on the Startup tab to the right one.
  3. Put your application.properties with the secrets next to it, and server.forward-headers-strategy=native in either file.
  4. Restart from the console. It shows the Spring banner and Tomcat started on port with your server's port.
  5. On the Website tab, add your domain and the DNS records it shows. HTTPS follows on its own, usually within a few minutes.

The proxy in front accepts uploads up to 25 MB each. Spring's own limit is lower, 1 MB per file by default; raise spring.servlet.multipart.max-file-size if your app takes uploads. The Java docs and website docs cover the rest.

Common problems

  • "no main manifest attribute, in ...jar": the wrong jar is running, usually Gradle's -plain.jar. Upload the boot jar, or set Start file.
  • UnsupportedClassVersionError ("class file version 65.0"): the jar was built for a newer Java than the server runs. Version 61 is Java 17, 65 is Java 21, 69 is Java 25. Pick a newer Java in Startup, or build for an older one.
  • Redirects and links go to http://: server.forward-headers-strategy is missing.
  • "This site is not answering": something set server.port or server.address to a fixed value. Remove it and let SERVER_PORT decide.
  • OutOfMemoryError: Java heap space: section 5.

Press / or Ctrl K on any page to search.