SSD Nodes Learn 🎉 VPS od $5.50/mies.
Przewodniki Matt ConnorAutor: Matt Connor · Zaktualizowano 2026-08-21

Jak uruchomić openGym na własnym serwerze przez Docker

Instrukcja wdrożenia openGym na VPS z wykorzystaniem Docker Compose. Dowiedz się jak skonfigurować TLS przed rejestracją Passkey, zarządzać danymi JSON i użyć serwera MCP.

Co zyskujesz, uruchamiając openGym we własnym zakresie

Uruchomienie openGym we własnym zakresie polega na sklonowaniu repozytorium, edycji dwóch linii w .env oraz uruchomieniu docker compose up -d --build za reverse proxy, które wykonuje terminację TLS (transport layer security). openGym to narzędzie do śledzenia treningów siłowych i z masą własnego ciała: obsługuje plany tygodniowe, treningi z przewodnikiem, rejestrowanie każdej serii oraz śledzenie wagi w czasie. Oprogramowanie jest licencjonowane na zasadach AGPL-3.0 i przechowuje wszystkie dane w zwykłych plikach JSON na dysku, więc nie wymaga uruchamiania serwera bazy danych.

Stos technologiczny składa się z dwóch działających w tle kontenerów: kontenera nginx, który serwuje kompilację React, oraz kontenera Node, który obsługuje API. Dodatkowo wymagane jest jednorazowe zadanie, które przy pierwszym uruchomieniu pobiera około 140 MB obrazów ćwiczeń i plików GIF.

Istnieją dwie kwestie, które plik README projektu sugeruje, ale nie wyjaśnia wprost w kontekście wdrożenia na publicznym serwerze. Logowanie za pomocą Passkey jest powiązane z nazwą hosta, dlatego domena oraz jej certyfikat muszą istnieć przed pierwszym logowaniem, a nie po nim. Ponadto opcjonalny serwer MCP działa w trybie tylko do odczytu i musi być uruchomiony na maszynie, na której działa klient AI, a nie wewnątrz stosu, co zmienia sposób postępowania, gdy dane znajdują się na serwerze VPS.

openGym jest młodym projektem. Pierwsze oznaczone wydanie, v1.0.0, datowane jest na 20 lipca 2026, a wersja v1.2.7 pojawiła się 18 sierpnia 2026. Trzynaście wydań w ciągu około miesiąca oznacza, że aplikacja jest w fazie intensywnego rozwoju, dlatego zaleca się korzystanie z konkretnego tagu wydania zamiast budowania wersji z domyślnej gałęzi.

Zaplanuj domenę przed pierwszym logowaniem

Passkeys to metoda logowania do openGym. Klucz dostępu jest powiązany z identyfikatorem strony ufającej (RP ID), którym jest domena, na której utworzono poświadczenie. Przeglądarki tworzą passkeys wyłącznie przez HTTPS. Jedynym wyjątkiem jest localhost.

Ma to konsekwencje, z którymi użytkownicy stykają się na telefonach. Otwarcie http://203.0.113.10:8080 z innego urządzenia powoduje brak monitu o passkey, ponieważ przeglądarka odmawia utworzenia poświadczenia w źródle HTTP lub przy użyciu samego adresu IP. Dokumentacja rozwiązywania problemów projektu potwierdza ten fakt: brak monitu oznacza, że użytkownik znajduje się na http:// lub korzysta z adresu IP.

Co gorsza, RP ID jest trwale zapisane w każdym poświadczeniu już zarejestrowanym przez użytkowników. Zmiana RP_ID w późniejszym terminie spowoduje, że klucze dostępu zapisane na urządzeniach przestaną pasować, uniemożliwiając logowanie. Należy najpierw ustalić nazwę hosta, skierować rekordy DNS na VPS i skonfigurować certyfikat, zanim ktokolwiek wybierze opcję Create profile.

Wdrożenie openGym przy użyciu Docker Compose

Plik compose montuje ./data oraz ./media w trybie bind-mount względem własnej lokalizacji, zatem katalog, do którego pobrano repozytorium, jest bazą danych. Należy umieścić go w trwałej lokalizacji.

sudo install -d -o "$USER" -g "$USER" /opt/opengym
git clone https://gitea.com/DuarteSantos/openGym /opt/opengym
cd /opt/opengym
cp .env.example .env

Plik README nadal wyświetla adres URL klonowania github.com. Ten adres nie jest już rozpoznawany, a powyższe repozytorium Gitea stanowi aktualne miejsce projektu.

