SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor · अपडेट किया गया: 2026-08-21

openGym को VPS पर Docker Compose से कैसे host करें

openGym को Docker Compose के जरिए VPS पर deploy करने का तरीका जानें। इसमें pinned git tag का उपयोग, TLS configuration, data storage और read-only MCP server की पूरी जानकारी दी गई है।

openGym को self-host करने पर आपको क्या मिलता है

आप repository को clone करके, .env में दो पंक्तियों को edit करके, और TLS (transport layer security) terminate करने वाले reverse proxy के पीछे docker compose up -d --build चलाकर openGym को self-host करते हैं। openGym एक gym और body weight tracker है: इसमें साप्ताहिक plans, guided workouts, हर set की logging, और समय के साथ वजन का track रखना शामिल है। यह AGPL-3.0 के अंतर्गत licensed है और यह सब कुछ आपकी disk पर plain JSON files में store करता है, इसलिए इसमें चलाने के लिए कोई database server नहीं होता।

यह stack दो long-running containers से बना है: एक nginx container जो React build को serve करता है और एक Node container जिसमें API होती है। इसके अलावा, एक one-shot job होती है जो पहली बार start करने पर लगभग 140 MB की exercise images और GIFs download करती है।

दो बातें ऐसी हैं जिन्हें project का README संकेत तो देता है, लेकिन public server पर deploy करने वाले व्यक्ति के लिए स्पष्ट नहीं करता। Passkey login एक hostname से बंधा होता है, इसलिए domain और उसका certificate पहले login से पहले ही मौजूद होना चाहिए, बाद में नहीं। और optional MCP server read-only होता है और उस machine पर चलता है जहाँ आपका AI client चलता है, न कि stack के अंदर। जब data VPS पर स्थित हो, तो यह बात आपके काम करने के तरीके को बदल देती है।

openGym अभी नया है। पहला tagged release, v1.0.0, 20 July 2026 का है, और v1.2.7, 18 August 2026 को आया था। लगभग एक महीने में तेरह tags का मतलब है कि app अभी विकसित हो रही है, इसलिए default branch पर जो कुछ भी हो उसे build करने के बजाय किसी release tag को checkout करें।

पहली बार लॉगिन करने से पहले डोमेन की योजना बनाएँ

openGym में साइन इन करने के लिए Passkeys का उपयोग किया जाता है। एक passkey एक relying party ID (RP ID) से बंधी होती है, जो वह डोमेन है जिस पर credential बनाया गया था, और ब्राउज़र केवल HTTPS पर ही passkeys बनाते हैं। इसका एकमात्र अपवाद localhost है।

इसका एक परिणाम वह है जिसका सामना लोग अपने फोन पर करते हैं। किसी अन्य डिवाइस से http://203.0.113.10:8080 खोलें और कोई passkey प्रॉम्प्ट दिखाई नहीं देगा, क्योंकि ब्राउज़र सादे HTTP origin या bare IP address पर credential बनाने से इनकार कर देता है। प्रोजेक्ट के अपने ट्रबलशूटिंग नोट्स भी यही कहते हैं: यदि कोई प्रॉम्प्ट नहीं आता है, तो इसका मतलब है कि आप http:// पर हैं या किसी IP का उपयोग कर रहे हैं।

इससे भी बुरा यह है कि RP ID आपके उन सभी credentials में समाहित (baked) होती है जिन्हें आपके उपयोगकर्ताओं ने पहले ही रजिस्टर कर लिया है। यदि आप बाद में RP_ID बदलते हैं, तो उनके डिवाइस पर संग्रहीत passkeys मेल नहीं खाएंगी, और कोई भी साइन इन नहीं कर पाएगा। सबसे पहले hostname तय करें, DNS को VPS की ओर पॉइंट करें, और किसी के भी Create profile पर टैप करने से पहले certificate को काम करने की स्थिति में लाएँ।

Docker Compose के साथ openGym को डिप्लॉय करना

