A minimal full-stack app used to demonstrate deploying to a VPS with Docker and CI/CD.
backend/– Spring Boot 4.1 (Java 25, Maven)frontend/– React 19 + Vite 8, plain CSS
- Java 25
- Node.js 20.19+ (or 22.12+)
You don't need Maven installed – the backend ships with the Maven Wrapper (mvnw / mvnw.cmd).
Use two terminals: one for the backend, one for the frontend.
1. Backend (http://localhost:8080)
macOS / Linux
cd backend
./mvnw spring-boot:runWindows (PowerShell)
cd backend
.\mvnw.cmd spring-boot:runWindows (Command Prompt)
cd backend
mvnw.cmd spring-boot:run2. Frontend (http://localhost:5173)
Same on every OS:
cd frontend
npm install
npm run devOpen http://localhost:5173. The Vite dev server proxies /api to the backend on port 8080.
cp .env.example .env # then edit the values
docker compose up -d --buildOpen http://localhost. After changing .env, run docker compose up -d again to apply it (docker compose restart does not pick up changes). Stop everything with docker compose down.
The Nginx config used in production is in frontend/nginx.conf.
| Endpoint | Description |
|---|---|
GET /api/message |
Returns message, version, serverTime |
GET /actuator/health |
Health check |
| Variable | Default |
|---|---|
APP_MESSAGE |
Hello from EmbarkX |
APP_VERSION |
local |
Variables are read once, when the backend starts. Set them in the same terminal you start the backend from, then restart the backend so the new values take effect.
One-off, for a single run:
APP_MESSAGE="Hello from my Mac" APP_VERSION="1.0.0" ./mvnw spring-boot:runOr for the whole terminal session (note the export – without it the variable is not passed to the app):
export APP_MESSAGE="Hello from my Mac"
export APP_VERSION="1.0.0"
./mvnw spring-boot:run$env:APP_MESSAGE = "Hello from Windows"
$env:APP_VERSION = "1.0.0"
.\mvnw.cmd spring-boot:runNo quotes around the value – CMD would include them in the message.
set APP_MESSAGE=Hello from Windows
set APP_VERSION=1.0.0
mvnw.cmd spring-boot:runThis doesn't rely on the shell's environment at all:
./mvnw spring-boot:run -Dspring-boot.run.arguments="--APP_MESSAGE='Hello from args' --APP_VERSION=1.0.0"On Windows, use .\mvnw.cmd (PowerShell) or mvnw.cmd (CMD) in place of ./mvnw.
cd backend
./mvnw clean package
# macOS / Linux
APP_MESSAGE="Hello from the JAR" APP_VERSION="1.0.0" java -jar target/backend-0.0.1-SNAPSHOT.jar# Windows (PowerShell)
$env:APP_MESSAGE = "Hello from the JAR"; $env:APP_VERSION = "1.0.0"
java -jar target\backend-0.0.1-SNAPSHOT.jarIDE run configurations do not inherit variables set in a terminal. Add them in the run configuration instead:
- IntelliJ: Run → Edit Configurations →
BackendApplication→ Environment variables →APP_MESSAGE=Hello from IntelliJ;APP_VERSION=1.0.0 - VS Code: add
"env": { "APP_MESSAGE": "Hello from VS Code", "APP_VERSION": "1.0.0" }to the launch configuration in.vscode/launch.json
curl http://localhost:8080/api/message(PowerShell: Invoke-RestMethod http://localhost:8080/api/message)
An older backend instance is probably still running on port 8080. The new one then fails with "Port 8080 was already in use", and you keep talking to the old one. Stop it, then start again:
- macOS / Linux:
lsof -i :8080to find the PID, thenkill <PID> - Windows:
netstat -ano | findstr :8080to find the PID, thentaskkill /PID <PID> /F
cd backend && ./mvnw clean package # -> backend/target/backend-0.0.1-SNAPSHOT.jar
cd frontend && npm run build # -> frontend/dist/On Windows, use mvnw.cmd clean package for the backend.