Edytuj .env. Na serwerze VPS istotne są trzy linie.

RP_ID=gym.example.com
ORIGIN=https://gym.example.com
WEB_PORT=127.0.0.1:8080

RP_ID to sama nazwa hosta, a ORIGIN to pełny adres URL wraz ze schematem. Muszą one dokładnie odpowiadać zawartości paska adresu, w przeciwnym razie logowanie zakończy się błędem verification failed. Wartość WEB_PORT została wyjaśniona w sekcji dotyczącej utrzymywania portu 8080 jako prywatnego.

docker compose up -d --build
docker compose ps
docker compose logs media

docker compose ps powinno wykazać, że web oraz api działają, a media zakończyło pracę z kodem 0. To zakończenie jest poprawne: zadanie obsługi mediów posiada restart: "no", ponieważ jego praca polega na jednorazowym pobraniu danych. Dziennik kończy się linią zaczynającą się od ✓ Exercise media ready, a ls media/img | wc -l powinno zwrócić wartość rzędu kilkuset, a nie 0. Pusty katalog oznacza, że pobieranie nie powiodło się, a aplikacja renderuje karty ćwiczeń z pustymi obrazami.

Flaga --build jest w tym przypadku wymagana. Plik compose wskazuje wstępnie zbudowane obrazy w ghcr.io, które nie są już publikowane, więc docker compose pull kończy się błędem denied lub manifest unknown, a oba serwisy są budowane ze źródła, które właśnie sklonowano. Oba zawierają sekcję build przeznaczoną właśnie do tego celu. Jeśli Docker Compose jest nowym narzędziem, zacznij od Docker Compose na serwerze VPS i wróć tutaj.

Przypnij wersję, ponieważ ten projekt jest młody

Ponieważ ta przestrzeń nazw w rejestrze przestała istnieć, nie pozostał żaden tag obrazu, który można by przypiąć. Zamiast tego należy przypiąć checkout na dysku, ponieważ to on decyduje, która wersja aplikacji trafi do kontenera.

cd /opt/opengym
git fetch --tags
git checkout v1.2.7

git status zgłasza teraz stan detached HEAD na tym tagu, co jest pożądanym zachowaniem na serwerze. Nic nie zmieni się bez Twojej wiedzy, dopóki nie wykonasz checkoutu innej wersji.

Następnie należy poinstruować Compose, aby całkowicie przestał odwoływać się do rejestru. Umieść to w docker-compose.override.yml, który Compose ładuje automatycznie i scala z plikiem śledzonym przez git. Klucze skalarne są zastępowane przez nadpisanie, więc nie trzeba edytować niczego w git, a git pull pozostaje czysty. Zobacz jak Compose scala plik nadpisujący, aby poznać pełne zasady scalania.

services:
  api:
    pull_policy: build
  web:
    pull_policy: build

Dzięki temu późniejsze polecenie docker compose up -d zbuduje obraz z posiadanych źródeł, zamiast kończyć się błędem przy próbie pobrania (pull). Sprawdź, czy scalanie odniosło skutek, a następnie przebuduj obraz przy użyciu tagu.

docker compose config | grep pull_policy
docker compose up -d --build

Terminacja TLS za pomocą reverse proxy

Kontenery komunikują się przy użyciu zwykłego protokołu HTTP. Komponent znajdujący się przed nimi musi obsługiwać certyfikat. Caddy stanowi najkrótszą ścieżkę, ponieważ automatycznie żąda certyfikatów od Let's Encrypt i odnawia je we własnym zakresie.

gym.example.com {
    reverse_proxy 127.0.0.1:8080
}

nginx, Traefik oraz Nginx Proxy Manager działają w ten sam sposób. Podobnie funkcjonuje Cloudflare Tunnel, który jest opisany w dokumentacji projektu i nie wymaga otwierania żadnych portów przychodzących.

curl -sI https://gym.example.com | head -1

Polecenie powinno zwrócić HTTP/2 200 bez ostrzeżenia o certyfikacie. Następnie należy otworzyć witrynę w przeglądarce i wybrać Create profile. Jeśli pojawi się monit o passkey, a logowanie zgłosi verification failed, RP_ID lub ORIGIN, oznacza to niezgodność z adresem URL w pasku przeglądarki. Należy poprawić .env i ponownie wykonać docker compose up -d, co spowoduje odtworzenie kontenerów i wczytanie nowych wartości. Polecenie docker compose restart nie przeładowuje .env.

