Spring Boot na BiedaHostingu

Wstęp: Po co to robimy?

BiedaHosting to hosting współdzielony - nie VPS, nie serwer dedykowany. Mimo to da się na nim uruchomić aplikację Spring Boot. Największy problem? Standardowe JRE waży za dużo na hosting z limitem 250 MB.

Rozwiązanie: używamy jlink żeby zbudować minimalne JRE tylko z modułami których potrzebuje nasza aplikacja. Efekt? JRE schodzi do 60 MB, a całość z fat JARem mieści się w 100 MB.

Czego potrzebujesz:

  • Konto na BiedaHostingu (pakiet bieda25 - 250 MB miejsca)
  • Open JDK 21 zainstalowane lokalnie (do budowania)
  • Maven lub Gradle
  • Dostęp SSH do serwera

Ważna uwaga: To środowisko testowe - nie do produkcji. Aplikacje Java na hostingu współdzielonym mogą być ubijane przez system przy wysokim zużyciu zasobów. Dlatego w tym wpisie omawiamy też watchdoga który automatycznie restartuje aplikację.


Krok 1 - Budowanie aplikacji Spring Boot

Zbuduj aplikację lokalnie do pliku JAR:

mvn clean package -DskipTests

Gotowy plik JAR znajdziesz w folderze target/. Przykładowa nazwa: test-app-1.0-SNAPSHOT.jar Ten poradnik skupia się na architekturze wdrożeniowej, a nie na pisaniu samego kodu biznesowego.


Krok 2 - Budowanie minimalnego JRE przez jlink

Zamiast wysyłać pełne JRE na serwer , budujemy minimalne tylko z modułami których potrzebuje nasza aplikacja Spring Boot.

Najpierw sprawdź jakich modułów potrzebuje Twoja aplikacja:

jdeps --ignore-missing-deps \
      --print-module-deps \
      target/test-app-1.0-SNAPSHOT.jar

Czasami jdeps może mieć problem z zajrzeniem do środka fat jara. Dlatego rozpakuj go do temp_folder i wydaj zmodyfikowaną komendę:


