# 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 size | VPS plan | JVM heap | Concurrency |
| Small API / MVP | VPS Starter (2 GB) | -Xmx512m | ~50 req/sec |
| Production API | VPS Business (4 GB) | -Xmx2g | ~200 req/sec |
| Enterprise / microservices | VPS Enterprise (8+ GB) | -Xmx4g per service | 1000+ req/sec |
## Step 1 — Install JDK
Java 21 LTS is the current long-term-support release (2026).
3
Verify: java -version → should show openjdk version "21..."
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 | -Xmx | OS overhead buffer |
| 2 GB | 256m | 1024m | ~1 GB |
| 4 GB | 512m | 2048m | ~2 GB |
| 8 GB | 1024m | 4096m | ~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
org.springframework.boot
spring-boot-maven-plugin
true
```
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