Compose फ़ाइल ./data और ./media को अपने सापेक्ष bind-mount करती है, इसलिए जिस डायरेक्टरी में आप क्लोन करते हैं, वही आपका डेटाबेस है। इसे किसी सुरक्षित और स्थायी स्थान पर रखें।

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

README में अभी भी एक github.com क्लोन URL दिखाई देता है। वह पता अब काम नहीं करता है, और ऊपर दिया गया Gitea रिपॉजिटरी ही इस प्रोजेक्ट का वर्तमान होम है।

.env को एडिट करें। VPS पर तीन लाइनें महत्वपूर्ण हैं।

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

RP_ID बेयर होस्टनेम है और ORIGIN स्कीम सहित पूरा URL है। इन्हें ब्राउज़र के एड्रेस बार से बिल्कुल मेल खाना चाहिए, अन्यथा लॉगिन verification failed के साथ विफल हो जाएगा। WEB_PORT वैल्यू के बारे में पोर्ट 8080 को प्राइवेट रखने वाले सेक्शन में बताया गया है।

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

docker compose ps में web और api रनिंग स्थिति में दिखने चाहिए, और media कोड 0 के साथ exited दिखना चाहिए। यह एग्जिट सही है: मीडिया जॉब का काम restart: "no" है क्योंकि यह एक बार का डाउनलोड है। इसका लॉग ✓ Exercise media ready से शुरू होने वाली लाइन पर समाप्त होता है, और ls media/img | wc -l को 0 के बजाय कुछ सौ प्रिंट करना चाहिए। खाली डायरेक्टरी का मतलब है कि डाउनलोड विफल हो गया है, और ऐप फिर खाली इमेज के साथ एक्सरसाइज कार्ड रेंडर करता है।

यहाँ --build फ्लैग वैकल्पिक नहीं है। Compose फ़ाइल ghcr.io पर उन प्रीबिल्ट इमेजेस का नाम लेती है जो अब पब्लिश नहीं की जाती हैं, इसलिए docker compose pull, denied या manifest unknown के साथ विफल हो जाता है, और दोनों सर्विसेज को उसी सोर्स से बिल्ड किया जाता है जिसे आपने अभी क्लोन किया है। दोनों में ठीक इसी काम के लिए एक build सेक्शन मौजूद है। यदि आप Compose के लिए नए हैं, तो VPS पर Docker Compose से शुरुआत करें और फिर वापस आएं।

वर्जन को पिन करें, क्योंकि यह प्रोजेक्ट अभी नया है

चूंकि वह रजिस्ट्री नेमस्पेस अब मौजूद नहीं है, इसलिए पिन करने के लिए कोई इमेज टैग नहीं बचा है। इसके बजाय आप डिस्क पर मौजूद चेकआउट को पिन करें, क्योंकि यही तय करता है कि कंटेनर में एप्लिकेशन का कौन सा वर्जन जाएगा।

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

git status अब उस टैग पर एक detached HEAD दिखाता है, जो कि सर्वर पर आपको यही चाहिए होता है। जब तक आप कोई दूसरा वर्जन चेकआउट नहीं करते, तब तक कुछ भी नहीं बदलेगा।

इसके बाद Compose को निर्देश दें कि वह रजिस्ट्री से संपर्क न करे। इसे docker-compose.override.yml में डालें, जिसे Compose स्वचालित रूप से लोड करता है और ट्रैक की गई फ़ाइल के ऊपर मर्ज कर देता है। स्केलर कीज़ (scalar keys) को ओवरराइड द्वारा बदल दिया जाता है, इसलिए git में किसी चीज़ को एडिट करने की आवश्यकता नहीं होती और git pull साफ रहता है। मर्ज के पूर्ण नियमों के लिए Compose ओवरराइड फ़ाइल को कैसे मर्ज करता है देखें।

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

एक बार यह सेट हो जाने पर, बाद में चलाया गया docker compose up -d पुल (pull) पर विफल होने के बजाय आपके पास मौजूद सोर्स से बिल्ड होगा। जांचें कि मर्ज प्रभावी हुआ है या नहीं, फिर टैग पर दोबारा बिल्ड करें।

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