Ograniczenie dostępu do portu 8080 z sieci publicznej

Domyślnie usługa WWW udostępnia 8080 na każdym interfejsie, co sprawia, że aplikacja jest dostępna przez zwykły protokół HTTP pod publicznym adresem IP, podczas gdy proxy obsługuje HTTPS na tej samej maszynie. Reguła firewalla nie rozwiązuje tego problemu. Docker publikuje port za pomocą reguły DNAT w tablicy nat, a ruch ten jest następnie przetwarzany w łańcuchu FORWARD, gdzie własne reguły Dockera go akceptują, podczas gdy reguły ufw znajdują się na ścieżce INPUT. Z tego powodu sudo ufw deny 8080/tcp nie blokuje żadnego ruchu.

Rozwiązaniem jest publikacja wyłącznie na adresie pętli zwrotnej (loopback). Plik compose mapuje "${WEB_PORT:-8080}:${NGINX_PORT:-80}", więc wartość ustawiona w WEB_PORT jest podstawiana po lewej stronie tego mapowania, a krótka składnia Dockera akceptuje tam parę ip:port. Dlatego właśnie WEB_PORT=127.0.0.1:8080 działa poprawnie.

docker compose config
sudo ss -ltnp | grep 8080

W scalonej konfiguracji, w sekcji ports usługi WWW, powinna widnieć wartość host_ip: 127.0.0.1. Polecenie ss powinno wskazywać 127.0.0.1:8080, a nie 0.0.0.0:8080. Z poziomu innej maszyny połączenie curl http://<your-vps-ip>:8080 powinno być teraz odrzucane lub kończyć się przekroczeniem czasu oczekiwania, podczas gdy nazwa hosta HTTPS powinna nadal działać.

Zamknięcie rejestracji po utworzeniu profilu

Rejestracja jest domyślnie otwarta, a tryb gościa włączony. W przypadku publicznej nazwy hosta oznacza to, że każdy, kto znajdzie adres URL, może utworzyć profil na serwerze. Najpierw zarejestruj własny profil, a następnie znajdź swój identyfikator użytkownika: ls data/ wyświetla plik o nazwie state-<uid>.json dla każdego użytkownika, a <uid> to wartość, której potrzebujesz.

ADMIN_UIDS=<your-uid>
INVITE_ONLY=1
ALLOW_GUEST=0

Uruchom ponownie docker compose up -d. W ustawieniach pojawi się teraz panel administratora, w którym można generować i unieważniać kody zaproszeń, dzięki czemu osoby, z którymi trenujesz, będą mogły się zarejestrować, a nikt inny nie uzyska dostępu. openGym nie obsługuje zewnętrznych dostawców tożsamości, więc kody zaproszeń dotyczą wyłącznie tej aplikacji, a nie innych usług na serwerze. Jeśli wolisz udostępnić jedno konto dla każdej osoby w ramach wszystkich uruchomionych usług, zastosowanie Authentik jako forward auth proxy zabezpieczy nazwę hosta, zanim załaduje się mechanizm logowania passkey w openGym.

Lokalizacja danych i kopia zapasowa zapewniająca ich ochronę

Wszystkie dane znajdują się w katalogu ./data, który jest montowany w kontenerze API w ścieżce /data. Występują cztery rodzaje plików: db.json przechowuje profile oraz publiczne dane uwierzytelniające passkey, state-<uid>.json zawiera rutyny, treningi i masę ciała użytkownika, secret to klucz sesji cookie, a vapid.json przechowuje klucze powiadomień push wygenerowane przy pierwszym uruchomieniu.

cd /opt/opengym
docker compose stop api
tar czf ~/opengym-$(date +%F).tar.gz data/
docker compose start api

Najpierw należy zatrzymać API, ponieważ tar kopiuje pliki w czasie, gdy API może dokonywać zapisu, a częściowo skopiowany plik JSON po przywróceniu będzie uszkodzony. Zatrzymanie i uruchomienie trwa około dwóch sekund. Następnie należy skopiować archiwum z serwera, ponieważ archiwum znajdujące się na VPS nie przetrwa awarii samego VPS. Należy pominąć media/ w kopii zapasowej: jest to 140 MB obrazów ćwiczeń, które zadanie obsługi mediów pobierze ponownie bez dodatkowych kosztów.

