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:
# Maven: the jar is target/demo-0.0.1-SNAPSHOT.jar
./mvnw package
# Gradle: the jar is in build/libs
./gradlew bootJarMaven 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:
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:
server.forward-headers-strategy=nativeWith 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:
# 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_PASSWORDEvery 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
- 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).
- 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
targetorbuild/libs, is found by itself; with more than one, set Start file on the Startup tab to the right one. - Put your
application.propertieswith the secrets next to it, andserver.forward-headers-strategy=nativein either file. - Restart from the console. It shows the Spring banner and
Tomcat started on portwith your server's port. - 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-strategyis missing. - "This site is not answering": something set
server.portorserver.addressto a fixed value. Remove it and letSERVER_PORTdecide. OutOfMemoryError: Java heap space: section 5.
Did this page help?
Thanks for the feedback.