Aplikacja w Node.js na hostingu współdzielonym u nas to zwykły proces w Twoim kontenerze,
nadzorowany przez systemd i wystawiony pod Twoją domeną przez serwer WWW Apache. Nie stawiasz
serwera, nie konfigurujesz zapory ani certyfikatów. Stawiasz kod. Ten wpis pokazuje
dokładnie, co się dzieje między git clone a działającą stroną pod Twoją domeną po HTTPS,
i gdzie leżą granice.
Czego potrzebujesz, zanim zaczniesz
Trzech rzeczy.
- Pakiet z powłoką SSH. Node zaczyna się u nas od pakietu Standard, a powód jest
techniczny, nie handlowy: zależności instalujesz komendą
npm cina serwerze, a pakiet Start ma tylko SFTP (kopiowanie plików bez powłoki). Da się wgrać gotowy katalognode_modulesprzez SFTP, ale każda aktualizacja zależności to ponowna wysyłka całego katalogu, więc tego nie polecamy. - Domenę podpiętą do usługi albo decyzję, że aplikacja ma na razie działać tylko wewnętrznie (bez domeny, do testów).
- Kod, który nasłuchuje na porcie ze zmiennej
PORT. To jedyny warunek po stronie aplikacji i za chwilę wyjaśnimy, dlaczego.
Runtime jest w każdym kontenerze: linie Node.js 20, 22 i 24 z obrazu serwera, domyślnie 22. Wersję wybierasz osobno dla każdej aplikacji, w panelu, a lista do wyboru jest czytana z Twojego kontenera, więc nie wybierzesz wersji, której tam nie ma.
Krok 1: plik startowy i port
Konwencja jest jedna i nie ma pola do wypełnienia: kod leży w katalogu ~/apps/<nazwa>,
plikiem startowym jest server.js, a aplikacja nasłuchuje na porcie z process.env.PORT,
na adresie lokalnym. Numeru portu nie wybierasz. Przydzielamy go z puli 3001 do 3099 na Twoim
kontenerze przy dodawaniu aplikacji i wpisujemy do środowiska procesu; serwer WWW kieruje na
niego ruch z domeny.
Najkrótszy server.js, który spełnia oba warunki:
import { createServer } from 'node:http';
const port = Number(process.env.PORT);
createServer((req, res) => {
res.setHeader('content-type', 'text/plain; charset=utf-8');
res.end('Działa: ' + req.url);
}).listen(port, '127.0.0.1', () => {
console.log('nasłuchuję na porcie ' + port);
});
Z Expressem to samo w jednej linii: app.listen(process.env.PORT, '127.0.0.1'). Frameworki,
które same czytają PORT (Next.js, Fastify z konfiguracją, Nest), wymagają tylko tego, żeby
skrypt startowy nazywał się server.js albo żeby server.js je uruchamiał.
Dwie pułapki z pierwszych dni:
- Port wpisany na sztywno (
listen(3000)). Aplikacja wstanie, ale serwer WWW szuka jej na porcie z puli, więc pod domeną dostaniesz błąd bramy. Zawszeprocess.env.PORT. - Nasłuch na wszystkich interfejsach (
0.0.0.0). Nie jest potrzebny: ruch wchodzi do aplikacji wyłącznie przez serwer WWW w tym samym kontenerze, po127.0.0.1.
Krok 2: wgranie kodu i instalacja zależności przez SSH
Dane do połączenia (adres serwera, port, login) znajdziesz w panelu, w sekcji SFTP swojej
usługi; tam też włączasz powłokę, jeśli jeszcze jej nie masz. Logujesz się hasłem albo
kluczem, oba sposoby są włączone. W kontenerze czekają git, rsync, tmux i sqlite3,
więc kod pobierzesz wprost z repozytorium:
mkdir -p ~/apps && cd ~/apps
git clone ADRES_REPOZYTORIUM sklep
cd sklep
node -v # 22.x, domyślna linia z obrazu
npm ci --omit=dev # zależności produkcyjne z package-lock.json
npm run build # jeśli projekt ma krok budowania
Chcesz budować inną linią niż domyślna? Binarki wszystkich linii leżą w /opt/nodejs:
/opt/nodejs/24/bin/npm ci zainstaluje zależności Node 24. Ta sama wersja, którą potem
wybierzesz w panelu, uruchomi proces, więc paczki natywne (na przykład sharp, bcrypt)
zbudują się pod właściwe ABI.
Budowania po stronie serwera nie ma: nie ma wyzwalacza na wysłanie zmian do repozytorium
i nie kompilujemy kodu za Ciebie. Każde wdrożenie to git pull, npm ci, ewentualnie
npm run build i restart z panelu. Dla wielu małych aplikacji to zaleta: widzisz każdy krok
i żaden nie dzieje się w tle bez Ciebie.
Krok 3: dodanie aplikacji w panelu
W usłudze otwórz Aplikacje i kliknij Dodaj aplikację. Formularz ma pięć pól:
| Pole | Co wpisać |
|---|---|
| Nazwa | małe litery, cyfry, myślnik; to także nazwa katalogu w ~/apps i nazwa procesu, na przykład sklep |
| Środowisko | Node.js |
| Wersja | 22 (zalecana), 20 albo 24 |
| Domena | jedna z domen usługi albo „Bez domeny (tylko wewnętrznie)” |
| Ścieżka | / = cała domena; albo podścieżka w rodzaju /api |
Po zatwierdzeniu dzieje się, w tej kolejności: przydział portu, zapis pliku środowiska
z PORT i NODE_ENV=production, utworzenie usługi systemd redicon-app@sklep, wpięcie
w domenę i start procesu. Potem czekamy 5 sekund i sprawdzamy, czy proces się utrzymał.
Jeśli nie, dodanie kończy się błędem, a w komunikacie dostajesz ogon logu aplikacji. To
jedyny ekran, na którym dowiadujesz się, dlaczego Twój kod nie wstał, więc nie zastępujemy
go zielonym znaczkiem.
Najczęstsze powody odmowy przy pierwszym dodaniu: brak server.js w katalogu, brak
node_modules (pominięty krok 2) i błąd w kodzie przy starcie. Wszystkie trzy widać w ogonie
logu.
Krok 4: domena, HTTPS i websockety
Przed aplikacją stoi Apache w Twoim kontenerze. Przyjmuje ruch z domeny i przekazuje go
na port aplikacji, a dla klienta wygląda to jak zwykła strona pod https://. Certyfikat
Let’s Encrypt jest ten sam, co dla stron PHP, i odnawia się sam. Certyfikat da się wystawić
dopiero wtedy, gdy domena wskazuje już na nas; do czasu wydania przeglądarka może pokazać
ostrzeżenie o certyfikacie, a my przez pierwszą godzinę sprawdzamy co pięć minut, czy domena
jest już u nas.
Websockety przechodzą przez to samo wpięcie, bez osobnej konfiguracji.
Aplikacja może stać pod korzeniem domeny albo pod podścieżką. Przy /api serwer WWW zdejmuje
prefiks, więc Twój kod widzi /x, a nie /api/x; żądanie GET /api bez ukośnika dostaje
przekierowanie na /api/. Kilka aplikacji na jednej domenie jest możliwe, jeśli stoją na
rozłącznych ścieżkach (/api i /panel). Aplikacja pod / zajmuje domenę całą i druga już
się nie zmieści; próba wpięcia nachodzącej ścieżki kończy się odmową z nazwą kolidującej
aplikacji.
Co robi systemd, a czego nie robi
Aplikacja jest zwykłą usługą systemu, nie procesem pod pm2. Wynika z tego kilka rzeczy, które warto znać przed pierwszą awarią.
Wstaje po restarcie serwera i po własnym upadku. Po awarii system podnosi proces z rosnącym odstępem: 2, 4, 8, 15, 30 i wreszcie 60 sekund między próbami. Odstęp rośnie, bo przyczyną bywa coś po naszej stronie, na przykład restart wspólnej bazy, i aplikacja ma wstać sama, gdy przyczyna minie. Awaria, która mija w ciągu kilku minut, leczy się więc bez Ciebie.
Trwała awaria kończy się stanem „nie działa”. Limit to 12 prób w oknie 15 minut. Po nim
proces przestaje być podnoszony, panel pokazuje „nie działa” i ogon logu, a Ty naprawiasz kod
i klikasz Restartuj. Ten przycisk robi więcej niż systemctl restart: zeruje
licznik nieudanych startów i po 5 sekundach sprawdza, czy proces naprawdę stoi.
Pojedynczy proces ma sufit pamięci: 60% RAM kontenera. Reszta zostaje dla serwera WWW i PHP, żeby jedna aplikacja nie położyła strony stojącej obok niej na tym samym koncie. Liczby dla każdego pakietu podaje strona o hostingu Node.js.
Logi procesu masz w panelu. Przycisk Logi przy aplikacji pokazuje ostatnie linie
wypisane przez proces (console.log, stack trace). To co innego niż ekran logów domeny,
gdzie są access log i error log Apache. Jeśli potrzebujesz dłuższej historii, pisz logi
aplikacji także do własnego pliku w jej katalogu; to Twój katalog i Twoja rotacja.
Zmienne środowiskowe, klucze API i baza
Plik środowiska usługi (PORT, NODE_ENV) odtwarzamy przy każdej zmianie ustawień
aplikacji, więc Twoje dopiski by z niego zniknęły w najmniej spodziewanym momencie. Własne
zmienne trzymaj w pliku swojej aplikacji, na przykład .env w katalogu ~/apps/sklep,
wczytywanym przez dotenv albo process.loadEnvFile() na starcie. Katalog jest Twój i poza
Tobą nikt do niego nie zagląda.
Bazę zakładasz w panelu: MariaDB albo PostgreSQL, do wyboru przy tworzeniu. Z internetu baz
nie wystawiamy; aplikacja łączy się z nimi z Twojego kontenera, a Ty z przeglądarki, przez
panel. Dane dostępowe wpisujesz do swojego .env, jak wyżej.
Zmiana wersji Node
W panelu, przy aplikacji, przycisk Edytuj otwiera okno „Zmień środowisko aplikacji”:
przełączasz w nim wersję, a proces startuje na nowo z nowym interpreterem. Paczki natywne z node_modules są
skompilowane pod ABI konkretnej linii, więc po zmianie wykonaj przez SSH npm rebuild (albo
rm -rf node_modules && /opt/nodejs/24/bin/npm ci). Panel przypomina o tym przed
zatwierdzeniem.
Linia 20 zakończyła cykl wsparcia 30 kwietnia 2026 i nie dostaje już poprawek bezpieczeństwa. Trzymamy ją, żeby starsze aplikacje w ogóle wstały; nową stawiaj na 22 albo 24.
Czego tu nie ma
- Dockera. Nie uruchamiamy Twojego
Dockerfile; aplikacja startuje jako zwykły proces z katalogu, który wgrywasz. - Budowania po wysłaniu zmian do repozytorium. Instalacja i build to Twoje komendy przez SSH.
- Redisa i Memcached jako usługi. Kolejka i cache po stronie aplikacji muszą oprzeć się na czymś, co uruchomisz u siebie w kontenerze, albo na bazie danych.
- Wersji Node spoza obrazu w roli runtime. Inną linię zainstalujemy Ci na życzenie w katalogu domowym do pracy przez SSH, ale aplikacja prowadzona z panelu chodzi wyłącznie na wersjach z obrazu (20, 22, 24).
Lista kontrolna przed pierwszym uruchomieniem
- pakiet z powłoką SSH (Standard albo wyżej), powłoka włączona w panelu,
- kod w
~/apps/<nazwa>, plik startowyserver.js, - nasłuch na
process.env.PORTi127.0.0.1, npm ciwykonane tą samą linią Node, którą wybierzesz w panelu,- własne zmienne w pliku aplikacji, nie w pliku środowiska usługi,
- domena podpięta do usługi i wskazująca na nas, jeśli aplikacja ma być publiczna.
Gdy któryś punkt nie zgadza się z Twoim projektem, napisz do nas przed zamówieniem. Odpiszemy, czy i jak to u nas wstanie, a jeśli nie wstanie, powiemy to wprost.