Shlink के साथ अपना URL shortener कैसे बनाएं
Docker Compose का उपयोग करके अपने VPS पर Shlink को होस्ट करना सीखें। इस गाइड में Postgres सेटअप, API keys, HTTPS कॉन्फ़िगरेशन और क्लिक एनालिटिक्स को मैनेज करने का पूरा तरीका शामिल है।
आप क्या बना रहे हैं
एक self-hosted URL shortener एक छोटा सर्वर है जो एक लंबे लिंक को आपके स्वामित्व वाले छोटे लिंक में बदल देता है और हर क्लिक को गिनता है। Shlink इसके लिए सबसे अच्छा विकल्प है: यह open source है, Docker image के रूप में उपलब्ध है, और यह एक container और database के साथ पूरा काम करता है। यह गाइड इसे एक VPS पर, एक वास्तविक short domain के पीछे, HTTPS, API key, QR codes और click stats के साथ स्थापित करती है।
दो घटक इसे एक व्यावसायिक shortener जैसा अनुभव देते हैं। API server redirects का जवाब देता है और डेटा रखता है। Web client एक अलग static app है जो आपके browser से उस API के साथ संवाद करती है। आप दोनों को चला सकते हैं, या केवल API को चलाकर उसे command line से नियंत्रित कर सकते हैं।
यहाँ दिए गए version numbers जुलाई 2026 तक के नवीनतम हैं: Shlink 5.1 और shlink-web-client 4.8।
सबसे पहले सर्वर पर एक छोटा डोमेन पॉइंट करें
डोमेन ही आपका प्रोडक्ट है। s.example.com/abc123 वह लिंक है जिसे लोग देखते हैं, इसलिए कुछ छोटा चुनें और किसी भी चीज़ को इंस्टॉल करने से पहले इसे तय कर लें। Shlink हर short URL के साथ डोमेन को स्टोर करता है, और बाद में इसे बदलने का मतलब है कि आपके द्वारा पहले से दिए गए सभी लिंक काम करना बंद कर देंगे।
अपने छोटे डोमेन के लिए एक DNS A record बनाएँ, जो आपके VPS के public IPv4 address की ओर पॉइंट करता हो। यदि सर्वर में IPv6 है, तो एक AAAA record भी जोड़ें। आगे बढ़ने से पहले पुष्टि करें कि यह resolve हो रहा है।
dig +short s.example.com Aआउटपुट आपके सर्वर का address होना चाहिए। यदि यह खाली है, तो record अभी तक propagate नहीं हुआ है, और बाद का हर कदम भ्रमित करने वाले तरीके से विफल हो जाएगा, क्योंकि जिस नाम का DNS resolve नहीं होता, उसके लिए TLS (transport layer security) certificate जारी नहीं किया जा सकता है।
Compose फ़ाइल
Shlink को एक डेटाबेस की आवश्यकता होती है। परीक्षण के लिए SQLite काम करता है, लेकिन यदि आप किसी चीज़ को लंबे समय तक रखना चाहते हैं तो Postgres सही विकल्प है, क्योंकि visit rows जमा होती रहती हैं और Postgres इंडेक्स तथा concurrent writes को बेहतर तरीके से संभालता है। इसे /opt/shlink/compose.yaml में रखें।
services:
shlink:
image: shlinkio/shlink:stable
restart: unless-stopped
ports:
- "127.0.0.1:8080:8080"
environment:
DEFAULT_DOMAIN: s.example.com
IS_HTTPS_ENABLED: "true"
DB_DRIVER: postgres
DB_HOST: database
DB_NAME: shlink
DB_USER: shlink
DB_PASSWORD: ${DB_PASSWORD}
depends_on:
- database
database:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_DB: shlink
POSTGRES_USER: shlink
POSTGRES_PASSWORD: ${DB_PASSWORD}
volumes:
- shlink_db:/var/lib/postgresql/data
web-client:
image: shlinkio/shlink-web-client:stable
restart: unless-stopped
ports:
- "127.0.0.1:8081:8080"
volumes:
shlink_db:दोनों published ports 127.0.0.1 पर bind होते हैं, इसलिए जब तक अगले सेक्शन में reverse proxy सेट नहीं हो जाता, तब तक इंटरनेट से कुछ भी एक्सेस नहीं किया जा सकता। Docker होस्ट फ़ायरवॉल से पहले अपने स्वयं के forwarding rules लिखता है, जिसका अर्थ है कि एक साधारण 8080:8080 लाइन उस सर्वर पर भी ऐप को expose कर देगी जिसका फ़ायरवॉल बंद दिखता है। loopback address पर bind करने से यह समस्या नहीं होती। यही पैटर्न उन सभी ऐप्स पर लागू होता है जिन्हें आप इस तरह चलाते हैं, और इसे VPS पर Docker Compose के लिए गाइड में विस्तार से समझाया गया है।
डेटाबेस पासवर्ड compose फ़ाइल के बगल में स्थित एक .env फ़ाइल से आता है, इसलिए यह कभी भी YAML में नहीं जाता है।
sudo mkdir -p /opt/shlink
printf 'DB_PASSWORD=%s\n' "$(openssl rand -base64 24)" | sudo tee /opt/shlink/.env
sudo chmod 600 /opt/shlink/.envइसे शुरू करें और API के चालू होने की प्रतीक्षा करें।
cd /opt/shlink
sudo docker compose up -d
sudo docker compose logs -f shlinkपहली बार शुरू करने पर डेटाबेस माइग्रेशन चलता है, इसलिए इसमें बाद की तुलना में अधिक समय लगता है। जब यह स्थिर हो जाए, तो जांचें कि सेवा स्थानीय रूप से उत्तर दे रही है या नहीं।
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/rest/health200 का अर्थ है कि API सक्रिय है और डेटाबेस कनेक्शन काम कर रहा है। यहाँ 500 का कारण लगभग हमेशा डेटाबेस होता है: .env में मौजूद DB_PASSWORD उस पासवर्ड से मेल नहीं खाता जिसके साथ Postgres बनाया गया था, क्योंकि Postgres इमेज POSTGRES_PASSWORD को केवल तभी पढ़ती है जब वह एक खाली डेटा डायरेक्टरी को इनिशियलाइज़ करती है। पासवर्ड को बाद में बदलने का कोई प्रभाव तब तक नहीं पड़ता जब तक आप वॉल्यूम को हटाकर फिर से शुरू नहीं करते।
इसके सामने HTTPS terminate करें
Shlink पोर्ट 8080 पर plain HTTP सर्व करता है। TLS का स्थान reverse proxy में होता है, और सबसे महत्वपूर्ण सेटिंग मूल host name को आगे पास करना है। Shlink यह तय करता है कि कोई short code किस domain का है, इसके लिए वह Host header को पढ़ता है। इसलिए, यदि कोई proxy इसे rewrite करता है, तो मौजूद links पर भी 404 response मिलता है और visit stats गलत domain से जुड़ जाते हैं।
server {
server_name s.example.com;
listen 80;
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}इसके बाद certificate जारी करें। renewal timer सहित पूरी प्रक्रिया Ubuntu 24.04 पर nginx के लिए Certbot गाइड में दी गई है।
sudo certbot --nginx -d s.example.comcompose file में IS_HTTPS_ENABLED: "true" वह सेटिंग है जो Shlink को उसके द्वारा return किए गए short URLs में https:// प्रिंट करने के लिए कहती है। यह अपने आप TLS enable नहीं करती। इसे HTTPS proxy के पीछे false छोड़ दें, अन्यथा API द्वारा दी गई हर link एक http:// link होगी जो बाद में redirect होती है। इससे एक अतिरिक्त round trip लगता है और web client में यह गलत दिखता है।
API key बनाना
API key के बिना कोई भी API से संवाद नहीं कर सकता। container के अंदर CLI के माध्यम से एक key जनरेट करें।
sudo docker compose exec shlink shlink api-key:generate --name "web client"यह command key को केवल एक बार print करती है। इसे अभी copy कर लें, क्योंकि यह hashed रूप में store होती है और इसे दोबारा नहीं दिखाया जा सकता। shlink api-key:list प्रत्येक key का नाम और उसकी स्थिति (enabled है या नहीं) दिखाता है, लेकिन key स्वयं नहीं दिखाता। shlink api-key:disable और नाम का उपयोग करके किसी key को revoke करें।
प्रत्येक REST call में key को X-Api-Key header में शामिल किया जाता है।
curl -H "X-Api-Key: YOUR_KEY" https://s.example.com/rest/v3/short-urlsshortUrls key वाला एक JSON object यह दर्शाता है कि key सही ढंग से काम कर रही है। INVALID_API_KEY वाला 401 यह दर्शाता है कि key गलत है, disabled है, या उसकी expiry date निकल चुकी है।
कमांड लाइन से शॉर्ट लिंक बनाना
CLI लिंक बनाने का सबसे तेज़ तरीका है, और यह स्क्रिप्टिंग के लिए सबसे उपयुक्त है।
sudo docker compose exec shlink shlink short-url:create https://example.com/a/very/long/path
sudo docker compose exec shlink shlink short-url:create https://example.com/docs --custom-slug docs --tag reference--custom-slug आपको जनरेट किए गए कोड के बजाय एक पठनीय लिंक देता है। स्लग (slugs) प्रत्येक डोमेन के लिए अद्वितीय होते हैं, इसलिए पहले से उपयोग किए गए स्लग पर दूसरा प्रयास विफल हो जाता है, न कि चुपचाप पहले लिंक को ओवरराइट करता है। --tag को दोहराया जा सकता है, और टैग्स (tags) का उपयोग उन लिंक्स को समूहित करने के लिए किया जाता है जिनके संयुक्त आँकड़े आप बाद में देखना चाहते हैं।
मौजूदा लिंक्स की सूची देखें, फिर किसी एक लिंक का ट्रैफिक देखें।
sudo docker compose exec shlink shlink short-url:list
sudo docker compose exec shlink shlink short-url:visits docsshort-url:visits प्रति क्लिक एक पंक्ति प्रिंट करता है जिसमें तारीख, रेफरर (referrer) और यूजर एजेंट शामिल होते हैं। देश और शहर के कॉलम तब तक खाली रहते हैं जब तक आप GEOLITE_LICENSE_KEY एनवायरनमेंट वेरिएबल सेट नहीं करते, जो कि एक मुफ्त MaxMind की (key) है जिसका उपयोग Shlink, GeoLite2 डेटाबेस डाउनलोड करने के लिए करता है। इसके बिना, विज़िट्स रिकॉर्ड तो होती हैं, लेकिन उनकी लोकेशन का पता नहीं चलता।
वेब क्लाइंट और QR कोड
वेब क्लाइंट अब 127.0.0.1:8081 पर उपलब्ध है और इसे अपने स्वयं के प्रॉक्सी एंट्री की आवश्यकता है, या यदि आप इसे पब्लिश नहीं करना चाहते हैं तो आप SSH टनल का उपयोग कर सकते हैं। पहली बार लोड होने पर यह सर्वर URL और API की (key) मांग करता है। https://s.example.com और आपके द्वारा जनरेट की गई की (key) दर्ज करें। क्लाइंट इन दोनों को ब्राउज़र स्टोरेज में रखता है और सीधे आपके API को कॉल करता है, इसलिए कोई भी डेटा किसी अन्य के पास नहीं जाता है। इंटरफ़ेस को API से अलग करना एक ध्यान देने योग्य पैटर्न है, क्योंकि यही वह तरीका है जो Halcyon को Jellyfin लाइब्रेरी को 1990 के दशक के रेंटल स्टोर जैसा दिखाने की अनुमति देता है, बिना इसके पीछे के मीडिया सर्वर को बदले।
QR कोड के लिए किसी कॉन्फ़िगरेशन की आवश्यकता नहीं होती है। किसी भी शॉर्ट URL के अंत में /qr-code जोड़ें और API इमेज रिटर्न कर देगा।
https://s.example.com/docs/qr-code?size=500&format=svg&margin=20size पिक्सेल में चौड़ाई है और यह 50 से 1000 तक की वैल्यू स्वीकार करता है, जिसमें 300 डिफ़ॉल्ट है। format का मान png या svg हो सकता है। margin कोड के चारों ओर पिक्सेल में खाली जगह (quiet space) है, और तैयार इमेज का माप साइज प्लस मार्जिन का दोगुना होता है। ऐसे कोड के लिए errorCorrection=Q जोड़ें जो छोटा प्रिंट होने या आंशिक रूप से ढके होने पर भी स्कैन हो सके।
इसे चालू रखें
एक shortener चुपचाप विफल हो सकता है। लिंक redirect करना बंद कर देते हैं और कोई आपको सूचित नहीं करता, क्योंकि लिंक पर क्लिक करने वाले व्यक्ति को लगता है कि लिंक मृत है। होम पेज के बजाय किसी वास्तविक short URL पर uptime check सेट करें, और किसी भी ऐसी चीज़ पर alert सेट करें जो redirect न हो। एक self-hosted Uptime Kuma instance यह काम अच्छी तरह करता है, और यह एक विशिष्ट status code के लिए निगरानी रख सकता है।
कंटेनर का नहीं, बल्कि डेटाबेस का बैकअप लें। एक कमांड इसे डंप कर देती है।
sudo docker compose exec -T database pg_dump -U shlink shlink | gzip > shlink-$(date +%F).sql.gzवह फ़ाइल और आपकी compose फ़ाइल मिलकर नए सर्वर पर पूरी सर्विस को फिर से बना देती हैं। बॉक्स पर मौजूद हर ऐप को उस जोड़ी के अपने संस्करण की आवश्यकता होती है, और फोटो लाइब्रेरी एक कठिन मामला है, क्योंकि PhotoPrism और Immich दोनों ही डेटाबेस में पंक्तियों के साथ-साथ डिस्क पर भी ओरिजिनल फाइलें रखते हैं, इसलिए केवल डंप से कुछ भी रिस्टोर नहीं होता। अपग्रेड sudo docker compose pull के बाद sudo docker compose up -d करने से होते हैं, और Shlink शुरू होने पर कोई भी नया माइग्रेशन चलाता है। पुल करने से पहले डंप ले लें, क्योंकि माइग्रेशन को रोल बैक नहीं किया जा सकता।
FAQ
Reverse proxy जोड़ने के बाद मेरे short links 404 error क्यों दिखा रहे हैं?
Shlink short code का मिलान Host header में मौजूद domain से करता है। यदि proxy अपना नाम या internal address भेजता है, तो Shlink उस domain पर code ढूँढता है जहाँ कोई links नहीं हैं, इसलिए वह 404 error देता है। nginx location block में proxy_set_header Host $host; सेट करें और proxy को reload करें। container को restart किए बिना ही links तुरंत काम करने लगेंगे।
क्या मुझे Postgres की आवश्यकता है, या SQLite पर्याप्त है?
Shlink को आज़माने के लिए SQLite ठीक है और इसके लिए किसी दूसरे container की आवश्यकता नहीं है। महत्वपूर्ण links प्रकाशित करने से पहले Postgres पर आ जाएँ, क्योंकि हर click के साथ visit rows बढ़ती हैं और SQLite writes को serialize करता है। बाद में switch करने का अर्थ है अपने links को export और re-import करना, इसलिए शुरुआत में ही Postgres चुनना आपको उस migration से बचा लेता है।
क्या मैं वह API key रिकवर कर सकता हूँ जिसे मैं copy करना भूल गया था?
नहीं। Shlink key का hash store करता है, इसलिए api-key:list केवल नाम और status दिखाता है, value कभी नहीं। shlink api-key:generate के साथ एक नई key generate करें, उसे web client में paste करें, और फिर पुरानी key को shlink api-key:disable के साथ disable कर दें ताकि वह काम करना बंद कर दे।
मेरे visit stats में country columns खाली क्यों हैं?
Geolocation के लिए GeoLite2 database की आवश्यकता होती है, जिसे Shlink केवल तभी download करता है जब आप उसे GEOLITE_LICENSE_KEY प्रदान करते हैं। यह key MaxMind से मुफ्त मिलती है। इसे environment section में जोड़ें, container को recreate करें, और नई visits की location दिखने लगेगी। ऐसा करने से पहले दर्ज की गई visits तब तक खाली रहेंगी जब तक आप shlink visit:locate नहीं चलाते।
मैं Shlink को दूसरे server पर कैसे ले जाऊँ?
Domain को वही रखें और data को move करें। pg_dump के साथ database का dump लें, dump और compose file को नए server पर copy करें, stack को start करें, और फिर वास्तविक traffic आने से पहले खाली database में dump को restore करें। DNS record को सबसे अंत में बदलें। Short codes और उनका visit history सुरक्षित रहता है, क्योंकि सब कुछ database में ही होता है।