Client Area

Deploying Java and Spring Boot Applications on DomainIndia VPS

ByDomain India Team·DomainIndia Engineering
5 min readPublished 20 Apr 2026Updated 23 Jun 2026335 views

In this article

  • 1Why VPS, not shared
  • 2Step 1 — Install JDK
  • 3Step 2 — Build your Spring Boot JAR
  • 4Step 3 — systemd service
  • 5Step 4 — nginx reverse proxy

Deploying Java and Spring Boot Applications on DomainIndia VPS

TL;DR
Java/Spring Boot runs on any DomainIndia VPS. Package your app as an executable JAR, run it under systemd with a dedicated Java user, front with nginx + Let's Encrypt SSL. This guide covers JDK choice, memory tuning, database config, and going live.

Why VPS, not shared

Java apps are long-running JVM processes consuming 256 MB–2 GB+ RAM. Shared cPanel/DirectAdmin optimises for PHP per-request workloads — the JVM doesn't fit. A DomainIndia VPS gives you the root access, persistent process control, and RAM you need.

Minimum VPS for Spring Boot production:

App sizeVPS planJVM heapConcurrency
Small API / MVPVPS Starter (2 GB)-Xmx512m~50 req/sec
Production APIVPS Business (4 GB)-Xmx2g~200 req/sec
Enterprise / microservicesVPS Enterprise (8+ GB)-Xmx4g per service1000+ req/sec

Step 1 — Install JDK

Java 21 LTS is the current long-term-support release (2026).

  1. SSH into VPS as root
  2. Install OpenJDK 21:

```bash

# AlmaLinux / Rocky

sudo dnf install -y java-21-openjdk-devel

# Ubuntu 22.04+

sudo apt install -y openjdk-21-jdk

```

  1. Verify: java -version → should show openjdk version "21..."
  2. Create app user:

```bash

sudo useradd -r -s /bin/false -m -d /opt/spring-app spring

```

Info

Use LTS versions only. Java 21 is LTS (support until 2031). Java 22/23 are non-LTS — fine for experiments, risky for production. Our OpenJDK packages include security updates via standard OS updates.

Step 2 — Build your Spring Boot JAR

On your laptop:

bash
./mvnw clean package -DskipTests
# Produces target/your-app-0.0.1-SNAPSHOT.jar

Upload:

bash
scp target/your-app-0.0.1-SNAPSHOT.jar root@your-vps:/opt/spring-app/app.jar
sudo chown spring:spring /opt/spring-app/app.jar

Step 3 — systemd service

Create /etc/systemd/system/spring-app.service:

ini
[Unit]
Description=Spring Boot Application
After=network.target postgresql.service

[Service]
Type=simple
User=spring
Group=spring
WorkingDirectory=/opt/spring-app
ExecStart=/usr/bin/java 
    -Xms256m -Xmx1024m 
    -XX:+UseG1GC 
    -XX:+HeapDumpOnOutOfMemoryError 
    -XX:HeapDumpPath=/opt/spring-app/heapdumps 
    -Dspring.profiles.active=production 
    -Dserver.port=8080 
    -jar /opt/spring-app/app.jar
SuccessExitStatus=143
TimeoutStopSec=20
Restart=on-failure
RestartSec=10

# Security
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/opt/spring-app
ProtectHome=true

# Environment
EnvironmentFile=-/opt/spring-app/.env
StandardOutput=journal
StandardError=journal

[Install]
WantedBy=multi-user.target

Create /opt/spring-app/.env:

DB_URL=jdbc:postgresql://localhost/myapp
DB_USER=spring
DB_PASSWORD=changeme
JWT_SECRET=some-random-256-bit-string

Start:

bash
sudo systemctl daemon-reload
sudo systemctl enable --now spring-app
sudo journalctl -u spring-app -f

Step 4 — nginx reverse proxy

/etc/nginx/conf.d/spring.conf:

nginx
upstream spring {
    server 127.0.0.1:8080;
    keepalive 32;
}

server {
    listen 80;
    server_name api.yourcompany.com;

    client_max_body_size 20M;

    location / {
        proxy_pass http://spring;
        proxy_http_version 1.1;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        proxy_read_timeout 60s;
    }
}

Test + reload + Let's Encrypt:

bash
sudo nginx -t && sudo systemctl reload nginx
sudo certbot --nginx -d api.yourcompany.com

Step 5 — Database (PostgreSQL)

bash
sudo dnf install -y postgresql-server postgresql-contrib
sudo postgresql-setup --initdb
sudo systemctl enable --now postgresql
sudo -u postgres psql -c "CREATE DATABASE myapp;"
sudo -u postgres psql -c "CREATE USER spring WITH ENCRYPTED PASSWORD 'changeme';"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE myapp TO spring;"

application-production.yml in your Spring app:

yaml
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USER}
    password: ${DB_PASSWORD}
  jpa:
    hibernate:
      ddl-auto: validate   # or update for dev; never create in prod
    show-sql: false
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQLDialect

Memory tuning

Wrong heap sizing is the #1 cause of Spring Boot crashes on small VPS.

VPS RAM-Xms-XmxOS overhead buffer
2 GB256m1024m~1 GB
4 GB512m2048m~2 GB
8 GB1024m4096m~4 GB

Rule: JVM max heap ≤ (total RAM / 2). The JVM uses non-heap memory (metaspace, native code, threads) that's not counted in -Xmx. Over-sized heap + OOM killer = service restart loop.

Enable XX:+HeapDumpOnOutOfMemoryError so when it does crash, you have a dump to debug with Eclipse MAT.

Build optimisation

Multi-module projects with Maven:

bash
./mvnw -T 4 clean package -DskipTests -Dspring-boot.build-image.skip=true

-T 4 uses 4 parallel threads.

Spring Boot layered JAR (faster Docker rebuilds):

xml
<!-- pom.xml -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <configuration>
        <layers><enabled>true</enabled></layers>
    </configuration>
</plugin>

Unpack the JAR into layers, containerise each layer separately → dependency layers cached, app layer small and fast.

Common pitfalls

FAQ

Q Which JVM — OpenJDK, Oracle JDK, or GraalVM?

OpenJDK for production stability. Oracle JDK requires a commercial licence for business use. GraalVM Community Edition is interesting for low-RAM workloads (native-image reduces to ~30 MB + <100 ms startup), but builds are complex — worth exploring for microservices, overkill for monoliths.

Q Native image (GraalVM) vs traditional JVM?

Native: faster cold start, 5–10× less RAM, harder to build (reflection issues, long compile). JVM: slower start (3–10s), higher RAM, easier build. Start with JVM, move to native only if cold-start or RAM is critical.

Q Tomcat (embedded) vs Netty vs Undertow?

Spring Boot defaults to Tomcat — fine for 95% of apps. Netty is reactive/non-blocking (WebFlux). Undertow is lighter than Tomcat if you're on a 2 GB VPS and need to squeeze RAM.

Q How do I enable HTTPS directly in Spring?

Don't — let nginx handle SSL. Simpler to renew, simpler to tune, standard ops practice.

Q Can I run multiple Spring services on one VPS?

Yes — different ports + different systemd services + nginx server blocks per domain. Plan RAM carefully.

Java/Spring needs a proper VPS with 4+ GB RAM for production workloads. See VPS plans

Was this article helpful?

Your feedback helps us improve our documentation

Still need help? Submit a support ticket