Serwer VPS — pierwsze uruchomienie (faza 0)
Dokument prowadzi przez przygotowanie serwera Hostinger KVM 8 pod aplikację po migracji z Cloudflare (plan: docs/plans/migracja-vps-sqlite-coolify.md, pkt 4.13, 7 i „Faza 0”). Część serwerowa jest zautomatyzowana skryptem deploy/bootstrap-vps.sh; reszta to kliknięcia w hPanelu, Coolify i Cloudflare Zero Trust, opisane krok po kroku.
👤 Instrukcja krok po kroku
1. hPanel Hostingera (przed pierwszym logowaniem)
- System: Ubuntu 24.04 LTS (szablon czysty, bez preinstalowanego Coolify — instaluje go skrypt, dzięki czemu wersja i konfiguracja są powtarzalne).
- Klucz SSH: VPS → Settings → SSH keys → Add SSH key. Na swoim komputerze:
Wklej zawartość plikussh-keygen -t ed25519 -C "[email protected]"cat ~/.ssh/id_ed25519.pub
.pubi zaznacz instalację na tym VPS. Sprawdź:ssh root@<IP>wchodzi bez hasła. - Firewall hPanel (VPS → Firewall): jedna reguła
Accept TCP 22z Twojego adresu IP; nic więcej. Porty 80/443/8000 mają być zamknięte — ruch do aplikacji wchodzi przez Cloudflare Tunnel, a firewall hPanel działa przed Dockerem (Docker publikuje porty z pominięciemufw). - Backupy: włącz dzienny backup VPS (dodatek do planu) — obejmie konfigurację Coolify, GlitchTip i wolumeny.
2. Skrypt bootstrap (jedno polecenie jako root)
ssh root@<IP>
apt-get install -y git
git clone --depth 1 --branch develop https://github.com/DMT-Softwares/Acepark_Rezerwacje.git /root/acepark
cd /root/acepark
DEPLOY_USER=deploy \
SSH_PUBLIC_KEY="ssh-ed25519 AAAA... [email protected]" \
ADMIN_IPS="<Twoje IP>" \
./deploy/bootstrap-vps.sh
Skrypt trwa 5–10 minut (instalator Coolify pobiera obrazy). Na końcu wypisuje dalsze kroki. Można go uruchamiać wielokrotnie — każdy krok sprawdza swój stan.
Co robi:
| Krok | Efekt |
|---|---|
| Pakiety | apt upgrade, ufw, fail2ban, unattended-upgrades, curl, git, jq, sqlite3, htop |
| Strefa czasu | host w UTC (kontenery też, TZ=UTC — cały kod dat używa date-fns-tz) |
Użytkownik deploy | sudo bez hasła, klucz z SSH_PUBLIC_KEY (ten sam klucz zostaje u roota — Coolify zarządza hostem przez SSH roota z własnym kluczem) |
sshd | tylko klucze, PermitRootLogin prohibit-password, 4 próby, keepalive |
ufw | deny incoming, allow outgoing, 22 tylko z ADMIN_IPS (bez listy: 22 z limitem prób) |
fail2ban | jail sshd: 5 prób / 10 min → ban na 1 h |
| Aktualizacje | automatyczne poprawki bezpieczeństwa, bez automatycznego restartu |
| Wolumeny | /data/acepark/{dev,qa,prod} i …/backups, właściciel uid 1000 (użytkownik node w obrazie aplikacji) |
| Coolify | oficjalny instalator (cdn.coollabs.io/coolify/install.sh) — instaluje Dockera i podnosi Coolify na localhost:8000; pomijany, gdy już działa |
cloudflared na hoście | tylko gdy podasz CLOUDFLARE_TUNNEL_TOKEN — usługa systemd dla awaryjnego hostname'u SSH (pkt 4.13); tunel aplikacji i tak działa jako usługa w Coolify |
3. Coolify — pierwsze logowanie i ustawienia
- Panel nie jest wystawiony na świat. Zaloguj się przez tunel SSH:
i otwórzssh -L 8000:localhost:8000 deploy@<IP>
http://localhost:8000. Załóż konto administratora (pierwsze konto jest właścicielem instancji). - Settings → Instance: nazwa
acepark-vps, 2FA dla konta administratora, Concurrent builds = 1 (build na tym samym hoście co produkcja, pkt 7). - Sources → GitHub App: utwórz aplikację GitHub dla organizacji
DMT-Softwaresz dostępem doAcepark_Rezerwacje(deploy na push, preview deployments). - Servers → localhost → Proxy: zostaw Traefik. Certyfikaty Let's Encrypt nie są potrzebne — TLS kończy się w Cloudflare (pkt 4.13).
4. Cloudflare Tunnel i Access
- Zero Trust → Networks → Tunnels → Create tunnel (
acepark-vps, typ Cloudflared). Skopiuj token. - W Coolify: Projects → acepark-infra → Add resource → Cloudflared (szablon usługi), wklej token jako sekret
TUNNEL_TOKEN, sieć: ta sama cocoolify-proxy. Deploy. - W tunelu dodaj hostname'y (Zero Trust tworzy rekordy CNAME sam):
coolify.acepark.pl→http://coolify:8000, polityka Access: SSO Google, tylko wskazane konta;dev.acepark.pl,qa.acepark.pl,qa-panel.acepark.pl,klient.acepark.pl,panel.acepark.pl→http://coolify-proxy:80(Access opcjonalnie dladev);glitchtip.acepark.plihc.acepark.pl→http://coolify-proxy:80, Access z bypassem dla/api/*(GlitchTip) i/ping/*(Healthchecks).
- Cloudflare → SSL/TLS: tryb Full (nie „Full (strict)”). Domeny w Coolify wpisuj jako
https://…— inaczej pętla przekierowań. - Od tej chwili panel Coolify jest dostępny pod
https://coolify.acepark.plza Access; tunel SSH z kroku 3 zostaje jako droga awaryjna.
5. GlitchTip i Healthchecks
- Add resource → GlitchTip (szablon): domena
https://glitchtip.acepark.pl, SMTP SendGrid (EMAIL_URL=smtp://apikey:<klucz>@smtp.sendgrid.net:587),GLITCHTIP_DOMAIN, rejestracja zamknięta. Projektaceparkz osobnymi środowiskami; DSN do zmiennych aplikacji w fazie 3. - Add resource → Healthchecks (szablon):
https://hc.acepark.pl, SMTP jak wyżej, projekt per środowisko;PING_KEYkażdego projektu trafi doHEALTHCHECKS_PING_URLkonteneracron(https://hc.acepark.pl/ping/<PING_KEY>), a klucz API doHEALTHCHECKS_API_KEY(dlanode dist/cron.js list --sync-healthchecks).
6. Backupy poza hostem
- Cloudflare R2: bucket
acepark-backups, token API z dostępem tylko do tego bucketu. - Coolify: Settings → Backup (instance backup → S3/R2, codziennie) oraz backup bazy GlitchTip i Healthchecks (zasób → Backups → S3).
- Litestream dla
acepark.dbkonfigurowany w fazie 3 (Compose każdego środowiska), z tym samym bucketem i prefiksem per środowisko.
7. Lista kontrolna po bootstrapie
-
ssh deploy@<IP>działa,ssh root@<IP>działa kluczem, hasło odrzucane. -
sudo ufw statuspokazuje tylko 22 z Twojego IP; w hPanelu 80/443/8000 zamknięte. -
docker pspokazujecoolify,coolify-db,coolify-redis,coolify-proxy. -
https://coolify.acepark.plprzechodzi przez Access,http://<IP>:8000nie odpowiada. -
ls -la /data/acepark— trzy katalogi środowisk z właścicielem uid 1000. - GlitchTip i Healthchecks odpowiadają przez tunel; testowy ping
curl https://hc.acepark.pl/ping/<PING_KEY>/test?create=1tworzy check.
🛠 Dokumentacja techniczna
Dlaczego skrypt, a nie szablon „Coolify” z hPanelu
Szablon Hostingera instaluje Coolify na obrazie, którego wersji i ustawień nie kontrolujemy (użytkownik, firewall, sshd). Skrypt w repozytorium jest przeglądany jak kod, wersjonowany i powtarzalny — ten sam bootstrap zadziała na innym dostawcy, co było jednym z założeń wyboru jednego serwera (łatwa przeprowadzka po migracji).
Firewall: ufw i hPanel
ufw chroni sam host (SSH). Docker wpina własne reguły iptables przed łańcuchem ufw, więc port opublikowany przez kontener (np. 80/443 Traefika Coolify) jest osiągalny z internetu mimo deny incoming. Dlatego zamknięcie 80/443/8000 musi nastąpić w firewallu hPanel (przed maszyną). Ruch do aplikacji wchodzi wyłącznie tunelem, który jest połączeniem wychodzącym z kontenera cloudflared.
Dostęp roota
Coolify zarządza „localhost” przez SSH jako root z kluczem, który sam generuje i dopisuje do /root/.ssh/authorized_keys. Stąd PermitRootLogin prohibit-password zamiast no. Logowanie hasłem jest wyłączone globalnie.
Idempotencja
Każdy krok sprawdza stan: użytkownik tworzony tylko gdy nie istnieje, klucz dopisywany tylko gdy go nie ma, ufw --force reset odtwarza reguły od zera, instalator Coolify jest pomijany, gdy kontener coolify już działa, cloudflared instalowany tylko bez binarki. Ponowne uruchomienie po przerwanym kroku jest bezpieczne.
Co skrypt celowo pomija
- Konfigurację Coolify (konta, GitHub App, zasoby) — to stan w bazie Coolify, robiony przez UI lub API (
https://coolify.acepark.pl/api/v1, token z Keys & Tokens). - Tunel aplikacji — jako usługa w Coolify, żeby restart i logi były w jednym miejscu z resztą zasobów.
- Litestream i Compose środowisk — faza 3 planu (
Dockerfile,docker-compose.yml,litestream.yml).