jdeps --ignore-missing-deps --multi-release 21 --print-module-deps -recursive temp_folder/BOOT-INF/classes temp_folder/BOOT-INF/lib/*.jar

Polecenie wylistuje wszystkie potrzebne moduły. Dla typowej aplikacji Spring Boot wynik będzie podobny do:

java.base,java.sql,java.naming,java.desktop,java.management,
java.security.jgss,java.instrument,java.rmi,jdk.unsupported

Teraz zbuduj minimalne JRE:

jlink \
  --add-modules java.base,java.sql,java.naming,java.desktop,\
java.management,java.security.jgss,java.instrument,java.rmi,jdk.unsupported \
  --strip-debug \
  --no-header-files \
  --no-man-pages \
  --compress=zip-9 \
  --output custom-runtime

Jeżeli pracujesz na windows a chcesz przygotowac jre dla linuxa - pobierz jdk 21 dla linux w tar.gz i rozpakuj w dowolnym folderze. Wydaj komende zmodyfikowaną o ścieżke do modułów


jlink --module-path JDK_LINUX_HOME\jmods --add-modules java.base,java.compiler,java.desktop,java.instrument,java.management,java.naming,java.net.http,java.prefs,java.rmi,java.scripting,java.security.jgss,java.sql,jdk.jfr,jdk.unsupported --output jre-linux-mini --strip-debug --no-man-pages --no-header-files --compress=2

Sprawdź rozmiar:

Wynik: około 60 MB

Uwaga: Moduły zależą od tego co używa Twoja aplikacja. Bardziej rozbudowane aplikacje mogą potrzebować więcej modułów - użyj jdeps żeby sprawdzić co jest naprawdę potrzebne.


Krok 3 - Wgrywanie na biedahosting

Wgraj na przez FTP lub panel hostingu na serwer dwa elementy:

  • Folder custom-runtime/ - minimalne JRE
  • Plik JAR - np. test-app-1.0-SNAPSHOT.jar

Docelowa struktura na serwerze:

/home/twojlogin/java/testapp/
├── custom-runtime/
│   └── bin/
│       └── java
└── test-app-1.0-SNAPSHOT.jar

Krok 4 - Uruchomienie aplikacji przez SSH

Zaloguj się przez SSH i utwórz potrzebne foldery:

mkdir -p $HOME/tmp
mkdir -p $HOME/tomcat

Uruchom aplikację przez tmux używając własnego JRE:

tmux new-session -d -s testapp \
  "$HOME/java/testapp/custom-runtime/bin/java \
   -Djava.io.tmpdir=$HOME/tmp \
   -jar $HOME/java/testapp/test-app-1.0-SNAPSHOT.jar \
   --server.port=18080 \
   --server.address=127.0.0.1 \
   --server.tomcat.basedir=$HOME/tomcat \
   >> $HOME/java/testapp/app.log 2>&1"

Kilka ważnych parametrów:

  • custom-runtime/bin/java - używamy własnego minimalnego JRE
  • -Djava.io.tmpdir=$HOME/tmp - katalog tymczasowy w folderze użytkownika
  • --server.address=127.0.0.1 - aplikacja słucha tylko lokalnie
  • --server.tomcat.basedir=$HOME/tomcat - katalog roboczy Tomcata
  • --server.port=18080 - port aplikacji. Wybierze swoje porty!

⚠ Ważna uwaga bezpieczeństwa: Aplikacja nasłuchuje na 127.0.0.1 - czyli tylko lokalnie, nie jest bezpośrednio dostępna z internetu. Jeżeli nie ustawisz tego parametru - każdy w interncie moze dostac się do aplikacji. Pamiętaj o tym. Jednak na hostingu współdzielonym port 18080 jest dostępny dla każdego innego użytkownika tego samego serwera - wystarczy że zna numer portu.

Co to oznacza w praktyce:

  • Nie wystawiaj przez tę aplikację wrażliwych danych osobowych
  • Nie przechowuj haseł ani tokenów dostępowych w odpowiedziach API
  • Jeśli aplikacja wymaga autoryzacji - koniecznie ją włącz
  • To środowisko testowe i deweloperskie - nie produkcyjne

Jeśli potrzebujesz pełnej izolacji - BiedaHosting nie jest odpowiednim miejscem.


Krok 5 - Reverse Proxy w .htaccess

Dodaj plik .htaccess w folderze public_html żeby aplikacja była dostępna pod domeną:

RewriteEngine On
RewriteRule ^(.*)$ http://127.0.0.1:18080/$1 [P,L]

Wchodząc na twojadomena.deploy.net.pl ruch jest przekierowywany do aplikacji Spring Boot na porcie 18080.


Krok 6 - Watchdog

Aplikacje Java na hostingu współdzielonym mogą być ubijane przez system. Poniższy watchdog monitoruje aplikację i automatycznie ją podnosi gdy padnie - wybierając wolny port z listy i aktualizując .htaccess.

Zapisz jako $HOME/java/testapp/watchdog.sh:

#!/bin/bash
# --- konfiguracja ---
APP_NAME="test-app"
JAR_PATH="$HOME/java/testapp/test-app-1.0-SNAPSHOT.jar"
JAVA_BIN="$HOME/java/testapp/custom-runtime/bin/java"
LOG_FILE="$HOME/java/testapp/app.log"
PID_FILE="$HOME/java/testapp/app.pid"
HTACCESS_FILE="$HOME/public_html/.htaccess"
source $HOME/.bash_profile
export TMUX_TMPDIR=$HOME/my_tmux_tmp
 
# lista portów do wykorzystania
PORTS=(18080 18081 18082 18083 18084)
 
# komenda uruchamiająca aplikację
JAVA_CMD="$JAVA_BIN -Djava.io.tmpdir=$HOME/tmp -jar $JAR_PATH"
 
# wzorzec do podmiany w .htaccess
HTACCESS_PATTERN='http://127\.0\.0\.1:[0-9]+'
 
# --- funkcje ---
is_running() {
    if [ -f "$PID_FILE" ]; then
        PID=$(cat "$PID_FILE")
        if ps -p "$PID" > /dev/null 2>&1; then
            return 0
        fi
    fi
    if pgrep -f "$JAR_PATH" > /dev/null 2>&1; then
        return 0
    fi
    return 1
}
 
find_free_port() {
    for PORT in "${PORTS[@]}"; do
        HEX_PORT=$(printf '%04X' "$PORT")
        if ! grep -qi ":$HEX_PORT" /proc/net/tcp; then
            echo "$PORT"
            return 0
        fi
    done
    return 1
}
 
update_htaccess() {
    local NEW_PORT="$1"
    if [ ! -f "$HTACCESS_FILE" ]; then
        log "Brak pliku .htaccess: $HTACCESS_FILE"
        return 1
    fi
    cp "$HTACCESS_FILE" "$HTACCESS_FILE.bak"
    sed -i -E "s|$HTACCESS_PATTERN|http://127.0.0.1:$NEW_PORT|g" "$HTACCESS_FILE"
    log "Zaktualizowano .htaccess -> port $NEW_PORT"
}
 
start_app() {
    local PORT="$1"
    local SESSION_NAME="${APP_NAME}-${PORT}"
    log "Uruchamiam aplikację na porcie $PORT w tmux"
    tmux kill-session -t "$SESSION_NAME" >/dev/null 2>&1
    tmux new-session -d -s "$SESSION_NAME" \
        "$JAVA_CMD --server.port=$PORT --server.tomcat.basedir=$HOME/tomcat --server.address=127.0.0.1 >> '$LOG_FILE' 2>&1"
    sleep 5
    PID=$(pgrep -f "$JAR_PATH.*$PORT" | head -n1)
    if [ -n "$PID" ] && ps -p "$PID" >/dev/null 2>&1; then
        echo "$PID" > "$PID_FILE"
        log "Aplikacja uruchomiona, PID=$PID, sesja=$SESSION_NAME"
        update_htaccess "$PORT"
    else
        log "Nie udało się uruchomić aplikacji"
        return 1
    fi
}
 
timestamp() {
    date '+%Y-%m-%d %H:%M:%S'
}
 
log() {
    echo "[$(timestamp)] $1"
}
 
# --- główna logika ---
if is_running; then
    log "Aplikacja działa"
    exit 0
fi
 
log "Aplikacja nie działa, szukam wolnego portu..."
FREE_PORT=$(find_free_port)
 
if [ -z "$FREE_PORT" ]; then
    log "Brak wolnych portów z listy: ${PORTS[*]}"
    exit 1
fi
 
start_app "$FREE_PORT"

Nadaj uprawnienia i dodaj do crona:

chmod +x $HOME/java/testapp/watchdog.sh

w panelu directadmin przejdz do ustawień crona i ustaw sprawdzania np. co pół godziny

*/30 * * * * /home/twojlogin/java/testapp/watchdog.sh >> /home/twojlogin/java/testapp/watchdog.log 2>&1