Przywracanie polega na rozpakowaniu archiwum do tej samej ścieżki na hoście obsługującym tę samą domenę. Klucz passkey zapisany w telefonie jest przypisany do identyfikatora RP ID, dla którego został utworzony, więc przywrócenie danych na nową nazwę hosta skutkuje działającą bazą danych, do której nikt nie może się zalogować. Należy zachować domenę lub zaplanować ponowną rejestrację każdego klucza passkey. Ta sama zasada dotyczy wszystkich innych uruchamianych usług, a artykuł tworzenie kopii zapasowych i aktualizacja stosu Docker Compose opisuje ogólną procedurę.

Serwer MCP działa w trybie tylko do odczytu i uruchamia się lokalnie

MCP (model context protocol) to sposób, w jaki klient, taki jak Claude Desktop lub Cursor, komunikuje się z lokalnym serwerem narzędzi. openGym dostarcza go w mcp/. Nie jest on częścią pliku compose, nie działa w kontenerze i nie nasłuchuje na żadnym porcie. Klient uruchamia go jako proces potomny i komunikuje się przez stdio, dlatego w README zaznaczono, że nigdy nie opuszcza on Twojej maszyny.

Zainstaluj go tam, gdzie działa klient, a nie na serwerze:

cd openGym/mcp
npm install

Następnie dodaj go do claude_desktop_config.json:

{
  "mcpServers": {
    "opengym": {
      "command": "node",
      "args": ["/absolute/path/to/openGym/mcp/src/index.js"],
      "env": {
        "OPENGYM_DATA": "/absolute/path/to/openGym/data",
        "OPENGYM_UID": "<your-uid>"
      }
    }
  }
}

OPENGYM_UID jest opcjonalne w instalacji dla jednego użytkownika, gdzie serwer automatycznie wykrywa jedyny dostępny profil. Udostępnia on osiem narzędzi: list_routines, get_routine, get_week_plan, list_workouts, get_workout, get_bodyweight, estimate_1rm oraz muscle_balance. Każde z nich służy wyłącznie do odczytu. Żadne nie zapisuje danych, więc asystent może odpowiedzieć na pytanie o wyniki z zeszłego tygodnia, ale nie może zarejestrować serii, edytować planu ani niczego usunąć.

Oto kwestia, którą musi rozwiązać użytkownik VPS. OPENGYM_DATA to ścieżka w systemie plików, a Twoje dane znajdują się na VPS, podczas gdy klient AI działa na laptopie. Oto dwie uczciwe opcje rozwiązania tego problemu.

  1. Skopiuj dane lokalnie i wskaż serwerowi kopię: rsync -a --delete user@gym.example.com:/opt/opengym/data/ ~/opengym-data/, a następnie ustaw OPENGYM_DATA na ~/opengym-data. Serwer tylko odczytuje dane, więc kopia nie powoduje utraty funkcjonalności. Uruchom ponownie rsync, gdy będziesz potrzebować aktualnych danych.
  2. Uruchom serwer przez ssh, z command ustawionym na ssh oraz args ustawionym na ["-T", "user@gym.example.com", "OPENGYM_DATA=/opt/opengym/data node /opt/opengym/mcp/src/index.js"]. Wymaga to zainstalowanego Node na VPS oraz loginu, który nie wypisuje niczego na stdout, ponieważ stdout jest kanałem komunikacyjnym protokołu.

Jeśli cat data/db.json zwraca Permission denied, oznacza to, że kontener API zapisał te pliki jako root i Twój użytkownik nie ma uprawnień do ich odczytu. Skopiuj je za pomocą sudo lub zmień właściciela plików na hoście. W przypadku serwerów, które mają nasłuchiwać przez sieć zamiast przez stdio, zobacz uruchamianie serwerów MCP na VPS.

openGym czy wger: co uruchomić?

wger to uznane rozwiązanie w tej niszy, będące znacznie bardziej rozbudowanym oprogramowaniem. Jego stos compose uruchamia gunicorn obsługujący aplikację Django, PostgreSQL, Redis oraz worker Celery za serwerem nginx. W zamian otrzymuje się śledzenie odżywiania i składników, udokumentowane API REST, dużą bazę ćwiczeń społecznościowych oraz funkcje dla trenerów zarządzających planami innych osób.

openGym składa się z dwóch kontenerów, folderu plików JSON i nie wymaga administrowania kontami poza użyciem passkeys. Na tym polega cała różnica.

