2026-04-05·6 min read·sota.io team

Deploy Java & Spring Boot to Europe — EU Hosting for JVM Apps in 2026

Java remains the most widely used programming language in European enterprise — banking, insurance, healthcare, logistics, and SAP integrations run on the JVM. Spring Boot is the framework of choice for the vast majority of these systems. Yet when it comes to deployment, most Java developers still reach for AWS Elastic Beanstalk (us-east-1 by default) or spend days configuring Kubernetes on a rented server.

This guide shows how to deploy a Spring Boot application to European infrastructure in minutes using sota.io — no YAML sprawl, no Kubernetes, no AWS account required.

Why EU Hosting Matters for Java Applications

Java is dominant in industries where data residency is not optional. The Schrems II ruling (CJEU C-311/18) invalidated the EU-US Privacy Shield and created binding obligations for data processors: personal data processed for EU residents must remain under EU jurisdiction. AWS's default regions (us-east-1, us-west-2) are operated by Amazon.com Inc., a US company subject to the Cloud Act.

For banking, healthcare, and insurance applications — precisely the industries where Spring Boot dominates — this is not a theoretical concern. It is a compliance requirement enforced by BaFin (Germany), FCA (UK), and equivalent regulators across DACH and France.

sota.io runs on Hetzner infrastructure in Germany. No data leaves the EU. No configuration required to achieve this — it is the default.

What You Need

Spring Boot's embedded Tomcat means no application server setup. The whole application is a single JAR.

Step 1: Write a Production Dockerfile

Spring Boot builds to a fat JAR. A multi-stage Dockerfile keeps the final image lean:

# Build stage — JDK 21 with Maven
FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn -q clean package -DskipTests

# Runtime stage — JRE only (~180MB vs ~350MB with JDK)
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

For Gradle projects, replace the build stage:

FROM eclipse-temurin:21-jdk-alpine AS build
WORKDIR /app
COPY build.gradle settings.gradle ./
COPY src ./src
RUN ./gradlew -q bootJar

FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=build /app/build/libs/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]

Step 2: Configure Port and Database

Spring Boot reads configuration from environment variables automatically. sota.io injects PORT at runtime — configure your app to use it:

# src/main/resources/application.properties
server.port=${PORT:8080}

Or in application.yml:

server:
  port: ${PORT:8080}

PostgreSQL — Auto-Injection

When you attach a managed PostgreSQL instance in sota.io, the following variables are injected automatically:

PGHOST, PGPORT, PGUSER, PGPASSWORD, PGDATABASE

Spring Boot's auto-configuration picks these up via spring.datasource:

spring.datasource.url=jdbc:postgresql://${PGHOST}:${PGPORT}/${PGDATABASE}
spring.datasource.username=${PGUSER}
spring.datasource.password=${PGPASSWORD}
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect

Add the PostgreSQL JDBC driver to pom.xml:

<dependency>
  <groupId>org.postgresql</groupId>
  <artifactId>postgresql</artifactId>
  <scope>runtime</scope>
</dependency>

Flyway Migrations on Startup

For automatic schema management, Flyway runs migrations before the application starts:

<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
</dependency>
spring.flyway.enabled=true
spring.flyway.locations=classpath:db/migration

Migrations in src/main/resources/db/migration/V1__init.sql run automatically at container startup. No manual intervention.

Step 3: Deploy to sota.io

npm install -g @sota-io/cli
sota deploy

The CLI packages your Dockerfile and source, builds it on sota.io infrastructure, and returns a live HTTPS URL. The first build takes 2–3 minutes (Maven dependency download + compilation). Subsequent deploys are faster as layers are cached.

Alternatively, connect your GitHub repository in the sota.io dashboard for automatic deploys on push.

Step 4: JVM Memory Tuning

The default JVM heap sizing algorithm uses 25% of available memory for the heap. On a 512MB container this is 128MB — tight for Spring Boot with Hibernate and a connection pool. Add explicit flags to your entrypoint:

ENTRYPOINT ["java", \
  "-XX:+UseContainerSupport", \
  "-XX:MaxRAMPercentage=75.0", \
  "-Djava.security.egd=file:/dev/./urandom", \
  "-jar", "app.jar"]

-XX:+UseContainerSupport (default since JDK 8u191) ensures the JVM reads cgroup memory limits rather than the host's total RAM. -XX:MaxRAMPercentage=75.0 allows the heap to use up to 75% of the container's allocated memory — appropriate for a container running a single Java process.

-Djava.security.egd=file:/dev/./urandom prevents slow startup on Linux containers due to blocking entropy reads.

Performance: GraalVM Native vs JVM

For latency-sensitive microservices, GraalVM Native Image compiles Spring Boot to a native binary:

FROM ghcr.io/graalvm/native-image:21 AS build
WORKDIR /app
COPY pom.xml .
COPY src ./src
RUN mvn -q -Pnative native:compile

FROM debian:bookworm-slim
WORKDIR /app
COPY --from=build /app/target/app ./app
EXPOSE 8080
ENTRYPOINT ["./app"]

Startup time drops from ~3 seconds (JVM) to ~50ms (native). Memory usage drops from ~150MB to ~30MB. Trade-off: build times increase to 5–10 minutes and some reflection-heavy libraries require additional configuration hints.

For most Spring Boot applications, the standard JVM image is the right choice. Reserve GraalVM native for high-traffic microservices where cold-start and memory density matter.

Comparison: sota.io vs AWS Elastic Beanstalk vs Railway

Featuresota.ioAWS Elastic BeanstalkRailway
Default regionGermany (EU)us-east-1 (US)US default
EU data residencyYesExtra config + costPaid plan
GDPR DPA availableYesComplex (AWS DPA)Limited
Java auto-detectionYes (via Dockerfile)YesYes
Managed PostgreSQLIncludedSeparate RDS costPaid add-on
PricingFlat €9/moComplex (EC2+ALB+S3)Usage-based USD
Spring Boot startupJVM warmup ~3sJVM warmup ~3sJVM warmup ~3s

Honest comparison: AWS Elastic Beanstalk wins for teams already invested in the AWS ecosystem — IAM roles, CloudWatch, VPC peering with RDS. sota.io wins for EU teams building new services where GDPR compliance and simple pricing matter more than AWS ecosystem depth.

Get Started

Free tier: 5 projects, 256 MB RAM, hosted in Germany. No credit card required.

Join the waitlist at sota.io →

Your Spring Boot application runs in the EU by default. No aws configure --region eu-central-1 needed.


See also: Deploy .NET & ASP.NET Core to Europe · Railway Alternative — EU-Native Hosting · Deploy Python to Europe