Reverse proxy के साथ TLS termination

Containers केवल plain HTTP पर काम करते हैं। इसलिए, सामने किसी ऐसी चीज़ का होना आवश्यक है जो certificate को संभाल सके। Caddy सबसे सरल विकल्प है, क्योंकि यह Let's Encrypt से certificate को स्वयं request और renew करता है।

gym.example.com {
    reverse_proxy 127.0.0.1:8080
}

nginx, Traefik और Nginx Proxy Manager सभी एक ही तरह से काम करते हैं। Cloudflare Tunnel भी इसी तरह काम करता है, जिसे प्रोजेक्ट के documentation में बताया गया है और इसके लिए किसी भी inbound port को open रखने की आवश्यकता नहीं होती।

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

इससे HTTP/2 200 प्राप्त होना चाहिए, जिसमें कोई certificate warning नहीं होनी चाहिए। अब ब्राउज़र में साइट खोलें और Create profile पर tap करें। यदि passkey prompt दिखाई देता है और login करने पर verification failed, RP_ID या ORIGIN रिपोर्ट होता है, तो इसका मतलब है कि यह address bar में दिए गए URL से मेल नहीं खाता है। .env को ठीक करें और docker compose up -d को फिर से चलाएं, जो containers को फिर से बनाता है ताकि वे नए values को पढ़ सकें। एक docker compose restart करने से .env reload नहीं होता है।

Port 8080 को public internet से दूर रखें

डिफ़ॉल्ट रूप से, web service हर interface पर 8080 पब्लिश करती है, इसलिए आपकी public IP पर plain HTTP के माध्यम से app तक पहुँचा जा सकता है, जबकि proxy उसी box पर HTTPS सर्व कर रहा होता है। Firewall rule इसे ठीक नहीं करता है। Docker, nat table में DNAT rule के साथ port पब्लिश करता है, और वह traffic फिर FORWARD chain में handle होता है जहाँ Docker के अपने rules उसे स्वीकार कर लेते हैं, जबकि ufw के rules INPUT path पर स्थित होते हैं। इसलिए sudo ufw deny 8080/tcp कुछ भी ब्लॉक नहीं करता है।

इसका समाधान केवल loopback address पर पब्लिश करना है। Compose file "${WEB_PORT:-8080}:${NGINX_PORT:-80}" को map करती है, इसलिए आप WEB_PORT में जो भी सेट करते हैं वह उस mapping के बाईं ओर प्रतिस्थापित (substitute) हो जाता है, और Docker का short syntax वहाँ ip:port pair को स्वीकार करता है। यही कारण है कि WEB_PORT=127.0.0.1:8080 काम करता है।

docker compose config
sudo ss -ltnp | grep 8080

Merged config में, web service के ports के अंतर्गत, आपको host_ip: 127.0.0.1 दिखाई देना चाहिए। ss को 127.0.0.1:8080 दिखाना चाहिए, न कि 0.0.0.0:8080। किसी अन्य machine से, curl http://<your-vps-ip>:8080 को अब refused या time out हो जाना चाहिए, जबकि HTTPS hostname काम करना जारी रखेगा।

अपनी प्रोफाइल बनने के बाद साइनअप बंद करें

साइनअप डिफ़ॉल्ट रूप से खुला रहता है और गेस्ट मोड चालू रहता है। एक पब्लिक होस्टनेम पर इसका मतलब है कि URL जानने वाला कोई भी व्यक्ति आपके सर्वर पर प्रोफाइल बना सकता है। पहले अपनी खुद की प्रोफाइल रजिस्टर करें, फिर अपनी यूजर ID खोजें: ls data/ प्रत्येक यूजर के लिए state-<uid>.json नाम की एक फाइल लिस्ट करता है, और वह <uid> ही वह वैल्यू है जिसकी आपको आवश्यकता है।

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

