SwiftWave — self-hostowany PaaS jako alternatywa dla Heroku
Lekki PaaS w Go, który na dowolnym VPS-ie stawia Docker Swarm, HAProxy, własny proxy UDP i certyfikaty Let's Encrypt — a potem pozwala wdrażać aplikacje z gita, obrazu albo katalogu. Rozbieramy architekturę odczytaną z kodu, realną procedurę instalacji, snapshoty wolumenów na S3 i webhooki CI. Do tego uczciwa ocena tempa projektu: 893 gwiazdki, jeden imienny opiekun i ośmiomiesięczna przerwa między wydaniami — bo przy wyborze PaaS-a to waży więcej niż lista funkcji.
Wdrożenie aplikacji w Laravelu na własnym serwerze to zawsze ta sama lista. Reverse proxy z certyfikatem. Kontenery albo systemd. Pobranie kodu z gita i przebudowanie obrazu. Zmienne środowiskowe w miejscu, które nie jest repozytorium. Wolumen na pliki użytkowników. Kolejki jako osobny proces. Harmonogram. Backup. Restart po awarii. Monitoring, żeby wiedzieć, że awaria była.
Robi się to raz porządnie i potem kopiuje między projektami — a każda kopia po pół roku różni się od oryginału, bo w jednym projekcie ktoś poprawił konfigurację proxy, a w drugim nie. Po trzech latach mamy pięć serwerów z pięcioma wariantami tego samego pomysłu i dokumentacją w głowie jednej osoby.
PaaS rozwiązuje to interfejsem: jest panel, jest przycisk, jest przewidywalny wynik. Tylko że Heroku, Vercel i Render kosztują proporcjonalnie do ruchu i wynoszą dane poza nasz serwer — co przy części klientów jest po prostu wykluczone.
SwiftWave (github.com/swiftwave-org/swiftwave) próbuje wejść w tę lukę: self-hostowany, lekki PaaS napisany w Go, który instaluje się pakietem systemowym na dowolnym VPS-ie i stawia komplet — orkiestrację, reverse proxy, certyfikaty i panel. Poniżej: co realnie potrafi, jak jest zbudowany (architekturę odczytamy z kodu, bo mówi więcej niż strona projektu), jak to wygląda przy aplikacji w Laravelu — oraz uczciwa ocena tempa rozwoju, bo przy narzędziu, na którym ma stać produkcja, jest to ważniejsze od listy funkcji.
Stan projektu — czytaj to najpierw
Dane z API GitHuba na 20 września 2026:
- 893 gwiazdki i 66 forków. To jest projekt mały — dla porównania, narzędzia z tej samej kategorii mają dziesiątki tysięcy gwiazdek,
- repozytorium założone 6 sierpnia 2023, kod w Go, licencja Apache-2.0, 24 otwarte zgłoszenia,
- najnowsze wydanie
2.23.1-1z 2 sierpnia 2026, przed nim2.22.4-1z 2 maja 2026, a przed nim2.22.3-1z 19 września 2025. Czyli między wrześniem 2025 a majem 2026 nie było żadnego wydania, - w ostatnich dwóch miesiącach 17 commitów, z czego praktycznie wszystkie z jednego dnia — 2 sierpnia — a znaczna część to podbicia zależności od Dependabota,
- polityka bezpieczeństwa wskazuje jednego imiennego opiekuna jako punkt kontaktowy dla zgłoszeń podatności.
Trzeba to zinterpretować uczciwie, bo obie skrajności byłyby nieprawdziwe. Projekt nie jest martwy — w sierpniowej serii pojawiły się realne, sensowne poprawki: dławienie nieudanych prób logowania, walidacja nazw domen przed przekazaniem ich do konfiguracji HAProxy oraz usuwanie zawieszonych połączeń z puli SSH zamiast blokowania się na nich. To są zmiany osoby, która rozumie, gdzie w takim systemie boli.
Ale nie jest to też produkt z zespołem i przewidywalnym rytmem wydań. To projekt rozwijany w seriach, z przerwami liczonymi w tygodniach i miesiącach, przy jednym głównym opiekunie i społeczności rzędu dziewięciuset gwiazdek. Przy wyborze narzędzia, które ma trzymać produkcję klienta przez trzy lata, jest to czynnik do policzenia, nie do pominięcia.
Wracamy do tego na końcu, przy alternatywach — bo pytanie nie brzmi „czy SwiftWave działa" (działa), a „czy w tej kategorii jest coś, co daje to samo przy mniejszym ryzyku".
Co dostajemy
Lista funkcji jest zaskakująco kompletna na projekt tej skali:
- Trzy źródła wdrożenia — z repozytorium gita, z obrazu Dockera albo z lokalnego katalogu,
- Automatyczny generator konfiguracji Dockera — rozpoznaje język i framework projektu i generuje Dockerfile. Dla prostych aplikacji zdejmuje pierwszy krok, na którym ludzie się zatrzymują,
- Wbudowany katalog aplikacji — wybór gotowej aplikacji, wypełnienie formularza, wdrożenie w kilka minut,
- Trwałe wolumeny z obsługą lokalnych, NFS, CIFS i Samby — czyli pliki użytkowników mogą leżeć na współdzielonym magazynie, co w trybie klastrowym jest warunkiem sensownego działania,
- Snapshoty wolumenów z zapisem lokalnym albo na S3, z odtwarzaniem. To jest funkcja, której w tej kategorii często brakuje,
- Ingress — HAProxy do ruchu TCP, HTTP i HTTPS oraz własny proxy UDP do ruchu UDP,
- Certyfikaty Let's Encrypt z automatycznym odnawianiem i rejestracją własnych domen,
- Dwa tryby pracy — standalone na jeden serwer i cluster na wiele serwerów przy większym ruchu,
- Monitoring (Stats-Ninja) — CPU, pamięć, wejście-wyjście sieci i dysku, per serwer i per aplikacja,
- Webhook CI — automatyczne ponowne wdrożenie po zmianie w gicie albo po pojawieniu się nowego obrazu.
Projekt deklaruje przy tym zużycie około 180 MB pamięci razem z komponentami i zgodność z architekturami AMD64, ARM64 i ARMv7 — to ostatnie oznacza, że działa na Raspberry Pi, co przy PaaS-ie nie jest oczywiste.
Architektura odczytana z kodu
Strona projektu mówi, co narzędzie robi. Struktura repozytorium mówi, jak — i to jest informacja, na podstawie której da się ocenić, czy rozwiązanie pasuje do naszych warunków.
Pod spodem jest Docker Swarm, nie Kubernetes i nie samo Compose. Widać to wprost w kodzie modułu zarządzania kontenerami, który importuje api/types/swarm z SDK Dockera i operuje na usługach przez ServiceInspectWithRaw. To jest decyzja o dużych konsekwencjach, do której wracamy w pułapkach.
Moduły najwyższego poziomu składają się na czytelny podział odpowiedzialności:
container_manager— orkiestracja usług na Swarmie,haproxy_manageriudp_proxy_manager— generowanie konfiguracji ingressu, osobno dla TCP/HTTP i osobno dla UDP,ssl_manager— obsługa ACME, na bibliotecemholt/acmez,git_manager— pobieranie kodu, nago-git, czyli bez zależności od binarki gita na hoście,docker_config_generator— rozpoznawanie stacku i generowanie Dockerfile'a,ssh_toolkit— i to jest moduł kluczowy dla trybu klastrowego,task_queueipubsub— kolejka zadań i komunikacja między komponentami.
Tryb klastrowy działa po SSH, nie przez agenta. SwiftWave zarządza zdalnymi serwerami, łącząc się do nich po SSH z węzła zarządzającego — stąd cały ssh_toolkit z pulą połączeń, a stąd też sierpniowa poprawka usuwająca z tej puli połączenia-zombie. Model jest prostszy niż agentowy (nie trzeba nic instalować na węzłach poza Dockerem), ale ma konsekwencję bezpieczeństwa: klucze do wszystkich serwerów leżą w jednym miejscu, którym jest panel zarządzania.
Z pliku zależności wynika reszta obrazu: Go 1.25, PostgreSQL jako baza stanu (z migracjami przez Atlas i GORM), Echo jako serwer HTTP, gqlgen — czyli API panelu jest w GraphQL, Cobra na interfejs wiersza poleceń, gotp do haseł jednorazowych, co oznacza obsługę dwuskładnikowego uwierzytelniania, oraz SDK AWS do wysyłania snapshotów na S3.
Jedna decyzja w tym zestawie jest wyraźnie przemyślana. Kolejka zadań ma dwa warianty: lokalny, oparty o PostgreSQL i kanały Go, oraz zdalny, na RabbitMQ albo Redisie, opisany w dokumentacji jako lepszy dla niezawodności i skalowalności. W praktyce oznacza to, że w trybie jednoserwerowym nie trzeba stawiać brokera wiadomości — i to właśnie pozwala utrzymać deklarowane 180 MB. Broker dochodzi wtedy, gdy zaczyna być potrzebny, a nie na wejściu.
Instalacja
Wymagania są niskie: Linux (przetestowane Debian/Ubuntu oraz Fedora/CentOS), minimum 1 GB RAM i 1 vCPU, narzędzia curl, git, unzip, tar — i zainstalowany Docker, którego SwiftWave sam nie stawia.
Sama instalacja to pakiet systemowy:
# Debian / Ubuntu
sudo apt update -y && sudo apt install swiftwave -y
# Fedora / CentOS
sudo dnf install swiftwave -yPotem pięć kroków z CLI:
sudo swiftwave init # konfiguracja domeny węzła zarządzającego
sudo swiftwave start # pierwsze uruchomienie
# ...panel pod podanym adresem: utworzenie konta administratora
sudo swiftwave service enable # włączenie usługi systemowej
sudo swiftwave tls generate # certyfikaty (pomijalne przy localhost)
sudo swiftwave auto-update enable # opcjonalnieDomena węzła zarządzającego przyjmuje adres publiczny, własną domenę albo localhost do testów lokalnych — i w tym ostatnim przypadku generowanie certyfikatów można pominąć. Rejestracja wymaga prawdziwego adresu e-mail; projekt odrzuca adresy jednorazowe, korzystając z publicznej listy takich domen.
Kolejne serwery dodaje się z panelu, w sekcji zarządzania serwerami, konfigurując na nich obsługę proxy ingressowego. Dokumentacja wspomina przy tym o pięciominutowym okienku na konfigurację — nietypowe ograniczenie, o którym lepiej wiedzieć przed rozpoczęciem, a nie w połowie. Monitoring zasobów wymaga osobnego włączenia z menu akcji serwera.
Uwaga na dokumentację w repozytorium. Plik docs/setup/production.md nadal opisuje instalację skryptem curl -L get.swiftwave.org | bash i twierdzi, że „obecnie do produkcji dostępna jest tylko wersja v1.0.x". Przy wydaniu 2.23.1 jest to nieaktualne o dwie wersje główne i pokazuje, gdzie szukać prawdy: na stronie dokumentacji projektu, nie w plikach w repozytorium.
API, czyli wdrażanie bez klikania
Panel jest wygodny do pierwszego wdrożenia i niewygodny do dwudziestego. Dla agencji, która stawia podobne środowiska wielokrotnie, liczy się to, czy da się z tym rozmawiać programowo — i tu podział jest wyraźny.
Właściwym API jest GraphQL. Dokumentacja mówi wprost, że REST obsługuje „głównie uwierzytelnianie i bardzo niewiele rzeczy", a wszystko pozostałe idzie przez GraphQL — co jest spójne z obecnością gqlgen w zależnościach i wygenerowanym plikiem dokumentacji schematu w repozytorium.
REST pokrywa cztery operacje, i to akurat te, które w GraphQL byłyby niewygodne, bo dotyczą plików:
POST /auth/login— nazwa użytkownika i hasło w formularzu, w odpowiedzi token JWT. To jest punkt wejścia dla każdej automatyzacji,POST /upload/code— wysłanie kodu źródłowego jako archiwum tar; w odpowiedzi identyfikator pliku do użycia w dalszych wywołaniach. Czyli wdrożenie z lokalnego katalogu da się zautomatyzować, bez pośrednictwa repozytorium gita,GET /persistent-volume/backup/{id}/downloadi towarzyszący mu endpoint na nazwę pliku — pobranie snapshotu wolumenu,POST /persistent-volume/{id}/restore— wgranie archiwum i zakolejkowanie zadania odtworzenia. Odpowiedź informuje, że zadanie trafiło do kolejki, a status sprawdza się w panelu.
Praktyczny wniosek dla naszego stacku: całą ścieżkę „zbuduj artefakt w CI, wyślij, wdróż" da się poprowadzić przez API, a token z /auth/login jest jedynym sekretem, który musi trafić do pipeline'u. Odtworzenie wolumenu z backupu również jest wywołaniem HTTP, więc procedura awaryjna nie wymaga wchodzenia do panelu — co przy odtwarzaniu po incydencie ma znaczenie, bo panel może być właśnie tym, co nie działa.
Zastrzeżenie: dokumentacja API sama przyznaje, że jest w toku — plik w repozytorium zawiera zdanie o tym, że zespół „pracuje nad rozwojem i dokumentacją projektu" i planuje wydać dokumentację, żeby ułatwić pracę kontrybutorom. Schemat GraphQL jest publiczny i przeglądarka schematu działa, ale nie należy oczekiwać poradników do każdej operacji.
W kontekście aplikacji w Laravelu
Jeśli aplikacja jest już skonteneryzowana, dopasowanie jest dobre. Obraz aplikacji, HAProxy z certyfikatem przed nim, wolumen podmontowany na storage/app, zmienne środowiskowe w panelu zamiast w pliku na serwerze, webhook przebudowujący obraz po pushu na gałąź produkcyjną i snapshot wolumenu wysyłany na S3. To jest komplet tego, czego typowe wdrożenie potrzebuje, i wyklikanie go zajmuje mniej niż napisanie.
Trzeba jednak powiedzieć, czego tu nie ma w porównaniu z narzędziami zbudowanymi pod ten framework. SwiftWave nie ma pojęcia „aplikacja w Laravelu" — ma pojęcie „usługa w kontenerze". W praktyce oznacza to, że trzy rzeczy trzeba opisać samodzielnie, w obrazie i w komendach uruchomieniowych:
- Migracje przy wdrożeniu. Nie ma kroku „uruchom
artisan migratepo zbudowaniu, przed przełączeniem ruchu". Trzeba to umieścić w punkcie wejścia obrazu — z uwagą, że przy wielu replikach na Swarmie migracja odpali się tyle razy, ile jest replik, więc potrzebna jest blokada albo osobne zadanie jednorazowe, - Kolejki jako osobny typ procesu. Worker to po prostu druga usługa z tego samego obrazu i inną komendą. Działa, ale jest to nasza decyzja architektoniczna, nie pole w formularzu,
- Harmonogram. Ta sama historia — trzecia usługa z
schedule:workalbo wpis w cronie na hoście.
Nic z tego nie jest przeszkodą, ale warto wiedzieć, że PaaS zdejmuje tu warstwę infrastruktury, a nie warstwę wiedzy o tym, jak wdraża się Laravela.
Gdy trzeba zajrzeć do środka
Przy projekcie o tej skali prawdopodobieństwo, że wcześniej czy później trzeba będzie coś poprawić samodzielnie, jest realne — a to znaczy, że warto wiedzieć, ile kosztuje uruchomienie tego lokalnie. Odpowiedź jest zachęcająca: niedużo.
Wymagania to Git, Go, Node.js co najmniej 18 i Docker; środowisko musi być Linuksem albo macOS-em, bo Windows nie jest wspierany. Klonowanie wymaga flagi --recursive, bo panel jest podmodułem gita — co warto zapamiętać, żeby nie tracić kwadransa na diagnozę braku plików interfejsu.
git clone git@github.com:<uzytkownik>/swiftwave.git --recursive
cd swiftwave
npm install
npm run build:dashboard
docker swarm init # jeśli Swarm nie jest zainicjowany
sudo su
go run . init # domyślna konfiguracja
go run . setup # przygotowanie środowiska
EDITOR=nano go run . config # i wyłączenie service.use_tls na czas developmentuDwie rzeczy w tej procedurze są informacją o naturze narzędzia. Pierwsza: trzeba zainicjować Docker Swarm ręcznie — to potwierdza, że Swarm nie jest szczegółem implementacyjnym, a fundamentem, na którym wszystko stoi. Druga: praca odbywa się z uprawnieniami roota, bo SwiftWave zarządza demonem Dockera, konfiguracją HAProxy i portami systemowymi. Nie jest to narzędzie, które da się uruchomić w piaskownicy użytkownika, i przy ocenie ryzyka bezpieczeństwa trzeba to przyjąć jako założenie, nie jako wadę do naprawienia.
Alternatywy — i dlaczego to ważne pytanie
Przy self-hostowanym PaaS-ie rozmiar społeczności waży więcej niż lista funkcji, i to jest teza, której warto się trzymać przy takiej decyzji. Powód jest prosty: funkcje sprawdza się w godzinę, a odpowiedź na pytanie „dlaczego mi się nie wdraża" znajduje się albo w cudzym zgłoszeniu, albo nigdzie. Przy 24 otwartych zgłoszeniach i 893 gwiazdkach szansa, że ktoś już trafił na nasz problem i go opisał, jest niska.
Realne alternatywy w tej samej kategorii:
- Coolify i Dokploy — ta sama kategoria self-hostowanego PaaS-a, z wyraźnie większymi społecznościami i szybszym rytmem wydań. Przy wyborze „czegoś z panelem na własnym VPS-ie" są dziś pierwszym miejscem, gdzie warto zajrzeć,
- Laravel Forge z Envoyerem — płatne i nie self-hostowane, ale zna Laravela: migracje, kolejki i harmonogram są tam pojęciami pierwszej klasy, a nie rzeczą do opisania w Dockerfile'u,
- Kamal — wywodzi się ze świata Rails, ale działa z dowolnym obrazem Dockera; brak panelu, wszystko w pliku konfiguracyjnym w repozytorium. Dla zespołu, który woli konfigurację w gicie niż klikanie, bywa lepszym dopasowaniem,
- Własne Compose plus Traefik albo Caddy — najwięcej kontroli, najwięcej pracy, zero ryzyka związanego z cudzym tempem rozwoju. Wbrew pozorom przy jednym-dwóch projektach nadal jest to rozsądna opcja.
Gdzie w tym zestawie SwiftWave ma przewagę? W dwóch miejscach. Pierwsze to proxy UDP — większość PaaS-ów w tej kategorii obsługuje wyłącznie HTTP i TCP, więc jeśli aplikacja potrzebuje UDP, wybór szybko się zawęża. Drugie to snapshoty wolumenów z wysyłką na S3 jako funkcja wbudowana, a nie skrypt do napisania.
Pułapki
- Jeden główny opiekun i mała społeczność. Licencja Apache-2.0 pozwala forkować, ale fork PaaS-a nie jest forkiem biblioteki — to przejęcie utrzymania systemu, który zarządza produkcją. Przy decyzji na trzy lata warto to wycenić,
- Ośmiomiesięczna przerwa między wydaniami (wrzesień 2025 – maj 2026). Nie oznacza porzucenia, ale oznacza, że poprawka bezpieczeństwa może poczekać dłużej, niż byśmy chcieli,
- Docker Swarm pod spodem. Jest prosty, działa i nie wymaga zespołu do utrzymania — ale jest technologią w trybie utrzymania, nie rozwoju. Ekosystem, narzędzia i wiedza rynkowa idą w stronę Kubernetesa, więc przy dłuższym horyzoncie to jest świadoma stawka,
- Dokumentacja w repozytorium jest nieaktualna wobec strony projektu i opisuje instalację z poprzedniej wersji głównej,
- Wymaga portów 80 i 443, a dokumentacja zaleca świeży serwer. Na maszynie z działającym nginxem trzeba go najpierw zatrzymać,
- Docker musi być zainstalowany wcześniej — pakiet SwiftWave go nie dociąga,
- Klucze SSH do wszystkich węzłów w jednym panelu. W trybie klastrowym to jest najkrótsza droga do przejęcia całej infrastruktury, jeśli panel zostanie skompromitowany. Włączenie dwuskładnikowego uwierzytelniania (biblioteka TOTP jest w zależnościach) i ograniczenie dostępu sieciowego do panelu nie są tu opcjami,
- Migracje przy wielu replikach wymagają własnego rozwiązania — Swarm uruchomi punkt wejścia obrazu tyle razy, ile jest replik.
Podsumowanie
SwiftWave jest dobrze przemyślanym, kompletnym narzędziem w kategorii, w której kompletność jest rzadka — i jednocześnie projektem tak małym, że sam ten fakt musi wejść do decyzji. Co z tego wynika:
- Funkcjonalnie nie ma się czego wstydzić — trzy źródła wdrożenia, generator Dockerfile'a, katalog aplikacji, wolumeny na NFS i Sambie, snapshoty na S3, HAProxy z Let's Encrypt, proxy UDP, monitoring i webhooki CI, w około 180 MB pamięci i na trzech architekturach procesora,
- Architektura jest zrozumiała — Docker Swarm, PostgreSQL na stan, API w GraphQL, zarządzanie węzłami po SSH, kolejka zadań lokalnie na Postgresie albo zdalnie na RabbitMQ czy Redisie,
- Instalacja jest naprawdę krótka — pakiet z repozytorium systemowego i pięć komend CLI. Ale Docker trzeba mieć wcześniej, a serwer najlepiej świeży,
- Ufaj dokumentacji na stronie, nie w repozytorium — ta druga opisuje wersję 1.x,
- Przy Laravelu dopisz sobie migracje, kolejki i harmonogram — to są usługi w kontenerach, nie pola w formularzu. Uważaj na migracje przy wielu replikach,
- Włącz dwuskładnikowe uwierzytelnianie i zamknij panel przed internetem, zwłaszcza w trybie klastrowym, gdzie panel trzyma klucze SSH do wszystkich serwerów,
- Automatyzuj przez GraphQL, nie przez panel — REST obsługuje logowanie, wysyłkę kodu jako tar oraz pobieranie i odtwarzanie snapshotów wolumenów, więc ścieżka „zbuduj w CI, wyślij, wdróż" i procedura awaryjna działają bez wchodzenia do interfejsu,
- Przed decyzją porównaj z Coolify i Dokploy. Przy self-hostowanym PaaS-ie wielkość społeczności decyduje o tym, ile czasu zajmie diagnoza problemu — a to jest koszt, który ponosi się w najgorszym możliwym momencie,
- SwiftWave wybieraj świadomie — gdy potrzebujesz proxy UDP albo wbudowanych snapshotów wolumenów na S3, albo gdy działasz na sprzęcie ARM, gdzie lekkość ma znaczenie. To są jego realne przewagi.
Licencja: SwiftWave jest rozpowszechniany na Apache License 2.0 — permisywnej licencji open source, bez copyleftu, z jawnym udzieleniem licencji patentowej i bez warunków przy komercyjnym użyciu poza zachowaniem noty o prawach autorskich oraz wskazaniem istotnych zmian w plikach objętych licencją. W praktyce oznacza to pełną swobodę: instalacja na serwerach klientów, wdrażanie na tym ich aplikacji, modyfikacje pod własne potrzeby bez obowiązku publikacji, a nawet oferowanie hostingu opartego o SwiftWave jako usługi. Przy projekcie tej skali licencja permisywna ma dodatkowe znaczenie, którego nie ma przy dużych projektach: jest jedynym realnym zabezpieczeniem przed ryzykiem jednego opiekuna. Gdyby rozwój stanął, Apache-2.0 pozwala fork utrzymywać samodzielnie albo zlecić jego utrzymanie komukolwiek — bez rozmów o licencji. To nie czyni z forka taniej opcji, ale zamienia ryzyko „projekt umarł i nic nie możemy" na ryzyko „projekt umarł i musimy policzyć koszt utrzymania". Przy wyborze infrastruktury to jest różnica warta odnotowania.