Skip to main content

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)

  1. System: Ubuntu 24.04 LTS (szablon czysty, bez preinstalowanego Coolify — instaluje go skrypt, dzięki czemu wersja i konfiguracja są powtarzalne).
  2. Klucz SSH: VPS → Settings → SSH keys → Add SSH key. Na swoim komputerze:
    ssh-keygen -t ed25519 -C "[email protected]"
    cat ~/.ssh/id_ed25519.pub
    Wklej zawartość pliku .pub i zaznacz instalację na tym VPS. Sprawdź: ssh root@<IP> wchodzi bez hasła.
  3. Firewall hPanel (VPS → Firewall): jedna reguła Accept TCP 22 z 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ęciem ufw).
  4. 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:

KrokEfekt
Pakietyapt upgrade, ufw, fail2ban, unattended-upgrades, curl, git, jq, sqlite3, htop
Strefa czasuhost w UTC (kontenery też, TZ=UTC — cały kod dat używa date-fns-tz)
Użytkownik deploysudo 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)
sshdtylko klucze, PermitRootLogin prohibit-password, 4 próby, keepalive
ufwdeny incoming, allow outgoing, 22 tylko z ADMIN_IPS (bez listy: 22 z limitem prób)
fail2banjail sshd: 5 prób / 10 min → ban na 1 h
Aktualizacjeautomatyczne 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)
Coolifyoficjalny instalator (cdn.coollabs.io/coolify/install.sh) — instaluje Dockera i podnosi Coolify na localhost:8000; pomijany, gdy już działa
cloudflared na hościetylko 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

  1. Panel nie jest wystawiony na świat. Zaloguj się przez tunel SSH:
    ssh -L 8000:localhost:8000 deploy@<IP>
    i otwórz http://localhost:8000. Załóż konto administratora (pierwsze konto jest właścicielem instancji).
  2. Settings → Instance: nazwa acepark-vps, 2FA dla konta administratora, Concurrent builds = 1 (build na tym samym hoście co produkcja, pkt 7).
  3. Sources → GitHub App: utwórz aplikację GitHub dla organizacji DMT-Softwares z dostępem do Acepark_Rezerwacje (deploy na push, preview deployments).
  4. 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

  1. Zero Trust → Networks → Tunnels → Create tunnel (acepark-vps, typ Cloudflared). Skopiuj token.
  2. W Coolify: Projects → acepark-infra → Add resource → Cloudflared (szablon usługi), wklej token jako sekret TUNNEL_TOKEN, sieć: ta sama co coolify-proxy. Deploy.
  3. W tunelu dodaj hostname'y (Zero Trust tworzy rekordy CNAME sam):
    • coolify.acepark.plhttp://coolify:8000, polityka Access: SSO Google, tylko wskazane konta;
    • dev.acepark.pl, qa.acepark.pl, qa-panel.acepark.pl, klient.acepark.pl, panel.acepark.plhttp://coolify-proxy:80 (Access opcjonalnie dla dev);
    • glitchtip.acepark.pl i hc.acepark.plhttp://coolify-proxy:80, Access z bypassem dla /api/* (GlitchTip) i /ping/* (Healthchecks).
  4. Cloudflare → SSL/TLS: tryb Full (nie „Full (strict)”). Domeny w Coolify wpisuj jako https://… — inaczej pętla przekierowań.
  5. Od tej chwili panel Coolify jest dostępny pod https://coolify.acepark.pl za Access; tunel SSH z kroku 3 zostaje jako droga awaryjna.

5. GlitchTip i Healthchecks

  1. 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. Projekt acepark z osobnymi środowiskami; DSN do zmiennych aplikacji w fazie 3.
  2. Add resource → Healthchecks (szablon): https://hc.acepark.pl, SMTP jak wyżej, projekt per środowisko; PING_KEY każdego projektu trafi do HEALTHCHECKS_PING_URL kontenera cron (https://hc.acepark.pl/ping/<PING_KEY>), a klucz API do HEALTHCHECKS_API_KEY (dla node dist/cron.js list --sync-healthchecks).

6. Backupy poza hostem

  1. Cloudflare R2: bucket acepark-backups, token API z dostępem tylko do tego bucketu.
  2. Coolify: Settings → Backup (instance backup → S3/R2, codziennie) oraz backup bazy GlitchTip i Healthchecks (zasób → Backups → S3).
  3. Litestream dla acepark.db konfigurowany 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 status pokazuje tylko 22 z Twojego IP; w hPanelu 80/443/8000 zamknięte.
  • docker ps pokazuje coolify, coolify-db, coolify-redis, coolify-proxy.
  • https://coolify.acepark.pl przechodzi przez Access, http://<IP>:8000 nie 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=1 tworzy 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).