
Programowanie
Videoportal Gminy Stary Sącz
Własny serwis wideo gminy na PeerTube: motyw spójny z portalem i zgodny z WCAG, aktualizacja PeerTube i PostgreSQL bez utraty nagrań oraz cache, dzięki któremu transmisje sesji rady przestały się zacinać.
11.10.2026
Gmina Stary Sącz ma własny serwis wideo pod adresem video.starysacz.um.gov.pl. Są tam nagrania i transmisje sesji Rady Miejskiej, filmy z wydarzeń i materiały promocyjne. Kanał urzędu ma już ponad 140 filmów.
Portal działa na PeerTube, otwartym odpowiedniku YouTube, na serwerach gminy. Bez reklam, bez algorytmu podsuwającego cudze filmy i bez przekazywania danych widzów platformom zewnętrznym.
Moje zadanie było trzyelementowe: podnieść zaległą wersję systemu, rozwiązać problemy z transmisjami na żywo i dopasować wygląd do nowego portalu gminy.

Motyw spójny z portalem gminy
Videoportal wyglądał jak każda instancja PeerTube. Przygotowałem własny motyw, który bierze z portalu gminy fonty, kształty i neutralne kolory. Akcent jest karminowy, z korony w herbie, żeby od razu było widać, że to osobny serwis tej samej gminy.
- fonty self-hosted Atkinson Hyperlegible Next i Bricolage Grotesque, z polskimi znakami,
- tryb jasny i ciemny według ustawień systemu oraz wariant wysokiego kontrastu,
- wszystkie pary tekstu i tła z kontrastem co najmniej 4,5:1, obrys pól formularzy 3:1,
- wyraźny fokus klawiatury i oznaczenie bieżącej pozycji w menu,
- opisy filmów i komentarze w 16 pikselach zamiast 14.
Motyw jest osobną paczką z własnym numerem wersji. Wbudowany motyw PeerTube zostaje na miejscu, więc w razie problemu wystarczy jedno przełączenie w panelu.

Aktualizacja bez utraty nagrań
Portal stał na PeerTube 7.2 i PostgreSQL 13. Obie wersje miały już następców, a PostgreSQL 13 zbliżał się do końca wsparcia. Aktualizację zrobiłem w dwóch krokach, każdy ze skryptem i pełną kopią:
- PeerTube 7.2 do 8.2 z migracjami bazy,
- PostgreSQL 13 do 17 i PeerTube 8.3,
- aktualizacja yt-dlp, którego PeerTube używa do importu filmów,
- stare kontenery zostały zatrzymane, ale nieusunięte, do czasu potwierdzenia, że wszystko działa.
Przy okazji wyszedł błąd, którego nikt nie zauważył: kontener od sześciu dni zgłaszał stan „unhealthy”, bo test zdrowia używał narzędzia, którego nie było w nowym obrazie. Teraz test pyta po prostu API PeerTube.
Transmisje sesji, które się nie zacinają
Najbardziej odczuwalny problem dotyczył transmisji na żywo. Przy 30-40 widzach sesji rady część osób dostawała błędy przekroczenia czasu. Każdy widz pobierał te same, duże fragmenty filmu prosto z serwera PeerTube.
Rozwiązaniem jest cache w reverse proxy. nginx tnie segmenty wideo na kawałki po 1 MB i trzyma je w pamięci podręcznej przez dwie godziny. Playlista odświeża się co 5 sekund, więc transmisja nadal idzie na żywo.

Druga poprawka była mniej widoczna, ale ważna. PeerTube widział każdego widza jako adres serwera proxy. Przez to liczniki wyświetleń były zaniżone, a ochrona przed zgadywaniem haseł nie mogła działać. Po poprawieniu konfiguracji zaufanych proxy portal widzi prawdziwe adresy.
Ochrona całego proxy
Skoro i tak pracowałem na serwerze proxy, wdrożyłem na nim CrowdSec. Analizuje logi wszystkich stron, które przez niego przechodzą, i blokuje adresy na poziomie zapory. Korzysta też ze wspólnej listy adresów zgłaszanych przez innych użytkowników.
Do podglądu blokad i alertów napisałem prosty, wewnętrzny panel, który odświeża się co 30 sekund.
Treści, których wymaga prawo
Videoportal gminy to też serwis publiczny. Dostał deklarację dostępności, informację o ochronie danych osobowych i osobną klauzulę dla transmisji sesji, na wzór portalu gminy. Poczta wychodząca działa przez serwer pocztowy urzędu.
Transmisję sesji ogląda czasem kilkadziesiąt osób, czasem kilkaset. Dla każdej z nich to jedyna droga, żeby zobaczyć, jak radni głosują. Ona po prostu musi działać.
- Aleksander Kowal
Stack
- PeerTube 8.3 w Dockerze, PostgreSQL 17,
- własny motyw PeerTube z wersjonowaniem i CI na OneDev,
- nginx (Nginx Proxy Manager) z cache HLS w module slice,
- CrowdSec z bouncerem na zaporze.