docker compose up -d को दोबारा चलाएं। सेटिंग्स में अब एक Admin डैशबोर्ड दिखाई देता है जहाँ आप इनवाइट कोड जनरेट और रिवोक कर सकते हैं, ताकि केवल वे लोग रजिस्टर कर सकें जिनके साथ आप ट्रेनिंग करते हैं और कोई अन्य नहीं। openGym बाहरी आइडेंटिटी प्रोवाइडर्स के बारे में कुछ नहीं जानता है, इसलिए वे इनवाइट कोड केवल इसी ऐप को नियंत्रित करते हैं और बॉक्स पर मौजूद किसी अन्य चीज़ को नहीं; यदि आप अपने द्वारा चलाए जा रहे सभी ऐप्स के लिए प्रति व्यक्ति एक ही अकाउंट देना पसंद करते हैं, तो Authentik को फॉरवर्ड ऑथ प्रॉक्सी के रूप में आगे लगाने से openGym का अपना पासकी लॉगिन लोड होने से पहले ही होस्टनेम गेट हो जाता है।

डेटा कहाँ रहता है, और उसे सुरक्षित रखने वाला बैकअप

सब कुछ ./data डायरेक्टरी में है, जिसे API कंटेनर में /data पर माउंट किया गया है। यहाँ चार प्रकार की फाइलें हैं: db.json में प्रोफाइल और पब्लिक पासकी क्रेडेंशियल्स होते हैं, state-<uid>.json में एक यूजर की रूटीन, वर्कआउट और बॉडी वेट होता है, secret सेशन कुकी की है, और vapid.json में पहली बार रन करने पर जनरेट हुई पुश नोटिफिकेशन कीज होती हैं।

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

सबसे पहले API को रोकें क्योंकि tar उन फाइलों को कॉपी करता है जिन्हें API शायद लिख रहा हो, और आधी कॉपी हुई JSON फाइल रिस्टोर होने पर टूटी हुई JSON फाइल बन जाती है। रुकने और शुरू होने में लगभग दो सेकंड लगते हैं। इसके बाद आर्काइव को सर्वर से बाहर कॉपी करें, क्योंकि VPS पर रखा हुआ आर्काइव VPS के खराब होने पर सुरक्षित नहीं रहता। बैकअप से media/ को बाहर रखें: यह 140 MB की एक्सरसाइज इमेजेस हैं जिन्हें मीडिया जॉब दोबारा मुफ्त में डाउनलोड कर लेती है।

रिस्टोर करने का अर्थ है उसी डोमेन पर काम कर रहे होस्ट पर उसी पाथ में untar करना। आपके फोन पर स्टोर की गई पासकी उसी RP ID तक सीमित होती है जिस पर वह बनाई गई थी, इसलिए नए होस्टनेम पर रिस्टोर करने से आपको एक ऐसा वर्किंग डेटाबेस तो मिल जाएगा जिसमें कोई भी साइन-इन नहीं कर पाएगा। डोमेन को वही रखें, या फिर हर पासकी को दोबारा रजिस्टर करने की योजना बनाएं। यही अनुशासन आपके द्वारा चलाए जाने वाले बाकी सभी चीजों पर भी लागू होता है, और Docker Compose स्टैक का बैकअप और अपग्रेड सामान्य प्रक्रिया को कवर करता है।

MCP सर्वर read-only है और यह आपकी मशीन पर चलता है

MCP (model context protocol) वह तरीका है जिससे Claude Desktop या Cursor जैसा क्लाइंट एक लोकल टूल सर्वर से बात करता है। openGym इसे mcp/ में प्रदान करता है। यह compose file का हिस्सा नहीं है, यह कोई container नहीं है, और यह किसी port पर listen नहीं करता है। क्लाइंट इसे एक child process के रूप में शुरू करता है और stdio के माध्यम से इससे बात करता है, इसीलिए README में लिखा है कि यह आपकी मशीन से बाहर कभी नहीं जाता है।

इसे वहाँ install करें जहाँ क्लाइंट चलता है, सर्वर पर नहीं:

cd openGym/mcp
npm install

