openGym को VPS पर Docker Compose से कैसे host करें
Docker Compose का उपयोग करके openGym को self-host करने का तरीका जानें। इसमें TLS कॉन्फ़िगरेशन, डेटा स्टोरेज की जानकारी, git tag पिनिंग और read-only MCP सर्वर सेटअप शामिल है।
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 है: इसमें साप्ताहिक योजनाएं, guided workouts, हर set की logging, और समय के साथ वजन का ट्रैक रखना शामिल है। यह AGPL-3.0 लाइसेंस के अंतर्गत आता है और सब कुछ आपकी 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 से बंधा होता है, इसलिए login से पहले domain और उसका certificate मौजूद होना चाहिए, बाद में नहीं। और 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 करें।
पहली बार लॉगिन करने से पहले डोमेन की योजना बनाएं
Passkeys का उपयोग करके आप openGym में साइन इन करते हैं। एक passkey एक relying party ID (RP ID) से बंधी होती है, जो वह डोमेन है जिस पर credential बनाया गया था, और ब्राउज़र केवल HTTPS पर ही passkeys बनाते हैं। इसका एकमात्र अपवाद localhost है।
इसका एक परिणाम यह होता है जिसका सामना लोग अपने फोन पर करते हैं। किसी अन्य डिवाइस से http://203.0.113.10:8080 खोलें और कोई passkey प्रॉम्प्ट दिखाई नहीं देगा, क्योंकि ब्राउज़र plain HTTP origin या bare IP address पर credential बनाने से मना कर देता है। प्रोजेक्ट के अपने ट्रबलशूटिंग नोट्स भी यही कहते हैं: प्रॉम्प्ट न दिखने का मतलब है कि आप http:// पर हैं या किसी IP पर हैं।
इससे भी बुरा यह है कि RP ID आपके उन सभी credentials में समाहित (baked) हो जाती है जिन्हें आपके उपयोगकर्ताओं ने पहले ही रजिस्टर कर लिया है। यदि आप बाद में RP_ID बदलते हैं, तो उनके डिवाइस पर संग्रहीत passkeys अब मेल नहीं खाएंगी, इसलिए कोई भी साइन इन नहीं कर पाएगा। पहले होस्टनेम तय करें, DNS को VPS की ओर पॉइंट करें, और किसी के भी 'Create profile' पर टैप करने से पहले certificate को काम करने की स्थिति में लाएं।
Docker Compose के साथ openGym को deploy करना
Compose file अपने सापेक्ष ./data और ./media को bind-mount करती है, इसलिए जिस directory में आप clone करते हैं, वही आपकी database है। इसे किसी सुरक्षित और स्थायी स्थान पर रखें।
sudo install -d -o "$USER" -g "$USER" /opt/opengym
git clone https://gitea.com/DuarteSantos/openGym /opt/opengym
cd /opt/opengym
cp .env.example .envREADME में अभी भी एक github.com clone URL दिखाई देता है। वह पता अब काम नहीं करता है, और ऊपर दिया गया Gitea repository ही इस project का वर्तमान home है।
.env को edit करें। VPS पर तीन lines महत्वपूर्ण हैं।
RP_ID=gym.example.com
ORIGIN=https://gym.example.com
WEB_PORT=127.0.0.1:8080RP_ID bare hostname है और ORIGIN scheme सहित पूरा URL है। इन्हें browser के address bar से बिल्कुल मेल खाना चाहिए, अन्यथा login verification failed के साथ विफल हो जाएगा। WEB_PORT मान को port 8080 को private रखने वाले section में समझाया गया है।
docker compose up -d --build
docker compose ps
docker compose logs mediadocker compose ps में web और api को running के रूप में और media को code 0 के साथ exited के रूप में दिखना चाहिए। यह exit सही है: media job का काम restart: "no" है क्योंकि इसका कार्य केवल एक बार download करना है। इसका log ✓ Exercise media ready से शुरू होने वाली line पर समाप्त होता है, और ls media/img | wc -l को 0 के बजाय कुछ सौ output देना चाहिए। एक खाली directory का मतलब है कि download विफल रहा, और app फिर खाली images के साथ exercise cards render करती है।
यहाँ --build flag वैकल्पिक नहीं है। Compose file ghcr.io पर उन prebuilt images का नाम लेती है जो अब प्रकाशित नहीं हैं, इसलिए docker compose pull, denied या manifest unknown के साथ विफल हो जाता है, और दोनों services को उसी source से build किया जाता है जिसे आपने अभी clone किया है। उन दोनों में ठीक इसी काम के लिए एक build section मौजूद है। यदि आप Compose के लिए नए हैं, तो VPS पर Docker Compose से शुरुआत करें और फिर वापस आएँ।
Version को पिन करें, क्योंकि यह प्रोजेक्ट अभी नया है
चूंकि वह registry namespace अब मौजूद नहीं है, इसलिए पिन करने के लिए कोई image tag नहीं बचा है। इसके बजाय आप disk पर मौजूद checkout को पिन करें, क्योंकि यही तय करता है कि container में application का कौन सा version जाएगा।
cd /opt/opengym
git fetch --tags
git checkout v1.2.7git status अब उस tag पर एक detached HEAD दिखाता है, जो कि server पर आप चाहते भी हैं। जब तक आप किसी दूसरे version को checkout नहीं करते, तब तक कुछ भी नहीं बदलेगा।
इसके बाद Compose को निर्देश दें कि वह registry से संपर्क न करे। इसे docker-compose.override.yml में डालें, जिसे Compose स्वचालित रूप से load करता है और tracked file के ऊपर merge कर देता है। Scalar keys को override द्वारा बदल दिया जाता है, इसलिए git में किसी भी चीज़ को edit करने की आवश्यकता नहीं होती और git pull साफ रहता है। merge के पूर्ण नियमों के लिए Compose override file को कैसे merge करता है देखें।
services:
api:
pull_policy: build
web:
pull_policy: buildएक बार ऐसा हो जाने पर, बाद में किया गया docker compose up -d pull पर fail होने के बजाय आपके पास मौजूद source से build होगा। जाँच लें कि merge प्रभावी हो गया है, फिर tag पर rebuild करें।
docker compose config | grep pull_policy
docker compose up -d --buildरिवर्स प्रॉक्सी के साथ TLS समाप्त करना
कंटेनर plain HTTP का उपयोग करते हैं। सामने मौजूद किसी घटक को certificate संभालना होता है। Caddy सबसे सरल विकल्प है, क्योंकि यह Let's Encrypt से certificate का अनुरोध और नवीनीकरण (renewal) स्वयं करता है।
gym.example.com {
reverse_proxy 127.0.0.1:8080
}nginx, Traefik और Nginx Proxy Manager सभी एक ही तरह से काम करते हैं। Cloudflare Tunnel भी ऐसा ही करता है, जिसे प्रोजेक्ट के दस्तावेजों में बताया गया है और जिसके लिए किसी inbound port को खोलने की आवश्यकता नहीं होती है।
curl -sI https://gym.example.com | head -1इससे HTTP/2 200 वापस आना चाहिए और कोई certificate warning नहीं दिखनी चाहिए। अब ब्राउज़र में साइट खोलें और Create profile पर टैप करें। यदि passkey prompt दिखाई देता है और फिर login करने पर verification failed, RP_ID या ORIGIN रिपोर्ट होता है, तो इसका मतलब है कि यह address bar में मौजूद URL से मेल नहीं खाता है। .env को ठीक करें और फिर से docker compose up -d चलाएँ, जो कंटेनरों को फिर से बनाता है ताकि वे नए मान (values) पढ़ सकें। docker compose restart करने से .env रीलोड नहीं होता है।
Port 8080 को public internet से दूर रखें
डिफ़ॉल्ट रूप से, web service 8080 को हर interface पर publish करती है। इसलिए, जब proxy उसी box पर HTTPS serve कर रहा होता है, तब भी app आपके public IP पर plain HTTP के जरिए पहुँच योग्य होती है। एक firewall rule इसे ठीक नहीं करता है। Docker एक port को nat table में DNAT rule के साथ publish करता है, और उस traffic को फिर FORWARD chain में handle किया जाता है जहाँ Docker के अपने rules उसे स्वीकार कर लेते हैं, जबकि ufw के rules INPUT path पर स्थित होते हैं। इसलिए sudo ufw deny 8080/tcp कुछ भी block नहीं करता है।
इसका समाधान केवल loopback address पर publish करना है। Compose file "${WEB_PORT:-8080}:${NGINX_PORT:-80}" को map करती है, इसलिए आप WEB_PORT में जो भी set करते हैं, वह उस mapping के बाईं ओर प्रतिस्थापित (substitute) हो जाता है, और Docker का short syntax वहाँ एक ip:port pair स्वीकार करता है। इसीलिए WEB_PORT=127.0.0.1:8080 काम करता है।
docker compose config
sudo ss -ltnp | grep 8080Merged 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 को अब refuse हो जाना चाहिए या time out हो जाना चाहिए, जबकि HTTPS hostname काम करना जारी रखेगा।
अपनी प्रोफाइल बनने के बाद साइनअप बंद करें
साइनअप डिफ़ॉल्ट रूप से खुला रहता है और गेस्ट मोड चालू रहता है। एक पब्लिक होस्टनेम पर इसका मतलब है कि जो कोई भी URL ढूँढ लेगा, वह आपके सर्वर पर प्रोफाइल बना सकता है। पहले अपनी खुद की प्रोफाइल रजिस्टर करें, फिर अपनी यूजर ID ढूँढें: ls data/ प्रत्येक यूजर के लिए state-<uid>.json नाम की एक फाइल लिस्ट करता है, और वह <uid> ही वह वैल्यू है जिसकी आपको आवश्यकता है।
ADMIN_UIDS=<your-uid>
INVITE_ONLY=1
ALLOW_GUEST=0docker compose up -d को फिर से चलाएँ। सेटिंग्स में अब एक एडमिन डैशबोर्ड दिखाई देता है जहाँ आप इनवाइट कोड जनरेट और रिवोक (revoke) कर सकते हैं, ताकि केवल वे लोग रजिस्टर कर सकें जिनके साथ आप ट्रेनिंग करते हैं और कोई अन्य नहीं। 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 जैसा client एक local tool server से बात करता है। openGym इसे mcp/ में प्रदान करता है। यह compose file का हिस्सा नहीं है, यह कोई container नहीं है, और यह किसी port पर listen नहीं करता है। Client इसे एक child process के रूप में start करता है और stdio के माध्यम से इससे बात करता है, इसीलिए README में लिखा है कि यह कभी भी आपकी मशीन से बाहर नहीं जाता है।
इसे वहां install करें जहाँ client चलता है, न कि सर्वर पर:
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 single-user install पर वैकल्पिक है, जहाँ सर्वर उसे मिलने वाली एकमात्र profile का पता लगा लेता है। यह आठ tools expose करता है: list_routines, get_routine, get_week_plan, list_workouts, get_workout, get_bodyweight, estimate_1rm और muscle_balance। इनमें से प्रत्येक केवल read करता है। इनमें से कोई भी write नहीं करता है, इसलिए एक assistant यह तो बता सकता है कि आपने पिछले सप्ताह क्या bench किया था, लेकिन वह कोई set log नहीं कर सकता, routine edit नहीं कर सकता, या कुछ भी delete नहीं कर सकता। वह सूची उस एक निर्णय का संक्षिप्त उदाहरण है जिस पर agent design बार-बार वापस आता है, जो यह है कि आप जो tools expose करते हैं, वही सब कुछ है जो एक model कर सकता है, और स्वयं loop लिखकर यह सीखना कि agents कैसे काम करते हैं यह समझने का सबसे तेज़ तरीका है कि read-only tool set एक सीमा (limitation) के बजाय एक design choice क्यों है।
यहाँ वह हिस्सा है जिसे एक VPS उपयोगकर्ता को हल करना होगा। OPENGYM_DATA एक filesystem path है, और आपका data VPS पर है जबकि आपका AI client आपके laptop पर है। इसके लिए दो ईमानदार विकल्प हैं।
- Data को copy करके नीचे लाएं और सर्वर को उस copy पर point करें:
rsync -a --delete user@gym.example.com:/opt/opengym/data/ ~/opengym-data/, फिरOPENGYM_DATAको~/opengym-dataपर set करें। सर्वर केवल read करता है, इसलिए copy करने से कुछ भी खोता नहीं है। जब आपको ताज़ा numbers चाहिए हों, तो rsync को फिर से चलाएं। - सर्वर को ssh पर चलाएं, जिसमें
commandकोsshपर औरargsको["-T", "user@gym.example.com", "OPENGYM_DATA=/opt/opengym/data node /opt/opengym/mcp/src/index.js"]पर set किया गया हो। इसके लिए VPS पर Node install होना चाहिए, और एक ऐसा login होना चाहिए जो stdout पर कुछ भी print न करे, क्योंकि stdout ही protocol channel है।
दोनों विकल्पों में agent आपके laptop पर ही चलता है। यदि आप इसे data वाले उसी box पर चलाना चाहते हैं, तो OneCLI server पर प्रत्येक व्यक्ति को sandboxed agent देता है और data/ तक stdio hop फिर से local हो जाता है।
यदि cat data/db.json का परिणाम Permission denied आता है, तो API container ने उन files को root के रूप में लिखा है और आपका login उन्हें read नहीं कर सकता है। उन्हें sudo के साथ copy करें, या host पर ownership बदलें। उन सर्वर्स के लिए जो stdio के बजाय network पर listen करने के लिए बने हैं, देखें VPS पर MCP servers चलाना।
openGym या wger: आपको क्या चलाना चाहिए?
wger इस क्षेत्र में एक स्थापित विकल्प है और यह काफी बड़ा सॉफ्टवेयर है। इसका compose stack nginx के पीछे gunicorn, Django application, PostgreSQL, Redis और एक Celery worker चलाता है। इसके बदले में आपको पोषण और सामग्री ट्रैकिंग, एक प्रलेखित REST API, व्यायामों का एक बड़ा सामुदायिक डेटाबेस और दूसरों की योजनाएं प्रबंधित करने वाले प्रशिक्षकों के लिए फीचर्स मिलते हैं।
openGym में केवल दो containers और JSON फाइलों का एक फोल्डर होता है, और passkeys के अलावा प्रबंधित करने के लिए कोई accounts नहीं होते। यही मुख्य अंतर है। यदि आपने कभी Chatwoot install को चलाया है, जहाँ बैकअप का मतलब Postgres dump और uploads directory का होना है और हर version bump के साथ database migrations चलाने पड़ते हैं, तो आप पहले से जानते हैं कि wger के रखरखाव में क्या अपेक्षाएं होती हैं।
यदि आप प्रशिक्षण के साथ-साथ भोजन को भी ट्रैक करना चाहते हैं, या आपको किसी API की आवश्यकता है, तो wger चलाएं। यदि आप ऐसा stack चाहते हैं जो इतना छोटा हो कि उसे एक दोपहर में पूरा पढ़ा जा सके, और ऐसा login चाहते हैं जिसमें लीक होने के लिए कोई password न हो, तो openGym चलाएं। इस विकल्प की कीमत परिपक्वता (maturity) है: 19 August 2026 तक openGym का पहला release एक महीने पुराना है, जबकि wger के पीछे वर्षों के releases हैं। अपने version को pin करें, बैकअप रखें और हर update से पहले release notes पढ़ें।
यदि आप अभी भी यह तय कर रहे हैं कि सर्वर पर क्या जगह पाने के हकदार है, तो 2026 में क्या self-host करना सार्थक है में इन ट्रेड-ऑफ्स को कवर किया गया है, और यह ऐप उसी छोटे VPS पर Mealie for recipes या Actual Budget for money के साथ आसानी से फिट हो जाता है।
बिना डेटा खोए अपडेट करना
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 से फिर से build हो जाएं। हर बार सबसे पहले backup लें, क्योंकि डिस्क पर मौजूद JSON files को restore करने का तरीका केवल एक tar command है और इसमें कुछ ही सेकंड लगते हैं।
FAQ
मेरे फोन पर openGym कभी passkey प्रॉम्प्ट क्यों नहीं दिखाता है?
ब्राउज़र credential बनाने से मना कर रहा है क्योंकि आप http:// पर हैं या http://192.168.1.20:8080 जैसे bare IP एड्रेस का उपयोग कर रहे हैं। ब्राउज़र केवल HTTPS ऑरिजिन्स पर ही passkeys की अनुमति देते हैं, जिसमें localhost एकमात्र अपवाद है। openGym को एक वास्तविक hostname के लिए वास्तविक certificate रखने वाले reverse proxy के पीछे रखें, .env में RP_ID=gym.example.com और ORIGIN=https://gym.example.com सेट करें, और docker compose up -d चलाएं ताकि containers नए मानों को उठा सकें। यदि प्रॉम्प्ट दिखाई देता है लेकिन लॉगिन verification failed रिपोर्ट करता है, तो इसका मतलब है कि वे दो मान एड्रेस बार में मौजूद URL से बिल्कुल मेल नहीं खाते हैं।
openGym मेरा डेटा कहाँ स्टोर करता है, और मैं इसका बैकअप कैसे लूँ?
यह compose फ़ाइल के बगल में ./data डायरेक्टरी में होता है, जो API कंटेनर में /data के रूप में माउंट की जाती है। इसमें प्रोफाइल और पब्लिक passkey क्रेडेंशियल्स के लिए db.json, वर्कआउट और बॉडी वेट के लिए प्रति उपयोगकर्ता एक state-<uid>.json, सेशन कुकी की के लिए secret, और पुश नोटिफिकेशन कीज़ के लिए vapid.json होता है। इसका बैकअप लेने के लिए docker compose stop api, फिर tar czf ~/opengym-$(date +%F).tar.gz data/, और फिर docker compose start api का उपयोग करें, और आर्काइव को सर्वर से बाहर कॉपी कर लें। media/ को छोड़ दें, जो 140 MB की एक्सरसाइज इमेज है जिसे मीडिया जॉब अपने आप दोबारा डाउनलोड कर लेती है।
क्या Claude मेरा openGym वर्कआउट इतिहास पढ़ सकता है?
हाँ, mcp/ डायरेक्टरी में वैकल्पिक MCP सर्वर के माध्यम से, और केवल पढ़ने के लिए। यह रूटीन, वीक प्लान, लॉग किए गए वर्कआउट, बॉडी वेट, अनुमानित वन-रेप मैक्स और मसल बैलेंस को कवर करने वाले आठ टूल्स को expose करता है, और इनमें से कोई भी वापस राइट नहीं करता है। यह कोई कंटेनर नहीं है और कोई पोर्ट नहीं खोलता है: आपका क्लाइंट इसे stdio के माध्यम से शुरू करता है और यह सीधे OPENGYM_DATA पर मौजूद JSON फ़ाइलों को पढ़ता है। चूँकि यह एक फ़ाइलसिस्टम पाथ है, इसलिए VPS पर openGym चलाने का मतलब है या तो data/ की एक कॉपी को क्लाइंट चलाने वाली मशीन पर सिंक करना, या क्लाइंट कॉन्फ़िगरेशन से ssh के माध्यम से सर्वर को इनवोक करना।
क्या मुझे openGym या wger को self-host करना चाहिए?
यदि आप अपने ट्रेनिंग लॉग के साथ भोजन और पोषण ट्रैकिंग चाहते हैं, या जिस पर निर्माण करने के लिए एक प्रलेखित REST API चाहते हैं, तो wger चुनें। यह एक बड़ा स्टैक चलाता है: nginx के पीछे gunicorn के अंतर्गत Django, PostgreSQL, Redis और एक Celery वर्कर। यदि आप दो कंटेनर चाहते हैं, ऐसी JSON फ़ाइलें जिन्हें आप cat के साथ पढ़ सकते हैं, और बिना पासवर्ड मैनेज किए passkey लॉगिन चाहते हैं, तो openGym चुनें। 19 अगस्त 2026 तक, openGym का पहला tagged रिलीज़ एक महीने पुराना है, इसलिए हर अपडेट से पहले एक git tag चेक आउट करें और data/ का बैकअप लें।