Przejdź do treści

Ustawienia cookies

Poniżej wypisujemy nazwa po nazwie, co ta strona zapisuje w Twojej przeglądarce. Możesz tu wrócić w każdej chwili; odnośnik „Ustawienia cookies” stoi w stopce każdej strony.

Polityka prywatności §7
isbre
Blog

Jak uruchomić aplikację Node.js na hostingu współdzielonym (bez VPS-a): systemd, port, domena

Krok po kroku: plik startowy i port, zależności przez SSH, dodanie aplikacji w panelu, domena z HTTPS. Co robi za Ciebie system, a co zostaje po Twojej stronie.

· Zespół Isbre

  • Node.js
  • aplikacje
  • systemd
  • SSH

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.

  1. 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 ci na serwerze, a pakiet Start ma tylko SFTP (kopiowanie plików bez powłoki). Da się wgrać gotowy katalog node_modules przez SFTP, ale każda aktualizacja zależności to ponowna wysyłka całego katalogu, więc tego nie polecamy.
  2. Domenę podpiętą do usługi albo decyzję, że aplikacja ma na razie działać tylko wewnętrznie (bez domeny, do testów).
  3. 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. Zawsze process.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, po 127.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 startowy server.js,
  • nasłuch na process.env.PORT i 127.0.0.1,
  • npm ci wykonane 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.

Pytania do tego wpisu? Napisz na bok@isbre.pl. Wszystkie wpisy: blog.