---
title: "Deploy spring-boot-railway-template"
description: "The Java starter that deploys: Spring Boot 4.1 + Postgres in one click"
category: "Starters"
url: https://railway.com/deploy/spring-boot-railway-template
---

# Deploy spring-boot-railway-template

The Java starter that deploys: Spring Boot 4.1 + Postgres in one click

**[Deploy spring-boot-railway-template on Railway](https://railway.com/template/spring-boot-railway-template)**

Machine-readable deploy manifest (JSON, validated by TemplateCI): https://railway.com/deploy/spring-boot-railway-template/manifest.json

- **Creator:** Nameless 's Projects
- **Category:** Starters

## Template content

### Postgres https://devicons.railway.app/i/postgresql.svg

- **Image:** ghcr.io/railwayapp-templates/postgres-ssl:18

### app

- **Source:** https://github.com/lNamelessl/spring-boot-railway-template
- **Health check:** /actuator/health
- **Public domain:** Yes

## Documentation

# Spring Boot on Railway — the Java starter that actually deploys

[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/deploy/spring-boot-railway-template)

One click gives you a production-shaped **Spring Boot 4.1 (Java 25 LTS) application** wired to a
**PostgreSQL 18** database — with schema migrations, a real REST API, and a healthcheck that goes
green on the first deploy. No port errors, no out-of-memory crashes, no version surprises: every
classic Railway-Java failure mode is already handled in this template.

## What's inside

| Service   | What it runs                                                              |
| --------- | ------------------------------------------------------------------------- |
| `app`     | Spring Boot 4.1.1 on Java 25 (Eclipse Temurin), built by a multi-stage Dockerfile (`maven:3.9-eclipse-temurin-25` → `eclipse-temurin:25-jre`) |
| `Postgres`| Railway's official `postgres-ssl:18` image with a persistent volume       |

The `app` service ships a working reference application, not a hello-world stub:

- REST API for a todo list (`GET/POST/PATCH/DELETE /api/todos`) on Spring Data JPA
- Flyway migration (`V1__init.sql`) that creates the schema on first boot
- Spring Boot Actuator with `/actuator/health` wired as Railway's healthcheck
- Graceful shutdown so redeploys don't drop in-flight requests

Everything is pinned: Java 25 LTS in both Docker stages, Spring Boot 4.1.1 in `pom.xml`, the
Postgres 18 image tag. Nothing floats, so a deploy that works today works next month.

## Railway wiring (done for you)

- `SERVER_PORT = ${{PORT}}` binds the JVM to Railway's injected port; the app falls back
  gracefully (`server.port=${SERVER_PORT:${PORT:8080}}` in `application.properties`).
- JVM memory flags in the Dockerfile entrypoint: `-XX:MaxRAMPercentage=75.0
  -XX:+ExitOnOutOfMemoryError` — the heap scales with the container and never fights the OOM killer.
- Healthcheck: `/actuator/health`, 300 s timeout, restart-on-failure (×10).
- Database credentials are auto-generated and injected via variable references —
  `SPRING_DATASOURCE_URL=jdbc:postgresql://${{Postgres.PGHOST}}:${{Postgres.PGPORT}}/${{Postgres.PGDATABASE}}`,
  `SPRING_DATASOURCE_USERNAME=${{Postgres.PGUSER}}`, `SPRING_DATASOURCE_PASSWORD=${{Postgres.PGPASSWORD}}`.
  Zero hardcoded secrets.

# Deploy and Host

## About Hosting

Deploying provisions two services: the Spring Boot `app` (built from the linked GitHub repo with
Docker) and a `Postgres` database with persistent storage. The first build runs a full Maven
build, so expect roughly 3–5 minutes before the healthcheck goes green; subsequent deploys are
faster thanks to Docker layer caching. Hosting cost scales with usage — a typical small instance
runs the JVM at 0.5–1 GB of RAM, which lands around **$5–10/month including the Postgres
service**. After deploy, Railway assigns a public domain; open it to see the app metadata JSON,
then try `curl https://YOUR-DOMAIN/api/todos`.

## Why Deploy

Because every other path to Java on Railway tends to fail in one of four ways, and this template
fixes all four before you click:

1. **Port mismatch** — Spring defaults to 8080 while Railway expects the app to listen on its
   injected `PORT`. This template binds `SERVER_PORT` to `${{PORT}}` and the domain targets it.
2. **OOM kills** — a JVM with default heap settings on a small container gets killed by the
   kernel. `MaxRAMPercentage=75` keeps the heap inside the container.
3. **Failed healthchecks** — Railway needs an endpoint that actually reports readiness.
   Actuator's `/actuator/health` is wired as the deploy gate.
4. **Stale versions** — the old Spring Boot templates on Railway pin Boot 3.3.x and a
   single-stage alpine build. This one pins the current GA line (Boot 4.1.1) and the current LTS
   (Java 25) in a cached multi-stage build.

You also get a database that's actually wired: Flyway migrates on boot, and data survives
redeploys because it lives in Postgres, not in the container.

## Common Use Cases

- **A real starting point for a Spring Boot service on Railway** — swap the `todo` package for
  your own entities and controllers, keep the wiring, push to deploy.
- **REST API backends** for web and mobile apps, with validated inputs and a migrated schema.
- **Enterprise-style reference architecture** — JPA + Flyway + actuator is the layout most Java
  teams already know; use it as a template for internal services.
- **Learning Spring Boot** — the repo has a README with local-dev instructions, a test suite
  that runs without a database, and CI (GitHub Actions) building on every push.

## Dependencies for

### Deployment Dependencies

- A Railway account (the deploy button handles project creation).
- The GitHub repository [lNamelessl/spring-boot-railway-template](https://github.com/lNamelessl/spring-boot-railway-template) — public, MIT-licensed; the `app` service builds from it.
- The `Postgres` service is provisioned by the template itself (official `postgres-ssl:18`
  image); no external database is needed.
- No deploy-form inputs: all variables are pre-wired references, database credentials are
  generated per deployment, and there is nothing to type.

## Troubleshooting

- **Healthcheck fails on first deploy** — the first build is the slow one; check the deploy logs
  for `Started …Application`. If the app started but the check still fails, confirm the domain's
  target port matches `SERVER_PORT` (8080 by default).
- **`Connection refused` to Postgres at boot** — the app can start before Postgres finishes
  booting on the very first deploy. The template sets a 60 s connection-patient Hikari timeout
  and restarts on failure, so it self-heals; press *Redeploy* once if you're impatient.
- **Killed / exit 137** — the JVM outgrew the container. Lower the heap via a `JAVA_OPTS`
  service variable or add memory in the service settings.
- **Build timeout** — free-plan builds can time out during the first dependency download.
  Redeploy: Docker layer caching resumes where the build left off.
- **Flyway errors after you change the schema** — never edit an applied migration; add a new
  `V{n}__*.sql` file instead.

## Bring your own app

Fork the repo, replace the `todo` package with your own code, add your migrations as
`V2__…`, `V3__…`, and keep four things untouched: the port line in `application.properties`, the
healthcheck lines, the `Dockerfile`, and `railway.json`. Push to `main` — Railway redeploys
automatically.


## Similar templates

- [open-excalidraw](https://railway.com/deploy/open-excalidraw) — Self-hostable collaborative drawing built on Excalidraw
- [caring-vibrancy](https://railway.com/deploy/caring-vibrancy) — Deploy and Host caring-vibrancy with Railway
- [Appsmith](https://railway.com/deploy/appsmith-1) — Low-code platform for internal tools, dashboards, and admin panels.

Open this page in a browser: https://railway.com/deploy/spring-boot-railway-template