फिर इसे 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 वैकल्पिक है, जहाँ सर्वर को मिलने वाली एकमात्र प्रोफाइल का पता चल जाता है। यह आठ टूल्स expose करता है: list_routines, get_routine, get_week_plan, list_workouts, get_workout, get_bodyweight, estimate_1rm और muscle_balance। इनमें से प्रत्येक केवल पढ़ता (read) है। इनमें से कोई भी लिखता (write) नहीं है, इसलिए एक असिस्टेंट यह तो बता सकता है कि आपने पिछले सप्ताह क्या benched किया था, लेकिन वह कोई सेट log नहीं कर सकता, रूटीन edit नहीं कर सकता, या कुछ भी delete नहीं कर सकता।

यहाँ वह हिस्सा है जिसे VPS यूज़र को हल करना होगा। OPENGYM_DATA एक filesystem path है, और आपका डेटा VPS पर है जबकि आपका AI क्लाइंट आपके लैपटॉप पर है। इसके लिए दो ईमानदार विकल्प हैं।

  1. डेटा को नीचे कॉपी करें और सर्वर को उस कॉपी पर point करें: rsync -a --delete user@gym.example.com:/opt/opengym/data/ ~/opengym-data/, फिर OPENGYM_DATA को ~/opengym-data पर सेट करें। सर्वर केवल पढ़ता है, इसलिए कॉपी करने से कुछ भी खोता नहीं है। जब आपको ताज़ा आंकड़े चाहिए हों, तो rsync को फिर से चलाएं।
  2. सर्वर को ssh पर चलाएं, जिसमें command को ssh पर और args को ["-T", "user@gym.example.com", "OPENGYM_DATA=/opt/opengym/data node /opt/opengym/mcp/src/index.js"] पर सेट करें। इसके लिए VPS पर Node इंस्टॉल होना चाहिए, और एक ऐसा लॉगिन जो stdout पर कुछ भी प्रिंट न करे, क्योंकि stdout ही प्रोटोकॉल चैनल है।

यदि cat data/db.json का परिणाम Permission denied आता है, तो API container ने उन फाइलों को root के रूप में लिखा है और आपका लॉगिन उन्हें पढ़ नहीं सकता है। उन्हें sudo के साथ कॉपी करें, या host पर ownership बदलें। उन सर्वर्स के लिए जिन्हें stdio के बजाय नेटवर्क पर listen करना है, देखें VPS पर MCP सर्वर्स चलाना

openGym या wger: आपको क्या चलाना चाहिए?

wger इस क्षेत्र में एक स्थापित विकल्प है और यह काफी बड़ा सॉफ्टवेयर है। इसका compose stack nginx के पीछे gunicorn, Django application, PostgreSQL, Redis और एक Celery worker चलाता है। इसके बदले में आपको पोषण और सामग्री ट्रैकिंग, एक प्रलेखित REST API, व्यायाम का एक बड़ा डेटाबेस और अन्य लोगों की योजनाएं प्रबंधित करने वाले प्रशिक्षकों के लिए सुविधाएं मिलती हैं।

openGym में केवल दो containers, JSON फाइलों का एक फोल्डर और passkeys के अलावा प्रबंधित करने के लिए कोई account नहीं है। यही मुख्य अंतर है।

यदि आप प्रशिक्षण के साथ-साथ भोजन को भी ट्रैक करना चाहते हैं, या यदि आपको निर्माण के लिए API की आवश्यकता है, तो wger चलाएं। यदि आप ऐसा stack चाहते हैं जो इतना छोटा हो कि उसे एक दोपहर में पूरा पढ़ा जा सके, और ऐसा login चाहते हैं जिसमें लीक होने के लिए कोई पासवर्ड न हो, तो openGym चलाएं। इस विकल्प की कीमत परिपक्वता (maturity) है: 19 August 2026 तक openGym का पहला release एक महीने पुराना है, जबकि wger के पास वर्षों के releases का अनुभव है। अपने version को pin करें, backups रखें और प्रत्येक update से पहले release notes पढ़ें।