Uruchom wger, jeśli chcesz śledzić dietę obok treningów lub potrzebujesz API do budowania własnych rozwiązań. Uruchom openGym, jeśli potrzebujesz stosu na tyle małego, aby przeanalizować go w całości w jedno popołudnie, oraz logowania bez haseł, które mogłyby wyciec. Kosztem tego wyboru jest dojrzałość projektu: na dzień 19 sierpnia 2026 pierwsze wydanie openGym ma miesiąc, podczas gdy wger posiada za sobą lata wydań. Przypinaj wersje, wykonuj kopie zapasowe i czytaj informacje o wydaniu przed każdą aktualizacją.

Jeśli nadal zastanawiasz się, co warto zainstalować na serwerze, co warto hostować samodzielnie w 2026 omawia kompromisy, a ta aplikacja dobrze współgra z Mealie do przepisów lub Actual Budget do finansów na tym samym małym VPS.

Aktualizacja bez utraty danych

cd /opt/opengym
docker compose stop api
tar czf ~/opengym-$(date +%F).tar.gz data/
docker compose start api
git fetch --tags

Przełącz się na wybraną wersję za pomocą git checkout v<new>, a następnie uruchom docker compose up -d --build, aby kontenery zostały przebudowane z użyciem tego tagu. Wykonanie kopii zapasowej jest zawsze pierwszym krokiem, ponieważ przywrócenie plików JSON z dysku wymaga jedynie wykonania polecenia tar i trwa kilka sekund.

FAQ

Dlaczego openGym nie wyświetla monitu o klucz dostępu (passkey) na telefonie?

Przeglądarka odmawia utworzenia poświadczeń, ponieważ połączenie odbywa się przez http:// lub bezpośredni adres IP, taki jak http://192.168.1.20:8080. Przeglądarki zezwalają na użycie kluczy dostępu wyłącznie w źródłach HTTPS, z wyjątkiem localhost. Należy umieścić openGym za reverse proxy obsługującym certyfikat dla poprawnej nazwy hosta, ustawić RP_ID=gym.example.com oraz ORIGIN=https://gym.example.com w pliku .env, a następnie wykonać docker compose up -d, aby kontenery pobrały nowe wartości. Jeśli monit się pojawia, ale logowanie zwraca błąd verification failed, oznacza to, że te dwie wartości nie są identyczne z adresem URL widocznym w pasku przeglądarki.

Gdzie openGym przechowuje dane i jak wykonać ich kopię zapasową?

Dane znajdują się w katalogu ./data obok pliku compose i są montowane w kontenerze API jako /data. Katalog zawiera db.json dla profili i publicznych poświadczeń kluczy dostępu, po jednym pliku state-<uid>.json na użytkownika dla treningów i masy ciała, secret dla klucza ciasteczek sesji oraz vapid.json dla kluczy powiadomień push. Kopię zapasową należy wykonać za pomocą docker compose stop api, następnie tar czf ~/opengym-$(date +%F).tar.gz data/ i docker compose start api, a potem skopiować archiwum z serwera. Można pominąć media/, czyli 140 MB obrazów ćwiczeń, które zadanie media pobiera ponownie automatycznie.

Czy Claude może odczytać historię treningów z openGym?

Tak, za pośrednictwem opcjonalnego serwera MCP w katalogu mcp/, wyłącznie w trybie odczytu. Udostępnia on osiem narzędzi obejmujących rutyny, plany tygodniowe, zarejestrowane treningi, masę ciała, szacowany ciężar maksymalny (1RM) oraz balans mięśniowy; żadne z nich nie zapisuje danych. Nie jest to kontener i nie otwiera portów: klient uruchamia go przez stdio, a serwer odczytuje pliki JSON bezpośrednio z OPENGYM_DATA. Ponieważ jest to ścieżka w systemie plików, uruchomienie openGym na VPS wymaga synchronizacji kopii data/ na maszynę z klientem lub wywołania serwera przez ssh w konfiguracji klienta.

Czy wybrać samodzielny hosting openGym czy wger?

Wybierz wger, jeśli potrzebujesz śledzenia żywienia obok dziennika treningowego lub udokumentowanego API REST do dalszej rozbudowy. Aplikacja ta wymaga większego stosu technologicznego: Django pod kontrolą gunicorn, PostgreSQL, Redis oraz worker Celery za nginx. Wybierz openGym, jeśli preferujesz dwa kontenery, pliki JSON możliwe do odczytania za pomocą cat oraz logowanie kluczem dostępu bez konieczności zarządzania hasłami. Według stanu na 19 sierpnia 2026, pierwsze wydanie openGym ma miesiąc, dlatego przed każdą aktualizacją należy sprawdzić tag git i wykonać kopię zapasową data/.