Jak samodzielnie hostować OpenAnalytics na VPS
Sprawdź wymagania przed instalacją OpenAnalytics: 4 GB RAM, 25 GB wolnego miejsca, Docker Compose oraz cztery rekordy DNS. Poznaj strukturę ClickHouse, Postgres i Valkey.
Wymagania wstępne przed rozpoczęciem
Aby samodzielnie hostować OpenAnalytics, wymagany jest serwer VPS z systemem Linux, posiadający około 4 GB pamięci RAM, 25 GB wolnego miejsca na dysku, zainstalowany Docker z wtyczką Compose oraz cztery rekordy DNS skierowane na adres IP serwera. Jest to kluczowa informacja, która powinna zostać zweryfikowana przed wykonaniem pierwszej komendy.
Stos technologiczny składa się z sześciu usług aplikacyjnych oraz trzech magazynów danych. Postgres obsługuje płaszczyznę sterowania: konta, witryny, klucze API oraz linki do udostępniania. ClickHouse przechowuje surowe zdarzenia oraz agregaty odczytywane przez pulpit nawigacyjny. Valkey działa w dwóch instancjach: jako trwała kolejka zdarzeń oraz jako pamięć podręczna, której utrata jest dopuszczalna, ponieważ oba zadania wymagają odmiennych polityk usuwania danych. Tylko jeden proces, bramka zapytań (query gateway), posiada uprawnienia do odczytu z ClickHouse i weryfikuje podpis Ed25519 na każdym kopercie zapytania przed jego wykonaniem.
Jeśli celem było uzyskanie rozwiązania opartego na jednym pliku binarnym i jednym pliku konfiguracyjnym, to rozwiązanie nie spełnia tych założeń. GoatCounter jest opcją jednoplikową w tej kategorii: jeden plik wykonywalny w języku Go, domyślnie korzystający z SQLite, bez zewnętrznej bazy danych. Bardziej rozbudowany stos oferuje funkcje takie jak lejki sprzedażowe, wskaźniki web vitals, atrybucję przychodów z własnego konta Stripe oraz serwer MCP (model context protocol). Artykuł Wybór między samodzielnie hostowanymi narzędziami analitycznymi zawiera porównanie tych rozwiązań. Niniejszy przewodnik zakłada, że decyzja została już podjęta.
Skieruj najpierw cztery rekordy DNS na serwer
Cztery subdomeny muszą wskazywać na publiczny adres IP serwera przed rozpoczęciem jakichkolwiek działań. Caddy żąda certyfikatów Let's Encrypt przy pierwszym uruchomieniu, a wyzwanie (challenge) kończy się niepowodzeniem, jeśli nazwa nie jest jeszcze rozpoznawana.
app.example.comobsługuje pulpit nawigacyjny (dashboard).api.example.comobsługuje API oraz wywołania zwrotne OAuth.c.example.comobsługuje moduł zbierający (collector) oraz skrypt śledzący.rt.example.comobsługuje strumień danych w czasie rzeczywistym.
Użyj czterech rekordów A lub jednego rekordu A i trzech rekordów CNAME wskazujących na ten pierwszy. Potwierdź poprawność za pomocą dig +short app.example.com przed kontynuowaniem. Nazwa dodana przed chwilą może być nadal buforowana jako NXDOMAIN przez dowolny resolver, z którego korzysta Let's Encrypt, dlatego w przypadku niepowodzenia pierwszej próby uzyskania certyfikatu warto odczekać i sprawdzić dzienniki Caddy. Ponowne uruchomienie instalacji nie przyspiesza propagacji DNS.
Jak hostować OpenAnalytics przy użyciu Docker Compose
Pobierz oznaczoną wersję (tag). Domyślna gałąź służy do prac rozwojowych, natomiast tag wersji odpowiada opublikowanym obrazom. Poniższe polecenia zakładają, że Docker oraz wtyczka Compose są już zainstalowane, co opisano w uruchamianiu usług Docker Compose na VPS.
git clone https://github.com/OpenLabs-so/openanalytics
cd openanalytics
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./generate-secrets.sh --domain example.com --email you@example.com --with-geoip
docker compose pull && docker compose up -dFlaga sed '/-/d' w poleceniu checkout pomija tagi wersji przedpremierowych, dzięki czemu pobierana jest najnowsza stabilna wersja, a nie kandydat do wydania. --with-geoip pobiera bazę danych miast DB-IP podczas generowania. Pomiń ten krok, a każde zdarzenie będzie miało przypisany pusty kraj, przez co widok geograficzny pozostanie pusty. Możesz dodać tę bazę później, uruchamiając infra/selfhost/geoip/fetch-dbip.sh, ustawiając GEOIP_DB_PATH=/geoip/dbip-city-lite.mmdb w env/collector.env, a następnie odtwarzając kolektor za pomocą docker compose up -d --force-recreate collector. Baza danych jest odświeżana co miesiąc, więc powtarzaj pobieranie co miesiąc, w przeciwnym razie dane o miastach staną się nieaktualne.
Utwórz kopię zapasową wygenerowanych sekretów przed kontynuacją
Generator tworzy trzy elementy. .env przechowuje nazwy domen oraz odwołania do obrazów. env/*.env zawiera po jednym pliku sekretów dla każdej usługi. docker-compose.override.yml przechowuje trzy pary kluczy Ed25519 w postaci skalarów blokowych YAML, ponieważ wieloliniowy format PEM nie może znajdować się w pliku env. Całość jest ignorowana przez git i żadnego z tych elementów nie można wygenerować ponownie z tymi samymi wartościami.
Skopiuj te pliki z maszyny już teraz. Utrata każdego z nich wiąże się z konkretnymi konsekwencjami:
- Utrata haseł do magazynu oznacza brak dostępu do Postgres i ClickHouse, co można zresetować jedynie z wnętrza kontenerów.
- Utrata
OA_CREDENTIAL_KEYRINGsprawia, że wszystkie zapisane dane uwierzytelniające stron trzecich stają się niemożliwe do odzyskania; każdy, kto połączył konto Stripe, musi wykonać to ponownie. - Utrata
ANONYMOUS_IDENTITY_SECRETpowoduje zresetowanie tożsamości odwiedzających: wszyscy wczorajsi użytkownicy zostaną policzeni jako nowi, co będzie widoczne na wykresach. - Utrata
AUTH_SECRETunieważnia wszystkie sesje, co zmusza użytkowników do ponownego logowania. - Utrata klucza prywatnego podpisywania wymaga rotacji pary. Żadne dane nie zostają utracone.
Dwa sekrety muszą być identyczne pod względem bajtowym w dwóch różnych plikach każdy. ANONYMOUS_IDENTITY_SECRET występuje w collector.env oraz worker.env, ponieważ kolektor oblicza skrót odwiedzającego, a worker go zapisuje. OA_CREDENTIAL_KEYRING występuje w api.env oraz worker.env. Wszystkie pozostałe sekrety są celowo przypisane do dokładnie jednej usługi; jeśli usługa otrzyma sekret, którego nie powinna posiadać, zakończy działanie zamiast się uruchomić.
Uruchomienie stosu i weryfikacja
grep OA_IMAGE .env
docker compose pull
docker compose up -d
docker compose logs -f migrate
docker compose psmigrate stosuje schematy Postgres oraz ClickHouse, a następnie kończy działanie, dlatego zatrzymany kontener migrate jest poprawnym stanem końcowym. tracker-build kompiluje oa.js do wolumenu obsługiwanego przez Caddy i również kończy pracę. Wszystkie pozostałe usługi powinny mieć status healthy w docker compose ps. Usługa restartująca się w pętli niemal zawsze oznacza niepowodzenie walidacji środowiska, a dziennik wyświetla wszystkie problemy na jednej liście, zamiast po jednym na restart. Dwie najczęstsze przyczyny to pozostawienie pustej zmiennej, która jest odrzucana zamiast traktowania jej jako nieustawionej, oraz umieszczenie sekretu w niewłaściwym pliku usługi.
W architekturze arm64 lub w przypadku korzystania z gałęzi (branch) nie ma opublikowanych obrazów, więc budowanie odbywa się lokalnie za pomocą docker compose up -d --build. Host z 4 GB pamięci RAM wyczerpuje zasoby w trakcie tego procesu. Należy najpierw dodać swap, który jest wymagany tylko na czas budowania:
fallocate -l 4G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile
echo '/swapfile none swap sw 0 0' >> /etc/fstabBudowanie trwa około dziesięciu minut. Pobieranie obrazów zajmuje kilka minut, dlatego istnieją gotowe obrazy wydań.
Przejęcie pierwszego konta
Otwórz https://app.example.com. Wdrożenie, do którego nikt się jeszcze nie zalogował, nie wyświetla formularza logowania: oferuje utworzenie pierwszego konta. To konto na stałe staje się kontem uprzywilejowanym i jako jedyne ma dostęp do ekranu ustawień wdrożenia. Gdy konto już istnieje, ścieżka zwraca 409, dzięki czemu nikt niepowołany nie uzyska dostępu. Wykonaj tę czynność w momencie, gdy stos jest w pełni sprawny, a nie w późniejszym terminie.
Instalacja trackera
Dodaj witrynę w panelu sterowania, aby otrzymać tag. Jego format jest stały:
<script
async
src="https://c.example.com/oa.js"
data-key="YOUR_TRACKING_KEY"
data-collector="https://c.example.com"
></script>Umieść go w sekcji head strony. Klucz śledzenia jest z założenia publiczny, więc powinien znajdować się w kodzie HTML, gdzie każdy może go odczytać. Skrypt instaluje window.oa, a wywołania takie jak oa("track", ...) są kolejkowane przez stub i wysyłane po załadowaniu pliku, dzięki czemu zdarzenie niestandardowe wywołane wcześnie nie zostanie utracone. Jeśli inny element na stronie korzysta już z window.oa, tracker zainstaluje się jako window.openanalytics.
Następnie sprawdź całą ścieżkę od początku do końca:
curl -s https://c.example.com/oa.js -o /dev/null -w '%{http_code} %{size_download}\n'
curl -s https://api.example.com/health | head -c 200
docker compose logs --tail=50 worker | grep -i batchPierwsze polecenie powinno zwrócić 200 oraz kilka kilobajtów danych. Załaduj stronę w swojej witrynie, a następnie w ciągu kilku sekund wyszukaj linię wsadową w dzienniku worker. Kolektor odpowiada 202 w momencie przyjęcia zdarzenia, a 202 oznacza, że zostało ono zakolejkowane, a nie zapisane. To worker odpowiada za przenoszenie zdarzeń do ClickHouse. Jeśli zdarzenia są przyjmowane, ale nie pojawiają się w panelu sterowania, oznacza to, że worker jest zablokowany, co potwierdza stale rosnąca głębokość kolejki Valkey. Typowe przyczyny to błędne dane uwierzytelniające ClickHouse w worker.env lub brak uprawnień do tabeli dodanej w ramach ostatniej migracji.
Utrzymanie kolektora publicznie i panelu za uwierzytelnianiem
Caddy jest dostarczany wewnątrz pliku compose i automatycznie uzyskuje certyfikaty dla wszystkich czterech nazw, więc domyślna ścieżka nie wymaga dodatkowej konfiguracji proxy. Jeśli serwer korzysta już z odwrotnego proxy nginx, należy umieścić stos przed nim, używając dostarczonego infra/selfhost/nginx.conf.example, zachowując przy tym nienaruszoną obsługę nagłówków:
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header CF-Connecting-IP "";
proxy_set_header True-Client-IP "";
proxy_set_header Fly-Client-IP "";Kolektor wyznacza dzienny skrót odwiedzającego na podstawie adresu IP klienta, dlatego musi pobierać ten adres bezpośrednio z połączenia, a nie z nagłówka. Przekazywanie CF-Connecting-IP z niezaufanego węzła pozwala każdemu wywołującemu na podszycie się pod dowolny adres, co jednocześnie zafałszowuje geolokalizację i zawyża liczbę odwiedzin.
Dostęp jest rozdzielony według nazwy hosta. c. oraz rt. muszą być dostępne dla każdego odwiedzającego każdą mierzoną witrynę, dlatego nie należy stosować przed nimi uwierzytelniania basic auth ani list dozwolonych adresów IP. app. oraz api. powinny być dostępne wyłącznie dla osób zalogowanych. Własny mechanizm uwierzytelniania aplikacji chroni panel: logowanie hasłem jest domyślnie włączone poprzez AUTH_PASSWORD_SIGNIN=enabled w env/api.env, a przyciski Google lub GitHub pojawiają się tylko wtedy, gdy dla danego dostawcy zdefiniowano zarówno identyfikator klienta (client ID), jak i klucz tajny (client secret). Magiczne linki wymagają transportu poczty; bez niego API jedynie zapisuje wysyłkę w skrzynce nadawczej, więc wiadomość nie jest dostarczana, a błędy nie są zgłaszane.
Jedno ustawienie decyduje o tym, czy panel w ogóle działa. AUTH_TRUSTED_ORIGINS w env/api.env musi dokładnie odpowiadać źródłu (origin) panelu. Jeśli jest błędne lub go brakuje, API nie wysyła nagłówków CORS (cross-origin resource sharing), przeglądarka odrzuca każde wywołanie, a użytkownik otrzymuje panel, który renderuje układ, ale nie wyświetla danych, podczas gdy docker compose ps zgłasza, że wszystko działa poprawnie.
Podczas edycji konfiguracji proxy warto zająć się zautomatyzowanym ruchem. Boty indeksujące trafiają do kolektora tak samo jak inni użytkownicy, a ich odsłony trafiają do ClickHouse i zafałszowują statystyki. Blokowanie botów AI na poziomie serwera pozwala wyeliminować część tego ruchu przed zapisaniem go w bazie danych, co chroni przed utratą dokładności danych i niepotrzebnym zajmowaniem miejsca na dysku.
Co oznacza brak plików cookie i jakie są tego konsekwencje
Pliki cookie nie są używane. Tożsamość odwiedzającego jest przechowywana jako skrót z solą (salted hash), sól zmienia się codziennie, a surowe adresy IP nigdy nie są zapisywane. Geolokalizacja jest rozwiązywana lokalnie w oparciu o plik DB-IP znajdujący się na dysku, dzięki czemu żadne zapytanie dotyczące odwiedzającego nie opuszcza hosta.
Zaletą tego rozwiązania jest brak identyfikatora utrwalanego na urządzeniu użytkownika, co jest kluczowym czynnikiem podlegającym unijnym przepisom ePrivacy dotyczącym zgód. Z tego powodu konfiguracje oparte wyłącznie na danych zagregowanych są często stosowane bez banerów zgody. RODO nadal reguluje zakres i czas przechowywania danych, a decyzje w tym zakresie podejmuje doradca prawny, a nie plik README.
Koszt tego rozwiązania to brak możliwości śledzenia tożsamości w różnych dniach. Rotacja soli oznacza, że osoba odwiedzająca witrynę w poniedziałek i ponownie w środę jest liczona jako dwóch różnych odwiedzających – jest to działanie zamierzone i nie ma od niego obejścia. Dzienne liczby unikalnych użytkowników są dokładne. Tygodniowe i miesięczne statystyki unikalnych użytkowników są tworzone na podstawie danych dziennych, przez co zawyżają zasięg; w związku z tym wszelkie wskaźniki "powracających użytkowników" w dłuższym okresie nie mierzą tego, co sugeruje ich nazwa. Sesje i ścieżki użytkowników są wiarygodne w obrębie jednego dnia. Rotacja ANONYMOUS_IDENTITY_SECRET wywołuje ten sam efekt co zmiana dnia, dlatego należy traktować tę rotację jako zmianę danych, a nie jako rutynowe czyszczenie.
Kolektor respektuje nagłówki Do Not Track oraz Global Privacy Control, czyli sygnały przeglądarki informujące witrynę o zakazie sprzedaży lub udostępniania danych osobowych. Znacznik skryptu posiada własne przełączniki dla tych samych celów: data-respect-gpc, data-respect-dnt oraz data-require-consent, który wstrzymuje zbieranie danych do momentu uzyskania zgody i zapamiętuje odpowiedź w localStorage pod kluczem oa.consent. Ustawienie data-storage="none" całkowicie wyłącza przechowywanie danych w przeglądarce.
Dlaczego dysk zapełnia się po sześciu miesiącach
To zjawisko najczęściej prowadzi do awarii własnych instancji analitycznych, a zdarzenia zazwyczaj nie są jego przyczyną.
Należy zacząć od obrazów. Jedno wydanie publikuje ich dziesięć, co zajmuje około 13 GB na dysku. Aktualizacja pobiera nową generację przed usunięciem starej, więc przez pewien czas przechowywane są dwie generacje. To wypełnia większość z 25 GB wymaganego miejsca, zanim jeszcze dotrze pierwsze wyświetlenie strony.
Następnie dochodzą migawki. snapshot.sh zatrzymuje stos, archiwizuje oba wolumeny danych wraz ze wszystkimi kluczami (secrets) i restartuje usługę. Tylko kopie typu cold są tutaj bezpieczne, ponieważ ClickHouse scala części w tle, a kopia wykonana w trakcie scalania nie jest spójna. upgrade.sh wykonuje automatyczną kopię przed każdą aktualizacją, więc archiwa gromadzą się na tym samym dysku, dopóki nie zostanie nałożony na nie limit.
./snapshot.sh create --label before-something-risky
./snapshot.sh list
./snapshot.sh --keep 3Na hoście zbliżającym się do limitu należy usunąć poprzednią generację przed aktualizacją. Jest to bezpieczne podczas pracy stosu, ponieważ obrazy obsługujące uruchomione kontenery są nadal referencjonowane:
docker image prune -a -fKolejną kwestią są same zdarzenia. ClickHouse silnie kompresuje dane kolumnowe, więc objętość surowych zdarzeń rośnie wolniej, niż oczekuje większość użytkowników, a tabele podsumowujące (rollup tables), z których korzysta pulpit nawigacyjny, są niewielkie w porównaniu z tabelą surową. Należy mierzyć, zamiast zgadywać:
docker system df -v
docker compose exec clickhouse df -h /var/lib/clickhouseAby uzyskać dane dla poszczególnych tabel, należy uruchomić poniższe polecenie z poświadczeniami ClickHouse, które generator zapisał w infra/selfhost/env/:
SELECT table, formatReadableSize(sum(bytes_on_disk)) AS size, sum(rows) AS row_count
FROM system.parts
WHERE active
GROUP BY table
ORDER BY sum(bytes_on_disk) DESC;Należy wykonać ten pomiar w pierwszym i czwartym tygodniu. Dwa punkty pozwalają wyznaczyć tempo wzrostu, a tempo wzrostu wskazuje, kiedy konieczna będzie zmiana rozmiaru wolumenu. Przewodnik dotyczący self-hostingu nie dokumentuje żadnego parametru retencji ani czasu życia (time-to-live) dla surowych zdarzeń według stanu na sierpień 2026, więc należy dobrać rozmiar dysku na podstawie zmierzonego tempa wzrostu, zamiast zakładać, że stare wiersze wygasną samoczynnie.
Warto znać jedną pułapkę związaną z usuwaniem, zanim stanie się problemem. Usunięcie witryny lub konta dodaje zadania do kolejki dla procesu worker, a ten wymaga ustawienia CLICKHOUSE_MAINTENANCE_USER oraz CLICKHOUSE_MAINTENANCE_PASSWORD, przy jednoczesnym istnieniu pasującego użytkownika oa_maintenance w ClickHouse. Bez nich zadania usuwania pozostają w kolejce na zawsze. Witryna znika z pulpitu nawigacyjnego, ale wszystkie wiersze pozostają na dysku, co stwarza pozory czyszczenia przy braku odzyskania miejsca.
Aktualizacje i trzy rodzaje kosztów
git fetch --tags
git checkout "$(git tag -l 'v*' --sort=-v:refname | sed '/-/d' | head -1)"
cd infra/selfhost
./upgrade.shupgrade.sh wyświetla trzy rodzaje kosztów przed wykonaniem operacji. Przestój jest realny: zdarzenia wysłane w czasie, gdy kolektor jest wyłączony, przepadają, ponieważ tracker nie ponawia prób ich dostarczenia. Wycofanie zmian (rollback) powoduje utratę danych, gdyż rollback.sh --to backups/<snapshot> całkowicie zastępuje oba magazyny i odrzuca każdy wiersz zapisany po wykonaniu migawki. Trzecim kosztem jest przestrzeń dyskowa, zajmowana przez wspomniany wcześniej zbiór migawek.
Łatwo popełnić błędy w dwóch zasadach restartu. Uruchom bramę zapytań (query gateway) przed API, ponieważ nowsze API wysyła pola zapytań, których starsza brama nie akceptuje. ClickHouse wymaga odtworzenia (recreate), a nie restartu, ponieważ docker compose restart ponownie wykorzystuje pierwotne środowisko kontenera i ignoruje wprowadzone zmiany:
docker compose up -d --force-recreate clickhousePanel sterowania (dashboard) zawiera podobną pułapkę. Trzy źródła NEXT_PUBLIC_* w env/web.env są kompilowane do pakietu przeglądarkowego i podstawiane w momencie startu kontenera. Dlatego panel odwołujący się do błędnej nazwy hosta naprawia się za pomocą docker compose up -d --force-recreate web, a nie restart. Dziennik kontenera webowego wyświetla źródła, z którymi został uruchomiony; jest to najszybszy sposób na potwierdzenie, że poprawka została wdrożona.
Jeśli ClickHouse odmawia uruchomienia po edycji konfiguracji, przeczytaj pierwszą linię jego dziennika. Linia zaczynająca się od oa-entrypoint: oznacza, że punkt wejścia (entrypoint) odrzuca ustawioną wartość. Każdy inny komunikat zazwyczaj oznacza, że plik konfiguracyjny zawiera nieprawidłowy kod XML. Najczęstszą przyczyną jest podwójny myślnik wewnątrz komentarza XML, co jest niedozwolone.
Licencja AGPL-3.0 oraz nazwa
Kod jest objęty licencją AGPL-3.0. Uruchamianie go w niezmienionej formie na własnych witrynach nie nakłada żadnego obowiązku publikacji. Obowiązek ten powstaje w momencie modyfikacji kodu i uruchomienia tak zmienionej wersji jako usługi sieciowej: licencja wymaga wówczas udostępnienia zmodyfikowanego kodu źródłowego użytkownikom tej usługi. Dotyczy to zarówno udostępniania klientom paneli nawigacyjnych w ramach własnej instancji, jak i dołączania oprogramowania do produktów komercyjnych. Utrzymywanie zmian w publicznym forku spełnia ten wymóg bez konieczności podejmowania dalszych kroków.
Marka jest oddzielona od kodu. Nazwa "OpenAnalytics" oraz domena hostowana przez projekt identyfikują instancję obsługiwaną przez autorów i nie stanowią części udzielonej licencji. Wdrożenie użytkownika uruchamia oprogramowanie bez użycia tej marki, dlatego przed udostępnieniem usługi płacącym klientom należy nadać jej własną nazwę.
FAQ
Czy mogę uruchomić OpenAnalytics na VPS z 1 GB RAM?
Nie. Projekt wymaga około 4 GB pamięci RAM oraz 25 GB wolnego miejsca na dysku, ponieważ jedno wdrożenie uruchamia sześć usług aplikacyjnych obok Postgres, ClickHouse oraz dwóch instancji Valkey. Sam ClickHouse jest zasobożernym procesem. Na maszynie z 1 GB RAM kontenery startują, a następnie mechanizm OOM killer (out-of-memory killer) jądra systemu przerywa jeden z nich, zazwyczaj ClickHouse. Jeśli plan 1 GB jest sztywnym ograniczeniem, należy użyć narzędzia w formie pojedynczego pliku binarnego, takiego jak GoatCounter, który działa w oparciu o SQLite bez zewnętrznej bazy danych.
Czy muszę stosować baner cookies w OpenAnalytics?
To pytanie do prawnika, jednak fakty techniczne przemawiają na korzyść użytkownika. System nie używa plików cookie, identyfikator użytkownika jest solonym hashem zmieniającym się codziennie, a surowe adresy IP nigdy nie są zapisywane, więc nie powstaje trwały identyfikator odwiedzającego. RODO nadal reguluje kwestie przechowywania danych i czasu ich retencji. Jeśli zbieranie danych ma być jawnie autoryzowane, należy ustawić data-require-consent w tagu skryptu: tracker nie zbierze żadnych danych do momentu udzielenia zgody, a informację o niej przechowa w localStorage pod kluczem oa.consent.
Dlaczego zdarzenia zwracają kod 202, ale nie pojawiają się w panelu?
202 oznacza, że kolektor przyjął i zakolejkował zdarzenie, a nie że zostało ono zapisane. Proces roboczy (worker) opróżnia tę kolejkę do ClickHouse, więc pusty panel przy udanych żądaniach wskazuje na problem z workerem. Należy przeczytać docker compose logs --tail=50 worker i monitorować głębokość kolejki Valkey. Rosnąca kolejka oznacza, że worker jest zablokowany; najczęstszą przyczyną są błędne poświadczenia ClickHouse w worker.env lub brak uprawnień do tabeli utworzonej podczas ostatniej migracji.
Dlaczego panel jest pusty, mimo że wszystkie kontenery działają poprawnie?
W pierwszej kolejności należy sprawdzić AUTH_TRUSTED_ORIGINS w env/api.env. Musi ono dokładnie odpowiadać źródłu (origin) panelu; w przeciwnym razie API nie wyśle nagłówków CORS, przeglądarka odrzuci każde wywołanie i użytkownik zobaczy działający układ strony bez danych. Drugą kwestią do sprawdzenia są trzy wartości NEXT_PUBLIC_* w env/web.env, które są podstawiane podczas startu kontenera web. Ich poprawa wymaga wykonania docker compose up -d --force-recreate web, ponieważ zwykły restart zachowuje stare wartości.
Czy licencja AGPL-3.0 zabrania oferowania tego rozwiązania klientom?
Nie, nakłada ona tylko jeden warunek. Uruchamianie kodu w wersji niezmienionej nie wiąże się z żadnymi zobowiązaniami. W przypadku modyfikacji kodu i udostępniania go jako usługi dla innych osób, należy zapewnić tym użytkownikom dostęp do zmodyfikowanego kodu źródłowego, co można zrealizować poprzez publiczny fork. Ponadto nazwa "OpenAnalytics" nie jest licencjonowana wraz z kodem, więc każda usługa komercyjna musi posiadać własną nazwę.