Blog
DevOps17 min czytania

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-1 z 2 sierpnia 2026, przed nim 2.22.4-1 z 2 maja 2026, a przed nim 2.22.3-1 z 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 pracystandalone 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_manager i udp_proxy_manager — generowanie konfiguracji ingressu, osobno dla TCP/HTTP i osobno dla UDP,
  • ssl_manager — obsługa ACME, na bibliotece mholt/acmez,
  • git_manager — pobieranie kodu, na go-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_queue i pubsub — 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 -y

Potem 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  # opcjonalnie

Domena 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}/download i 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 migrate po 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:work albo 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 developmentu

Dwie 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.