← Blog

Deploying Spring Boot: why a Dockerfile is the right answer, not a fallback

Railyard

October 7, 2026

Rails has one web server convention, Django has gunicorn, Laravel has Octane or PHP-FPM — each ecosystem converged on a small, guessable set of ways to actually start a web process. The JVM world never did that the same way: a Spring Boot app might be a plain jar, a WAR in Tomcat, built with Maven or Gradle, started with half a dozen reasonable command shapes depending on how the project’s laid out. Guessing which one wrong doesn’t fail loudly — it fails in a way that looks like the app’s problem instead of the detection’s.

Why Dockerfile-first is the honest choice here

Rather than pretend to auto-detect a JVM build and run command that’s genuinely ambiguous across projects, a repo with its own Dockerfile is the actual first-choice build method on Railyard — checked before Railpack, before Nixpacks, before anything else. For Spring Boot specifically, that’s not a missing feature to work around; it’s the correct tool for a build shape that has too many valid answers to guess safely. A Dockerfile says exactly what JDK version, what build tool, and what final run command the app needs — information no scanner of pom.xml or build.gradle alone can reliably infer.

FROM eclipse-temurin:21-jre
COPY target/my-app.jar app.jar
ENV SERVER_PORT=$PORT
CMD ["java", "-jar", "app.jar"]

The one real requirement: the app has to bind $PORT (Spring Boot reads that from SERVER_PORT or server.port), the same contract every build method on the platform expects.

What still works the same way as any other app

  • Config and secrets: anything read via @Value or application.properties’s ${VAR} syntax is a plain environment variable under Variables, or set with railyard env:set my-app KEY=VALUE — never baked into the image, so the same build runs unchanged from staging through production.
  • Databases: point the app at a Postgres or MySQL add-on from the canvas and wire its connection string into application.properties as a variable — the add-on itself is provisioned the same way regardless of language.
  • Deploy on push: once the Dockerfile builds locally, git push (or railyard create my-app https://github.com/me/my-app) rebuilds and redeploys on every push, same as any other repo.

The actual tradeoff, stated plainly

This means a little more upfront work than a Rails or Django repo gets for free — writing the Dockerfile once, rather than it being inferred. In exchange, the build behaves exactly the way the Dockerfile says it will, every time, which for a JVM build is worth more than a convenience that would’ve guessed wrong on a real percentage of projects.

Full reference: Build methods, Deploy a Docker image.

Push a Spring Boot app right now.

Ship your own Dockerfile and deploy on every push — no magic build detection standing between you and a predictable build.

Free plan, no card, and 14 days of Pro for every new team. One flat price per team.