यदि आप अभी भी यह तय कर रहे हैं कि सर्वर पर क्या रखना उचित है, तो 2026 में क्या self-host करना सार्थक है में इसके फायदे और नुकसान बताए गए हैं, और यह ऐप उसी छोटे VPS पर व्यंजनों के लिए Mealie या पैसे के प्रबंधन के लिए Actual Budget के साथ आसानी से चल सकता है।

बिना डेटा खोए अपडेट करना

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

जिस release पर आप जाना चाहते हैं उसे git checkout v<new> के साथ checkout करें, फिर docker compose up -d --build चलाएं ताकि containers उस tag से फिर से बन सकें। हर बार सबसे पहले backup लें, क्योंकि डिस्क पर मौजूद JSON फाइलों को restore करने का रास्ता केवल एक tar command है और इसमें कुछ ही सेकंड लगते हैं।

FAQ

मेरे फोन पर openGym कभी passkey prompt क्यों नहीं दिखाता है?

Browser credential बनाने से मना कर रहा है क्योंकि आप http:// पर हैं या http://192.168.1.20:8080 जैसे bare IP address का उपयोग कर रहे हैं। Browsers केवल HTTPS origins पर ही passkeys की अनुमति देते हैं, जिसमें localhost एकमात्र अपवाद है। openGym को एक वास्तविक hostname के लिए वास्तविक certificate रखने वाले reverse proxy के पीछे रखें, .env में RP_ID=gym.example.com और ORIGIN=https://gym.example.com को सेट करें, और docker compose up -d चलाएँ ताकि containers नए values को उठा सकें। यदि prompt दिखाई देता है लेकिन login verification failed रिपोर्ट करता है, तो इसका मतलब है कि वे दो values address bar में मौजूद URL से बिल्कुल मेल नहीं खाते हैं।

openGym मेरा डेटा कहाँ स्टोर करता है, और मैं इसका बैकअप कैसे लूँ?

यह compose file के बगल में ./data directory में स्टोर होता है, जिसे API container में /data के रूप में mount किया जाता है। इसमें profiles और public passkey credentials के लिए db.json, workouts और body weight के लिए प्रति उपयोगकर्ता एक state-<uid>.json, session cookie key के लिए secret, और push notification keys के लिए vapid.json होता है। इसका बैकअप लेने के लिए docker compose stop api, फिर tar czf ~/opengym-$(date +%F).tar.gz data/, और फिर docker compose start api का उपयोग करें, और archive को सर्वर से बाहर copy कर लें। media/ को छोड़ दें, जो 140 MB की exercise images हैं जिन्हें media job अपने आप फिर से download कर लेती है।

क्या Claude मेरा openGym workout history पढ़ सकता है?

हाँ, mcp/ directory में मौजूद optional MCP server के माध्यम से, और केवल पढ़ने के लिए। यह routines, week plans, logged workouts, body weight, estimated one-rep max और muscle balance को कवर करने वाले आठ tools expose करता है, और उनमें से कोई भी वापस write नहीं करता है। यह एक container नहीं है और कोई port नहीं खोलता है: आपका client इसे stdio पर शुरू करता है और यह सीधे OPENGYM_DATA पर मौजूद JSON files को पढ़ता है। चूँकि यह एक filesystem path है, इसलिए VPS पर openGym चलाने का मतलब है या तो data/ की एक copy को client चलाने वाली machine पर sync करना, या client config से ssh के माध्यम से server को invoke करना।

क्या मुझे openGym self-host करना चाहिए या wger?

यदि आप अपने training log के साथ-साथ food और nutrition tracking चाहते हैं, या जिस पर build करने के लिए documented REST API चाहिए, तो wger चुनें। यह एक बड़ा stack चलाता है: nginx के पीछे gunicorn के अंतर्गत Django, PostgreSQL, Redis और एक Celery worker। यदि आप दो containers, ऐसी JSON files जिन्हें आप cat के साथ पढ़ सकें, और बिना password manage किए passkey login चाहते हैं, तो openGym चुनें। 19 August 2026 तक, openGym का पहला tagged release एक महीने पुराना है, इसलिए हर update से पहले git tag देखें और data/ का बैकअप लें।