Jak działa watchdog?

  1. Co 30 minut cron uruchamia skrypt
  2. Sprawdza czy aplikacja działa przez PID file i pgrep
  3. Jeśli działa - kończy, wszystko OK
  4. Jeśli nie działa - szuka wolnego portu przez /proc/net/tcp
  5. Uruchamia aplikację w nowej sesji tmux na wolnym porcie
  6. Automatycznie aktualizuje .htaccess z nowym portem
  7. Loguje wszystko do watchdog.log

Podsumowanie

Aplikacja Spring Boot działa na hostingu współdzielonym. Kluczem jest jlink- bez tego nie zmieścilibyśmy się na bieda25.

Rozmiar na dysku:

  • custom-runtime (jlink): ~60 MB
  • fat JAR Spring Boot: ~40 MB
  • Razem: ~100 MB - mieści się na bieda25 z zapasem

Co działa:

  • ✔ Aplikacja Spring Boot dostępna przez domenę
  • ✔ Minimalne JRE przez jlink około 60 MB
  • ✔ Automatyczny restart po ubiciu przez system
  • ✔ Dynamiczna zmiana portu i aktualizacja .htaccess
  • ✔ Logowanie zdarzeń

Ograniczenia:

  • ⚠ Hosting testowy - nie do produkcji
  • ⚠ Aplikacja może być ubijana przez system
  • ⚠ Port dostępny dla innych użytkowników serwera
  • ⚠ Moduły jlink zależą od Twojej aplikacji - sprawdź jdeps

Chcesz przetestować? Załóż konto na BiedaHosting.pl - 14 dni za darmo, bez karty kredytowej.