AppFlowy — self-hostowana alternatywa dla Notion
AppFlowy to otwarty odpowiednik Notion na licencji AGPLv3: dokumenty, bazy danych, kanban i AI, z klientem we Flutterze i backendem w Rust. Pokazujemy, co realnie stawia docker compose, które zmienne z deploy.env trzeba zmienić przed wystawieniem instancji do internetu i gdzie przebiega granica między wersją AGPL a płatnym forkiem komercyjnym z limitem miejsc.
AppFlowy to otwarty odpowiednik Notion: dokumenty z blokami, bazy danych, tablice kanban, kalendarz i asystent AI w jednym miejscu, z możliwością postawienia całości na własnym serwerze. Repozytorium AppFlowy-IO/AppFlowy zebrało ponad 75 tysięcy gwiazdek i 5,9 tysiąca forków przy ponad 7 tysiącach commitów. Wydanie 0.13.2 wyszło 11 sierpnia 2026, cztery dni przed publikacją tego wpisu, więc projekt żyje i nadaje się do rozważenia w firmie.
Klient napisano we Flutterze, warstwę danych w Rust. Działa na macOS, Windowsie, Linuksie, iOS i Androidzie, a paczki są też na FlatHubie i Snapcrafcie. Licencja klienta to AGPLv3.
Jedna rzecz zasługuje na miejsce na samym początku, bo zmienia rachunek dla zespołu: „postawić AppFlowy u siebie” i „postawić AppFlowy dla ośmiu osób” to dwa różne przedsięwzięcia, z różnymi repozytoriami i różnymi licencjami. Do tego wracamy w sekcji o open core.
Trzy tryby pracy, które łatwo pomylić
AppFlowy występuje w trzech wariantach i wybór między nimi rozstrzyga się przed pierwszym docker compose up:
- Tryb lokalny — instalujesz aplikację desktopową i pracujesz na plikach trzymanych na dysku. Bez konta, bez serwera, bez synchronizacji między urządzeniami. Do notatnika jednej osoby wystarcza i nie wymaga żadnej infrastruktury.
- AppFlowy Cloud — usługa hostowana przez producenta na AWS. Plan darmowy obejmuje jeden workspace do dwóch osób i 5 GB, plan Pro kosztuje 10 dolarów za osobę miesięcznie przy płatności rocznej i podnosi limit do 50 osób w workspace.
- Self-hosting — stawiasz AppFlowy-Cloud na własnym serwerze i przepinasz do niego klienty. Dane zostają u Ciebie, razem z obowiązkiem ich utrzymania.
Reszta wpisu dotyczy trzeciego wariantu, bo to on decyduje, czy AppFlowy jest odpowiedzią na pytanie „gdzie trzymamy dokumentację projektową klienta, której nie chcemy wysyłać do amerykańskiej chmury”.
Co stawia docker compose
Plik docker-compose.yml z repozytorium AppFlowy-Cloud uruchamia jedenaście kontenerów. Warto je znać z nazwy, bo przy pierwszej awarii szukasz logów konkretnej usługi, a nie „AppFlowy”:
appflowy_cloud(obrazappflowyinc/appflowy_cloud, port 8000) — rdzeń: API HTTP i obsługa WebSocketów dla współpracy w czasie rzeczywistym,gotrue(appflowyinc/gotrue, port 9999) — uwierzytelnianie, konta, tokeny JWT, logowanie zewnętrzne,appflowy_worker— zadania w tle: import danych, wysyłka poczty, obsługa kolejki na Redisie,appflowy_search(port 4002) — wyszukiwanie pełnotekstowe i semantyczne, z własnym indeksem na wolumenie,ai(appflowyinc/appflowy_ai, port 5001) — warstwa AI: streszczenia, czat, embeddingi,appflowy_web— aplikacja webowa, ten sam interfejs co w kliencie, tylko w przeglądarce,admin_frontend(port 3000) — panel administracyjny do kont i haseł,postgresna obraziepgvector/pgvector:pg16— baza z rozszerzeniem wektorowym, bo embeddingi lądują w tej samej instancji,redis— cache i kolejka,minio— magazyn plików zgodny z S3, do wyłączenia, jeśli masz zewnętrzne S3,nginx(porty 80 i 443) — proxy i terminacja TLS przed całą resztą.
Postawienie tego wymaga samego Dockera na jednej maszynie. Praktyka wdrożeniowa mówi o 2–4 GB RAM zajętych przez sam stack i 8 GB jako rozsądnym minimum dla serwera, na którym ma jeszcze zostać miejsce na dane. Dwadzieścia gigabajtów dysku to punkt wyjścia, nie sufit, bo wszystkie załączniki idą do MinIO.
Uruchomienie wygląda standardowo:
git clone https://github.com/AppFlowy-IO/AppFlowy-Cloud.git
cd AppFlowy-Cloud
cp deploy.env .envdocker compose up -dMiędzy tymi dwoma poleceniami mieści się cała robota, o której nie mówi żaden poradnik „w pięć minut”.
deploy.env, czyli plik z domyślnymi hasłami
Szablon deploy.env jest przygotowany pod uruchomienie na localhost i zawiera komplet poświadczeń demonstracyjnych. Wystawienie go do internetu bez zmian oddaje instancję pierwszemu skanerowi, który trafi na otwarty port. Lista wartości do podmiany przed startem:
POSTGRES_PASSWORD=password
GOTRUE_JWT_SECRET=hello456
GOTRUE_ADMIN_EMAIL=admin@example.com
GOTRUE_ADMIN_PASSWORD=password
AWS_ACCESS_KEY=minioadmin
AWS_SECRET=minioadmin
PGADMIN_DEFAULT_PASSWORD=passwordSekret JWT o wartości hello456 nie jest żartem redakcyjnym, tylko domyślną zawartością pliku. Podpisuje tokeny sesji, więc jego pozostawienie oznacza, że dowolna osoba znająca repozytorium podpisze sobie token administratora.
Druga grupa to adresy. AppFlowy porównuje adres, pod którym działa, z tym, który wpisujesz w kliencie, i rozjazd kończy się błędem połączenia bez sensownego komunikatu:
FQDN=appflowy.twojafirma.pl
SCHEME=https
WS_SCHEME=wss
APPFLOWY_BASE_URL=${SCHEME}://${FQDN}
APPFLOWY_WEBSOCKET_BASE_URL=${WS_SCHEME}://${FQDN}/ws/v2Przy HTTPS trzeba przestawić także WS_SCHEME na wss. Zostawienie tam ws daje instancję, która się loguje, wyświetla dokumenty i nie synchronizuje zmian, bo przeglądarka blokuje nieszyfrowane połączenie WebSocket na stronie po HTTPS. Tego objawu nie widać w logach serwera.
Trzecia grupa dotyczy rejestracji. Domyślnie każdy, kto trafi na Twój adres, założy sobie konto, a potwierdzenie adresu e-mail jest wyłączone:
GOTRUE_MAILER_AUTOCONFIRM=true
GOTRUE_DISABLE_SIGNUP=false
GOTRUE_JWT_EXP=604800Dla instancji firmowej sensowna kolejność wygląda tak: uruchamiasz z otwartą rejestracją, zakładasz konta zespołu, ustawiasz GOTRUE_DISABLE_SIGNUP=true i restartujesz kontener GoTrue. Kolejnych ludzi dodajesz zaproszeniem albo przez admin_frontend. GOTRUE_JWT_EXP=604800 to siedem dni ważności tokenu, do skrócenia, jeśli w firmie obowiązuje krótszy czas sesji.
Poczta wymaga dwóch osobnych konfiguracji SMTP, co jest częstym źródłem pomyłek. Jedna obsługuje maile logowania (GoTrue), druga powiadomienia z samej aplikacji:
GOTRUE_SMTP_HOST=smtp.twojafirma.pl
GOTRUE_SMTP_PORT=465
GOTRUE_SMTP_USER=appflowy@twojafirma.pl
GOTRUE_SMTP_PASS=...
GOTRUE_SMTP_ADMIN_EMAIL=it@twojafirma.pl
APPFLOWY_MAILER_SMTP_HOST=smtp.twojafirma.pl
APPFLOWY_MAILER_SMTP_PORT=465
APPFLOWY_MAILER_SMTP_USERNAME=appflowy@twojafirma.pl
APPFLOWY_MAILER_SMTP_EMAIL=appflowy@twojafirma.pl
APPFLOWY_MAILER_SMTP_PASSWORD=...
APPFLOWY_MAILER_SMTP_TLS_KIND=wrapperUstawienie APPFLOWY_MAILER_SMTP_TLS_KIND=wrapper odpowiada połączeniu SMTPS na porcie 465. Przy serwerze wymagającym STARTTLS na 587 ta wartość jest inna i maile po prostu nie wychodzą.
nginx, WebSockety i limity uploadu
Konfiguracja nginxa z repozytorium jest przemyślana i warto ją przeczytać przed podmianą na własne proxy. Kilka rzeczy, które w niej siedzą i o których łatwo zapomnieć przy Traefiku czy Caddym:
client_max_body_size 10Mglobalnie, z podniesieniem do 256 MB dla publikowania stron i 2 GB dla importu danych. Import całego workspace z Notion potrafi ważyć setki megabajtów i domyślny limit go utnie,- lokalizacja
/wsz nagłówkamiUpgradeiConnectionorazproxy_read_timeoutustawionym na 86400 sekund. Bez tego proxy rozłącza sesję współpracy po minucie ciszy, - osobne timeouty 600 sekund i wyłączone buforowanie dla czatu AI i importu, bo obie ścieżki strumieniują odpowiedź,
- lokalizacja
/minio-api/obsługująca podpisane adresy do plików. Jej pominięcie daje aplikację, w której załączniki wgrywają się bez błędu i nie dają się pobrać.
Routing całości: /gotrue na uwierzytelnianie, /api na backend, /ws na synchronizację, /web na panel administracyjny, /minio na interfejs magazynu, a / na aplikację webową.
Jeśli stawiasz AppFlowy za istniejącym proxy w firmie, przepisz te cztery ustawienia ręcznie: limit rozmiaru ciała żądania, timeout WebSocketu, wyłączone buforowanie dla strumieni i trasę do podpisanych adresów S3. Trzy z czterech objawiają się jako „działa, ale coś nie działa”, więc znajdziesz je dopiero wtedy, gdy zespół zdąży już wpisać dane do instancji.
Podpięcie klienta
Aplikacja desktopowa domyślnie łączy się z chmurą producenta. Przełączenie idzie przez Settings → Cloud Settings, wybór własnego serwera i wpisanie adresu instancji. Adres musi się zgadzać co do znaku z APPFLOWY_BASE_URL, razem ze schematem https:// i bez ukośnika na końcu.
Alternatywą jest aplikacja webowa z kontenera appflowy_web, dostępna pod adresem głównym instancji. Wygodna przy dostępie z cudzego komputera i przy telefonach, na których nie chcesz instalować aplikacji.
Wydanie 0.13.1 z 9 sierpnia 2026 dołożyło logowanie przez LDAP i własne OIDC. Dla firmy z Keycloakiem albo Entra ID to różnica między osobnym kontem w kolejnym narzędziu a wpięciem AppFlowy w istniejące SSO. Wcześniej lista logowań zewnętrznych kończyła się na Google, GitHubie, Discordzie, Apple i SAML-u.
AI: chmurowe albo lokalne
Warstwa AI w wersji self-hosted jest pusta do momentu, w którym podasz jej klucz. Kontener ai czyta:
AI_OPENAI_API_KEY=
AI_AZURE_OPENAI_API_KEY=
AI_AZURE_OPENAI_API_BASE=
AI_AZURE_OPENAI_API_VERSION=
APPFLOWY_EMBEDDING_CHUNK_SIZE=2000
APPFLOWY_EMBEDDING_CHUNK_OVERLAP=200
APPFLOWY_INDEXER_ENABLED=trueBez klucza funkcje AI milczą, a reszta aplikacji działa normalnie. Z kluczem OpenAI albo Azure OpenAI treść dokumentów wychodzi poza Twój serwer, co przy wdrożeniu motywowanym prywatnością danych trzeba świadomie rozstrzygnąć, a nie odkryć po fakcie. Indekser tnie dokumenty na fragmenty po 2000 znaków z zakładką 200 i zapisuje wektory w tym samym Postgresie.
Osobna zmienna ASSEMBLYAI_API_KEY włącza transkrypcję nagrań w czasie rzeczywistym przez zewnętrzną usługę. Ta sama uwaga o wynoszeniu danych obowiązuje tu podwójnie, bo dotyczy nagrań ze spotkań.
Trzecia droga to model lokalny. AppFlowy integruje się z Ollamą, więc czat i asystenta w dokumencie da się oprzeć o model chodzący na maszynie użytkownika: Llama 3.1, Gemma 3, Qwen 3, DeepSeek R1 czy Phi-4. Funkcja działa na macOS z układem M1 lub nowszym, Windowsie 10 i nowszym oraz Linuksie. Ograniczenie warto znać z góry: generowanie obrazów nie działa lokalnie. Przy dokumentacji objętej umową o poufności to jedyny wariant, w którym treść nie opuszcza sprzętu.
Open core, czyli gdzie kończy się AGPL
Tutaj robi się interesująco i tutaj najczęściej rozbijają się plany „darmowego Notion dla zespołu”.
AppFlowy prowadzi dwa osobne repozytoria serwera:
- AppFlowy-IO/AppFlowy-Cloud na licencji AGPL-3.0, z opisanym wyżej
docker-compose.yml. Kod jest publiczny, obrazy publiczne, klucza licencyjnego nikt nie wymaga, - AppFlowy-IO/AppFlowy-SelfHost-Commercial — zamknięty fork tego samego kodu wzbogacony o własnościowe rozszerzenia producenta, rozprowadzany wyłącznie na komercyjnej licencji self-hosted, z numeracją wydań idącą osobnym torem (0.17.4 w chwili pisania) i z liczeniem miejsc przy zapraszaniu użytkowników.
Producent opisuje ten model wprost jako open core: publiczny kod serwera zostaje otwarty, AppFlowy Web i klient Flutterowy również, a płatna dystrybucja to fork połączony z kodem zamkniętym. Plany self-hosted, o których mówi dokumentacja handlowa, dotyczą tej drugiej ścieżki. Ich poziom darmowy obejmuje jedno miejsce na instancję plus do trzech gości zaproszonych do wybranych stron i nieograniczoną liczbę workspace’ów. Każda kolejna osoba w zespole wymaga płatnej licencji.
Rozróżnienie jest istotne przy planowaniu budżetu, a materiały w sieci mieszają obie ścieżki. Wersja AGPL nie ma klucza licencyjnego ani licznika miejsc, ale też nie dostaje funkcji zarezerwowanych dla forka. Wersja komercyjna daje dopracowaną administrację i wsparcie, w zamian za opłatę od miejsca. Zanim ktoś w zespole obieca prezesowi „Notion za darmo”, warto sprawdzić aktualny cennik i warunki obu wariantów, bo producent zmienił model w trakcie życia projektu i może go jeszcze doprecyzować.
AGPLv3 w praktyce agencji
Licencja AGPLv3 obejmuje zarówno klienta, jak i publiczną wersję serwera. W codziennej pracy oznacza to trzy konkretne rzeczy.
Po pierwsze, wewnętrzne uruchomienie niezmodyfikowanego AppFlowy niczego nie wymaga. Stawiasz, używasz, nikomu nic nie udostępniasz. Tak samo wygląda postawienie instancji klientowi jako element usługi utrzymania.
Po drugie, sekcja 13 AGPL uruchamia się przy modyfikacji kodu połączonej z udostępnieniem go przez sieć. Jeśli dopiszesz do serwera własny moduł, integrację z systemem klienta albo zmienisz zachowanie API, a użytkownicy korzystają z tej instancji zdalnie, musisz zaoferować im kod źródłowy swojej wersji. Nie tylko klientowi, który zamówił zmianę, ale użytkownikom instancji.
Po trzecie, na kodzie AppFlowy nie zbudujesz zamkniętego produktu. Wyprowadzenie z niego własnego SaaS-a i sprzedawanie go bez publikacji zmian jest sprzeczne z licencją. Producent zrobił dokładnie to, na co pozwala mu status właściciela praw autorskich: wypuścił zamknięty fork na własnej licencji. Nikt inny takiego prawa nie ma.
Przy wdrożeniach objętych zamówieniami publicznymi albo polityką dostawców warto dołożyć do zestawienia trzy fakty: AGPLv3, model open core producenta i to, że funkcje AI w wersji chmurowej wysyłają treść dokumentów do OpenAI albo Azure.
Utrzymanie: kopie i aktualizacje
Stan instancji siedzi w trzech miejscach i kopia musi objąć wszystkie trzy naraz: baza Postgresa z dokumentami i wektorami, wolumen MinIO z załącznikami oraz plik .env z sekretami. Zrzut samej bazy daje po odtworzeniu dokumenty bez obrazków, a odtworzenie z innym GOTRUE_JWT_SECRET unieważnia sesje wszystkich użytkowników.
docker compose exec -T postgres pg_dump -U postgres postgres | gzip > appflowy-$(date +%F).sql.gzAktualizacja sprowadza się do pobrania nowych obrazów i przeładowania stacku:
docker compose pull
docker compose up -dPrzy 890 otwartych zgłoszeniach w repozytorium klienta i tempie wydań rzędu kilku na miesiąc opłaca się przypiąć wersje obrazów zamiast jechać na ruchomym tagu. Argument z ostatnich dni: wydanie 0.13.2 naprawiło błąd, w którym otwarcie dokumentu w kilku kartach naraz powodowało, że zapisy nie trafiały na dysk, a treść znikała po restarcie aplikacji. Taka regresja w narzędziu do notatek boli bardziej niż w większości oprogramowania, bo użytkownik orientuje się dopiero wtedy, gdy szuka czegoś, co napisał w zeszłym tygodniu.
Praktyczny wniosek: aktualizuj instancję firmową z opóźnieniem kilku dni względem wydania, po zerknięciu na listę zmian, i rób kopię przed każdą aktualizacją.
Kiedy to ma sens
AppFlowy sprawdza się w kilku sytuacjach, które da się nazwać konkretnie. Zespół potrzebuje wspólnej bazy wiedzy, a dane klientów nie mogą trafić do zewnętrznej chmury. Firma ma już serwer, Dockera i kogoś, kto go pilnuje. Ktoś pracuje w podróży i chce mieć dokumenty offline w natywnej aplikacji, a nie w karcie przeglądarki. Albo interesuje Cię AI nad własną dokumentacją bez wysyłania jej do zewnętrznego dostawcy, przez model lokalny w Ollamie.
Słabiej wypada, gdy liczy się parytet funkcji z Notion. Sam producent pisze w README, że nie twierdzi, by dziś przewyższał Notion możliwościami czy dopracowaniem interfejsu, i stawia zamiast tego na prywatność danych oraz rozszerzalność. Gorzej wypada też przy zespołach bez zaplecza administracyjnego: jedenaście kontenerów, SMTP, certyfikaty, kopie i aktualizacje to realna praca, której koszt trzeba porównać z abonamentem u dostawcy SaaS. Przy większym zespole dochodzi jeszcze koszt licencji komercyjnej, jeśli chcecie wariant wspierany.
Nie jest to również narzędzie do publikowania stron dla klientów końcowych. Funkcja publikowania stron istnieje, ale AppFlowy pozostaje przede wszystkim przestrzenią roboczą zespołu.
Podsumowanie
Co daje AppFlowy postawiony u siebie:
- dokumenty, bazy danych, kanban i kalendarz w jednej aplikacji, z natywnymi klientami na pięć platform,
- kompletny stack w
docker-compose.yml: backend w Rust, GoTrue, worker, wyszukiwarka, usługa AI, aplikacja webowa, panel administracyjny, Postgres z pgvectorem, Redis, MinIO i nginx, - dane na własnym serwerze, z kopią sprowadzającą się do zrzutu Postgresa, wolumenu MinIO i pliku
.env, - logowanie przez LDAP i własne OIDC od wydania 0.13.1, obok Google, GitHuba, Discorda, Apple i SAML-a,
- AI do wyboru: OpenAI, Azure OpenAI albo model lokalny przez Ollamę, bez wynoszenia treści poza sprzęt,
- projekt w dobrej kondycji: ponad 75 tysięcy gwiazdek, wydanie 0.13.2 z 11 sierpnia 2026, kilka wydań miesięcznie.
Na co uważać:
deploy.envto plik demonstracyjny z hasłempasswordi sekretem JWThello456— wystawienie go do internetu bez podmiany oddaje instancję,- otwarta rejestracja i wyłączone potwierdzanie adresu e-mail w ustawieniach domyślnych,
- WebSockety, limity uploadu i trasa do podpisanych adresów S3 wymagają ręcznego przeniesienia przy własnym proxy, a ich brak daje objawy trudne do powiązania z przyczyną,
- funkcje AI w wariancie chmurowym wysyłają treść dokumentów do OpenAI lub Azure, co znosi część powodów, dla których stawia się to u siebie,
- jedenaście kontenerów do utrzymania, kopiowania i aktualizowania.
Licencja: klient i publiczna wersja serwera są na AGPLv3. Uruchomienie niezmodyfikowanej instancji, także dla klienta w ramach usługi utrzymania, nie rodzi obowiązków publikacyjnych. Modyfikacja kodu połączona z udostępnieniem instancji przez sieć uruchamia sekcję 13 AGPL i wymaga zaoferowania źródeł użytkownikom tej instancji. Zamkniętego produktu na tym kodzie nie zbudujesz. Osobno działa model open core producenta: obok repozytorium AGPL istnieje zamknięty fork AppFlowy-SelfHost-Commercial na licencji komercyjnej, którego darmowy poziom obejmuje jedno miejsce na instancję i do trzech gości. Wersja AGPL nie liczy miejsc, ale i nie dostaje funkcji z forka. Przy planowaniu wdrożenia dla zespołu sprawdź aktualne warunki obu wariantów, zanim policzysz oszczędność względem abonamentu.