SSD Nodes Learn 🎉 VPS $5.50/माह से
गाइड Matt Connorलेखक: Matt Connor

एक VPS पर self-hosted log management कैसे सेटअप करें

एक ही VPS पर log management सेटअप करने का सही तरीका जानें। journald और logrotate से शुरुआत करें या Loki और OpenSearch के बीच RAM की खपत और retention नियमों के आधार पर चुनाव करें।

एक VPS पर self-hosted log management की वास्तविक लागत

एक VPS (virtual private server) पर self-hosted log management का पूरा मामला एक ही सवाल पर निर्भर करता है: क्या आपको search cluster की आवश्यकता है, या आपको केवल rotation और grep की जरूरत है? अधिकांश vendor guides इस सवाल का जवाब तीन nodes और 12 GB RAM से शुरू करके देते हैं, इससे पहले कि एक भी log line वहां भेजी जाए। एक सर्वर के लिए यह जवाब व्यर्थ है, इसलिए नीचे दी गई तुलना इस आधार पर है कि कोई भी विकल्प किसी भी डेटा को रखने से पहले एक छोटे सर्वर से क्या मांग करता है।

यदि आप एक या दो सर्वर चलाते हैं और यह जानना चाहते हैं कि पिछले मंगलवार को क्या हुआ था, तो systemd-journald और logrotate पहले से ही यह काम करते हैं, और आप अगले भाग के बाद रुक सकते हैं। यदि कई मशीनों के logs को एक स्थान पर जमा करना है और हफ्तों तक search करना है, तो Grafana Loki एक छोटे सर्वर के लिए उपयुक्त है, क्योंकि यह text के बजाय labels को index करता है। Elasticsearch और OpenSearch आपको वास्तविक full text search की सुविधा देते हैं, लेकिन इसके लिए वे memory की मांग करते हैं, क्योंकि JVM (Java virtual machine) heap की एक न्यूनतम सीमा होती है जिससे नीचे आप नहीं जा सकते।

journald से शुरुआत करें, क्योंकि अधिकांश लोग यहीं रुक जाते हैं

systemd-journald किसी भी वर्तमान Ubuntu या Debian सर्वर पर पहले से चल रहा होता है। यह प्रत्येक service unit के standard output, kernel messages और syslog पर भेजे गए किसी भी डेटा को capture करता है। चार commands अधिकांश घटनाओं को कवर करती हैं।

journalctl -u nginx.service --since "2026-08-14 09:00" --until "2026-08-14 10:00"
journalctl -p err -b
journalctl -f -u ssh.service
journalctl --disk-usage

अंतिम command Archived and active journals take up 1.1G in the file system. जैसा एक line print करती है। यह वह संख्या है जो तय करती है कि आपको और कुछ करने की आवश्यकता है या नहीं। यदि यह कुछ सौ megabytes दिखाती है और आप -u तथा --since के साथ अपनी जरूरत की जानकारी पा सकते हैं, तो आपका काम हो गया।

journal reboot के बाद सुरक्षित रहेगा या नहीं, यह Storage= और इस बात पर निर्भर करता है कि /var/log/journal मौजूद है या नहीं। सामान्य Storage=auto setting के साथ, जब वह directory मौजूद होती है तो journald /var/log/journal में लिखता है, और जब वह नहीं होती तो /run/log/journal में लिखता है। /run memory-backed होता है, इसलिए उस directory के बिना वाले box पर हर log reboot के समय मिट जाता है, जो कि ठीक वही समय है जब आप उन्हें पढ़ना चाहते हैं। Ubuntu images में यह directory पहले से होती है। Minimal और container-based images में अक्सर यह नहीं होती।

ls -d /var/log/journal
sudo mkdir -p /var/log/journal
sudo systemctl restart systemd-journald
journalctl --disk-usage

Restart के बाद, journalctl --disk-usage को /run के बजाय /var/log/journal से कम size दिखानी चाहिए। defaults पहले से ही सीमित हैं, जो मुख्य कारण है कि journald एक गंभीर समाधान है, न कि केवल एक fallback। journald.conf man page SystemMaxUse= को file system size के 10% पर और SystemKeepFree= को 15% पर set करता है, और प्रत्येक calculated default को 4G पर cap करता है। SystemMaxFileSize= default रूप से SystemMaxUse= के आठवें हिस्से पर होता है, जो 128M पर capped है, इसलिए आप सामान्यतः सात rotated files रखते हैं। MaxRetentionSec= default रूप से 0 होता है, जो age-based deletion को बंद कर देता है। उस अंतिम default को फिर से पढ़ें: out of the box journal केवल size द्वारा सीमित है, age द्वारा कभी नहीं।

[Journal]
Storage=persistent
SystemMaxUse=2G
MaxRetentionSec=30day

इसे /etc/systemd/journald.conf.d/99-size.conf में लिखें, journald को restart करें, फिर जाँचें कि journalctl --disk-usage आपकी नई सीमा की ओर बढ़ गया है। अगली rotation का इंतज़ार करने के बजाय अभी space खाली करने के लिए, sudo journalctl --vacuum-size=500M या sudo journalctl --vacuum-time=14d चलाएँ। दोनों ही हटाई गई हर file को print करते हैं, इसलिए silent run का मतलब है कि delete करने के लिए कुछ नहीं था।

journal के बाहर की हर चीज़, जैसे कि /var/log/nginx/access.log, logrotate का काम है, और यह systemd timer से प्रतिदिन चलता है। एक विफलता के बारे में जानना महत्वपूर्ण है क्योंकि यह df में bug जैसी दिखती है। Rotation के बाद पुरानी file directory listing से गायब हो जाती है जबकि daemon उसे अभी भी open रखता है, इसलिए du -sh /var/log बहुत कम space दिखाता है जबकि df -h disk full होने की रिपोर्ट देता है। space तभी वापस आती है जब process अपनी log file को फिर से open करती है, जिसके लिए config में postrotate reload line होती है। sudo lsof -nP +L1 उन deleted files को list करता है जो अभी भी open हैं, और उन्हें hold करने वाली process का नाम बताता है। किसी rule को बिना कुछ बदले test करने के लिए sudo logrotate -d /etc/logrotate.d/nginx का उपयोग करें।

कई सर्वरों से एक collector तक logs भेजना

जब एक से अधिक सर्वर हों, तो एक साथ कई Linux सर्वरों को manage करना तब आसान हो जाता है जब उनके logs एक ही स्थान पर प्राप्त हों। rsyslog अधिकांश distributions में पहले से ही installed होता है, इसलिए सबसे सस्ता central collector प्रत्येक sender पर एक file है।

*.* action(type="omfwd" target="logs.example.com" port="514" protocol="tcp")

इसे /etc/rsyslog.d/50-forward.conf के रूप में save करें, इसे sudo rsyslogd -N1 के साथ check करें, जो configuration को validate करता है और कुछ भी start किए बिना exit हो जाता है, फिर rsyslog को restart करें। Collector पर, TCP input को enable करें।

module(load="imtcp")
input(type="imtcp" port="514")

दो चेतावनियाँ, दोनों यांत्रिक हैं। Plain syslog में कोई encryption और कोई authentication नहीं होता है, इसलिए जो कुछ भी port 514 तक पहुँच सकता है, वह ऐसी log lines inject कर सकता है जो बिल्कुल आपकी logs जैसी दिखती हैं। इसे एक private network या VPN से bind करें और port को firewall करें। दूसरी बात, default action queue memory में रहती है, इसलिए जब collector तक पहुँचा न जा सके, तो queue भर जाती है और messages drop हो जाते हैं, जिनकी कोई copy नहीं बचती। rsyslog उस स्थिति के लिए अपने reliable forwarding tutorial में disk assisted queue का documentation देता है।

ELK stack छोटे VPS पर क्यों फिट नहीं होता

ELK का अर्थ है स्टोरेज और सर्च के लिए Elasticsearch, इनजेशन पाइपलाइन के लिए Logstash, और इंटरफेस के लिए Kibana। इसका आधार JVM heap है, जिसे किसी भी लॉग के आने से पहले ही सेट कर दिया जाता है।

Elastic का डॉक्यूमेंटेशन कहता है कि heap को प्रत्येक Elasticsearch node के लिए उपलब्ध कुल मेमोरी के 50% से अधिक न रखें, क्योंकि यह प्रोसेस off-heap buffers का भी उपयोग करती है और इंडेक्स फाइलों को तेजी से पढ़ने के लिए ऑपरेटिंग सिस्टम के फाइल कैश पर निर्भर करती है। इसलिए, 2 GB heap का मतलब है कि आपको 4 GB वाली मशीन चाहिए, और यह Kibana या उस मुख्य काम से पहले की आवश्यकता है जिसके लिए सर्वर खरीदा गया था। Elastic यह भी बताता है कि Elasticsearch नोड की भूमिकाओं और कुल मेमोरी के आधार पर heap का आकार स्वचालित रूप से निर्धारित करता है, जिसका अर्थ है कि एक छोटे सर्वर पर heap भी छोटा होगा और वह अपना अधिकांश समय garbage collecting में ही बिताएगा।

Logstash वह हिस्सा है जो छोटे बजट को पूरी तरह बिगाड़ देता है। Elastic का अपना JVM सेटिंग्स पेज सामान्य इनजेशन के लिए 4GB से कम और 8GB से अधिक न होने वाली heap की सिफारिश करता है। यह एक 4 GB VPS की पूरी क्षमता है, जो पाइपलाइन के बीच में केवल एक प्रोसेस के लिए है।

ChartDocumented JVM heap settings, from each project's own docs
The data behind this chart
[
  {
    "label": "Loki plus Alloy (no JVM)",
    "documented_heap_mb": 0
  },
  {
    "label": "OpenSearch demo compose",
    "documented_heap_mb": 512
  },
  {
    "label": "OpenSearch production example",
    "documented_heap_mb": 2048
  },
  {
    "label": "Logstash recommended minimum",
    "documented_heap_mb": 4096
  }
]

ये वे मान हैं जिन्हें प्रत्येक प्रोजेक्ट अपने डॉक्यूमेंटेशन में प्रकाशित करता है। ये किसी टेस्ट बॉक्स पर लिए गए माप नहीं हैं, और आपका वर्कलोड इन्हें बदल देगा। OpenSearch की sample compose फाइल डेमो के लिए 512 MB प्रति नोड और प्रोडक्शन उदाहरण में 2048 MB सेट करती है, जबकि Logstash की अनुशंसित निचली सीमा 4096 MB है। heap कॉलम में Loki और Alloy के लिए 0 मान दिखता है क्योंकि वे Go प्रोग्राम हैं जिन्हें कोई JVM heap रिजर्व करने की आवश्यकता नहीं होती। एक संख्या में यही पूरा अंतर है: एक JVM घटक अपना रिजर्वेशन ले लेता है, चाहे लॉग आए या न आए।

यदि आप फिर भी एक छोटे सर्वर पर Elastic stack चलाना चाहते हैं, तो Logstash को हटा दें और एक हल्के collector के साथ सीधे Elasticsearch में डेटा भेजें। Logstash का अस्तित्व बड़े पैमाने पर पार्सिंग और ट्रांसफॉर्मेशन के लिए है, और एक सर्वर पर आप वह काम edge पर कर सकते हैं या उसे छोड़ सकते हैं।

Elasticsearch और OpenSearch दोनों को vm.max_map_count को 262144 तक बढ़ाने की आवश्यकता होती है, क्योंकि वे इंडेक्स फाइलों को memory map करते हैं और Linux की डिफ़ॉल्ट सीमा उनके लिए बहुत कम है। एक नया सर्वर सेटअप करने पर यदि कोई कंटेनर स्टार्ट होने के कुछ सेकंड बाद ही बंद हो जाता है, तो आमतौर पर यही कारण होता है।

OpenSearch या Elasticsearch: आप किसे deploy कर सकते हैं?

लाइसेंस का संक्षिप्त इतिहास जानना आवश्यक है, क्योंकि यह तय करता है कि आप क्या चलाने के लिए अधिकृत हैं। जनवरी 2021 में, Elastic ने Elasticsearch और Kibana को Apache 2.0 से हटाकर दोहरे SSPL (server side public license) और Elastic License 2.0 मॉडल पर स्थानांतरित कर दिया। AWS ने अंतिम Apache 2.0 कोड को OpenSearch के रूप में fork किया, जो Apache 2.0 के अंतर्गत ही बना हुआ है। सितंबर 2024 में, Elastic ने मुफ्त सोर्स कोड के लिए एक और विकल्प के रूप में AGPLv3 (GNU Affero General Public License version 3) को जोड़ा। यदि आप एक व्यक्ति के रूप में एक VPS पर self-hosting कर रहे हैं, तो ये सभी लाइसेंस आपको ऐसा करने की अनुमति देते हैं। लाइसेंस की शर्तें तब लागू होती हैं जब आप इस सॉफ़्टवेयर को अन्य लोगों को managed service के रूप में प्रदान करते हैं।

एक छोटे सर्वर पर व्यावहारिक अंतर इसके इतिहास की तुलना में कम है, क्योंकि दोनों के भीतर एक ही इंजन काम करता है। इनके नाम अलग-अलग हैं: index lifecycle को OpenSearch में ISM (index state management) और Elasticsearch में ILM (index lifecycle management) कहा जाता है। अगस्त 2026 तक, OpenSearch 2.12 और उसके बाद के संस्करण पहली बार चलाने पर admin password सेट किए बिना start होने से मना कर देते हैं।

sudo sysctl -w vm.max_map_count=262144
printf 'vm.max_map_count = 262144\n' | sudo tee /etc/sysctl.d/99-opensearch.conf
docker run -d -p 9200:9200 -p 9600:9600 -e "discovery.type=single-node" \
  -e "OPENSEARCH_INITIAL_ADMIN_PASSWORD=<custom-admin-password>" \
  opensearchproject/opensearch:latest

sysctl -w लाइन सेटिंग को तुरंत लागू करती है और /etc/sysctl.d/ में मौजूद फ़ाइल वह हिस्सा है जो reboot के बाद भी बनी रहती है। जाँचें कि container curl -k -u admin:<password> https://localhost:9200 के साथ चालू हुआ है या नहीं। यह demo certificate का उपयोग करके https पर जवाब देता है, इसलिए -k सत्यापन (verification) को छोड़ देता है, और एक सफल प्रतिक्रिया एक छोटा JSON ब्लॉक होता है जो cluster और version का नाम बताता है। OpenSearch install पेज Docker Desktop उपयोगकर्ताओं को यह भी सलाह देता है कि host को कम से कम 4 GB memory दें, जो यह संकेत देता है कि यह process कितनी संसाधन क्षमता की अपेक्षा रखती है।

Loki का आकार छोटा कैसे रहता है: पूर्ण टेक्स्ट इंडेक्स के बजाय लेबल्स का उपयोग

Loki लेबल्स पर एक इंडेक्स रखता है और लॉग लाइन्स को कंप्रेस्ड चंक्स (compressed chunks) के रूप में स्टोर करता है। एक क्वेरी पहले स्ट्रीम्स का चयन करती है और फिर टेक्स्ट को फिल्टर करती है। {unit="ssh.service"} |= "Failed password" अपने लेबल द्वारा स्ट्रीम को चुनता है, और फिर उस स्ट्रिंग के लिए उन चंक्स को स्कैन करता है। लाइन की बॉडी को इंडेक्स नहीं किया जाता है, इसलिए इनजेशन (ingestion) सस्ता रहता है और मेमोरी में रखने के लिए कोई इनवर्टेड इंडेक्स नहीं होता है। लागत क्वेरी के समय पर स्थानांतरित हो जाती है, और जब आप आमतौर पर जानते हैं कि आप किस सर्विस को देख रहे हैं, तो यह एक अच्छा सौदा है।

Grafana का डॉक्यूमेंटेशन मोनोलिथिक मोड, जिसका अर्थ है -target=all के साथ एक ही प्रोसेस में पूरा Loki, को प्रति दिन लगभग 20GB तक के छोटे रीड और राइट वॉल्यूम के लिए उपयुक्त बताता है। एक VPS इसके भीतर आसानी से काम करता है।

इसमें मुख्य समस्या लेबल कार्डिनैलिटी (label cardinality) की है। लेबल वैल्यूज का प्रत्येक विशिष्ट संयोजन एक स्ट्रीम है, और स्ट्रीम की संख्या Loki की मेमोरी और इंडेक्स के आकार को निर्धारित करती है। क्लाइंट IP एड्रेस या रिक्वेस्ट आइडेंटिफायर रखने वाला लेबल प्रत्येक वैल्यू के लिए एक स्ट्रीम बनाता है, इसलिए एक व्यस्त वेब सर्वर एक दिन में हजारों स्ट्रीम उत्पन्न कर सकता है और प्रोसेस तब तक बढ़ती है जब तक कि कर्नल उसे रोक न दे। लेबल्स को उन वैल्यूज तक सीमित रखें जिन्हें आप कागज पर गिन सकें: unit, host, job, level। परिवर्तनशील विवरण को लाइन में ही रखें, जहाँ एक फिल्टर एक्सप्रेशन उसे क्वेरी के समय ढूंढ सके।

एक VPS पर Loki और Alloy इंस्टॉल करना

दो प्रक्रियाएं यह काम करती हैं। Loki लॉग्स को स्टोर करता है और queries का जवाब देता है। Grafana Alloy लॉग्स को पढ़ता है और उन्हें आगे भेजता है। पहले Promtail का उपयोग shipper के रूप में किया जाता था, लेकिन 2 March 2026 को यह end of life हो गया है, इसलिए नए इंस्टॉलेशन में Alloy का उपयोग होता है और Loki का अपना Docker उदाहरण अब एक Alloy कॉन्फ़िगरेशन के साथ आता है।

wget https://raw.githubusercontent.com/grafana/loki/v3.7.0/cmd/loki/loki-local-config.yaml -O loki-config.yaml

उस फ़ाइल का उपयोग करने से पहले उसे पढ़ें। यह path_prefix: /tmp/loki को /tmp/loki/chunks के अंतर्गत chunks के साथ सेट करता है, जो डेमो के लिए सही है लेकिन सर्वर के लिए गलत है: कंटेनर के /tmp के अंतर्गत कुछ भी कंटेनर के फिर से बनने पर सुरक्षित नहीं रहता, इसलिए इमेज अपडेट होते ही आपका इतिहास गायब हो जाएगा। इसे उस पाथ पर पॉइंट करें जिसे आप माउंट करते हैं।

common:
  instance_addr: 127.0.0.1
  path_prefix: /loki
  storage:
    filesystem:
      chunks_directory: /loki/chunks
      rules_directory: /loki/rules
  replication_factor: 1
  ring:
    kvstore:
      store: inmemory
docker volume create loki-data
docker run --name loki -d \
  -v $(pwd):/mnt/config -v loki-data:/loki \
  -p 127.0.0.1:3100:3100 \
  grafana/loki:3.7.0 -config.file=/mnt/config/loki-config.yaml
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:3100/ready

उस अंतिम कमांड को 200 प्रिंट करना चाहिए, क्योंकि Loki के ट्रैफ़िक स्वीकार करने के लिए तैयार होते ही /ready HTTP 200 रिटर्न करता है। कुछ और आने का मतलब है कि प्रक्रिया अभी भी शुरू हो रही है या कॉन्फ़िगरेशन अस्वीकार कर दिया गया है, और docker logs loki यह बताता है कि ऐसा क्यों हुआ। रन कमांड में दो विवरण जानबूझकर दिए गए हैं। पोर्ट केवल 127.0.0.1 पर पब्लिश किया गया है, क्योंकि सैंपल कॉन्फ़िगरेशन में auth_enabled: false शामिल है और Loki में अपना कोई यूजर ऑथेंटिकेशन नहीं है, इसलिए पोर्ट 3100 तक पहुँचने वाला कोई भी व्यक्ति हर लॉग को पढ़ सकता है और नकली लॉग लिख सकता है। इसे loopback पर रखें, या VPN या ऑथेंटिकेटिंग reverse proxy के पीछे रखें। नामित वॉल्यूम महत्वपूर्ण है क्योंकि इमेज loki यूजर के रूप में UID 10001 के साथ चलती है, इसलिए root के स्वामित्व वाली bind mounted होस्ट डायरेक्टरी कंटेनर द्वारा लिखने योग्य नहीं होती है।

sudo apt-get update && sudo apt-get install -y gpg wget
sudo mkdir -p /etc/apt/keyrings/
sudo wget -O /etc/apt/keyrings/grafana.asc https://apt.grafana.com/gpg-full.key
echo "deb [signed-by=/etc/apt/keyrings/grafana.asc] https://apt.grafana.com stable main" \
  | sudo tee /etc/apt/sources.list.d/grafana.list
sudo apt-get update
sudo apt-get install -y alloy

Alloy /etc/alloy/config.alloy को पढ़ता है। यह सिस्टम जर्नल और फ़ाइलों के एक सेट को लेता है, और दोनों को लोकल Loki पर भेजता है।

loki.write "local" {
  endpoint {
    url = "http://127.0.0.1:3100/loki/api/v1/push"
  }
}

loki.relabel "journal" {
  forward_to = []
  rule {
    source_labels = ["__journal__systemd_unit"]
    target_label  = "unit"
  }
}

loki.source.journal "read" {
  forward_to    = [loki.write.local.receiver]
  relabel_rules = loki.relabel.journal.rules
  labels        = {job = "systemd-journal", host = "app-01"}
}

local.file_match "nginx" {
  path_targets = [{"__path__" = "/var/log/nginx/*.log", "job" = "nginx", "host" = "app-01"}]
}

loki.source.file "nginx" {
  targets    = local.file_match.nginx.targets
  forward_to = [loki.write.local.receiver]
}

relabel नियम जर्नल फ़ील्ड __journal__systemd_unit को unit नामक लेबल में कॉपी करता है, जो बाद में {unit="ssh.service"} को काम करने योग्य बनाता है। उस नियम के बिना, यूनिट का नाम एंट्री के अंदर होता है न कि लेबल में, इसलिए आप इसे सिलेक्ट नहीं कर सकते और हर क्वेरी को सब कुछ स्कैन करना पड़ता है।

sudo systemctl reload alloy
systemctl show -p User alloy
sudo journalctl -n 5
sudo -u alloy journalctl -n 5

यहीं पर अधिकांश सेटअप अटक जाते हैं। Alloy अपने स्वयं के सर्विस अकाउंट के रूप में चलता है, root के रूप में नहीं, और सिस्टम जर्नल को पढ़ने के लिए systemd-journal ग्रुप की सदस्यता आवश्यक है, जबकि Debian और Ubuntu पर /var/log/nginx के अंतर्गत फ़ाइलें adm ग्रुप की होती हैं। उस अकाउंट को बदलें जिसे systemctl show ने अंतिम कमांड में प्रिंट किया था। यदि यह root रन की तुलना में बहुत कम एंट्रीज़ रिटर्न करता है, तो वह अकाउंट सिस्टम जर्नल को नहीं पढ़ सकता है, और आपका कॉन्फ़िगरेशन सही होने के बावजूद Loki खाली रहेगा। ग्रुप्स को जोड़ें और sudo usermod -aG systemd-journal,adm alloy के बाद sudo systemctl restart alloy के साथ रीस्टार्ट करें।

curl -G -s "http://127.0.0.1:3100/loki/api/v1/query_range" \
  --data-urlencode 'query={job="systemd-journal"}' \
  --data-urlencode 'limit=5' | jq '.data.result | length'

0 से ऊपर की संख्या का मतलब है कि उस लेबल के साथ streams मौजूद हैं और उनमें एंट्रीज़ हैं। 0 का मतलब है कि उस लेबल के अंतर्गत अभी तक कुछ भी नहीं आया है। एक डिफ़ॉल्ट सेटिंग एक सामान्य गलत अलार्म की व्याख्या करती है: loki.source.journal, max_age को 7h पर सेट करता है, इसलिए एक नई शुरुआत जर्नल के पिछले सात घंटों को पढ़ती है और उससे पुराना कुछ नहीं। मानव इंटरफ़ेस के लिए, उसी बॉक्स पर Grafana चलाएं और Loki डेटा सोर्स को http://127.0.0.1:3100. पर पॉइंट करें। कंटेनर लॉग्स के लिए एक अलग सोर्स की आवश्यकता होती है: Alloy चल रहे Docker कंटेनर्स को खोजता है और उन्हें tail करता है, जो कि Loki का अपना getting started उदाहरण करता है, और VPS पर एक सिंगल नोड k3s क्लस्टर पर वह जॉब उस पॉड लॉग डायरेक्टरी में चली जाती है जिसे kubelet लिखता है।

Retention: वह दिन चुनें जब आपके logs हट जाएंगे

लगभग कोई भी retention period तब तक नहीं चुनता जब तक disk भर न जाए, और फिर वे इसे सुबह के 3 बजे service बंद होने पर चुनते हैं। पहले दिन ही दो सवालों के आधार पर इसका निर्णय लें: आप वास्तव में कितना पीछे तक देखते हैं, और अगले महीने incident review के दौरान आपके पास क्या होना अनिवार्य है। एक single server के लिए, 14 से 30 दिन इन दोनों सवालों का जवाब देते हैं।

Loki तब तक कुछ भी delete नहीं करता जब तक आप compactor को enable न करें। Retention डिफ़ॉल्ट रूप से बंद रहता है, जो उन लोगों को हैरान करता है जिनका volume तब भर गया जब retention_period config में बिना कुछ किए पड़ा रहा।

limits_config:
  retention_period: 744h

compactor:
  working_directory: /loki/retention
  compaction_interval: 10m
  retention_enabled: true
  retention_delete_delay: 2h
  retention_delete_worker_count: 150
  delete_request_store: filesystem

744h का मतलब 31 दिन है। उस block को चार documented नियम नियंत्रित करते हैं:

  • Retention को compactor द्वारा लागू किया जाता है, और Grafana का documentation कहता है कि compactor को single instance के रूप में चलाएं। एक VPS पर यह अपने आप हो जाता है।
  • न्यूनतम retention period 24h है, और retention तभी काम करता है जब index period 24h हो। sample schema_config पहले से ही period: 24h का उपयोग करता है, इसलिए इसे न बदलें।
  • retention_enabled के true होने पर delete_request_store आवश्यक है। यह delete requests को रखने वाले store का नाम बताता है, इसलिए filesystem backed single node पर यह schema में मौजूद object_store: filesystem से मेल खाता है।
  • Chunks को पहले mark किया जाता है और retention_delete_delay के बाद हटाया जाता है, जो यहाँ 2h है, इसलिए खाली जगह policy के संकेत से कुछ समय बाद वापस मिलती है। reload के पांच मिनट बाद df द्वारा setting को न परखें।

OpenSearch अलग-अलग lines के बजाय पूरे indexes को delete करता है, यही कारण है कि log indexes प्रति दिन बनाए जाते हैं। एक ISM policy index को विभिन्न states से गुजारती है और पर्याप्त पुराना होने पर उसे delete कर देती है, और एक ism_template policy को नए indexes से जोड़ देता है ताकि आपको कभी याद न रखना पड़े।

ISM policy जो 14 दिनों के बाद log indexes को delete करती है
{
  "policy": {
    "description": "delete log indexes after 14 days",
    "default_state": "hot",
    "states": [
      {
        "name": "hot",
        "actions": [],
        "transitions": [
          { "state_name": "delete", "conditions": { "min_index_age": "14d" } }
        ]
      },
      {
        "name": "delete",
        "actions": [ { "delete": {} } ],
        "transitions": []
      }
    ],
    "ism_template": { "index_patterns": ["logs-*"], "priority": 100 }
  }
}

इसे _plugins/_ism/policies/logs-retention पर PUT के साथ create करें। यह template policy के अस्तित्व में आने के बाद बनाए गए indexes पर लागू होता है, इसलिए disk पर पहले से मौजूद किसी भी चीज़ के साथ policy को मैन्युअल रूप से जोड़ना होगा।

आप जो भी system चलाएं, एक retention number उतना ही अच्छा है जितना उसके पीछे का free space check। 14 दिनों पर delete करना आपकी मदद नहीं करेगा यदि 10 दिनों के logs पहले ही volume को भर चुके हैं, इसलिए policy को VPS पर disk health monitoring और 80% उपयोग होने पर एक alert के साथ जोड़ें।

प्रति GB लॉग के लिए कितनी डिस्क चाहिए

इसका सही उत्तर आपकी लाइनों और फील्ड्स पर निर्भर करता है, इसलिए किसी प्रकाशित अनुपात पर भरोसा करने के बजाय अपने डेटा पर इसे मापें। दोनों के काम करने के तरीके इतने अलग हैं कि आप दिशा का अनुमान लगा सकते हैं। OpenSearch और Elasticsearch हर indexed फील्ड पर एक inverted index लिखते हैं, जो स्टोर किए गए डॉक्यूमेंट के साथ रहता है। इसलिए डिस्क पर जाने वाला डेटा रॉ टेक्स्ट से बड़ा होता है और हर replica इसे गुणा कर देता है। एक सिंगल नोड पर replica count को 0 पर सेट करें, क्योंकि उसी नोड पर मौजूद replica shard उस नोड के फेल होने पर नहीं बच सकता: इसे 1 पर छोड़ने से डिस्क का उपयोग दोगुना हो जाता है और क्लस्टर हेल्थ हमेशा के लिए yellow बनी रहती है। Loki कंप्रेस्ड चंक्स और एक छोटा लेबल इंडेक्स लिखता है, इसलिए इसका फुटप्रिंट लाइनों के कंप्रेस्ड आकार के अनुसार होता है।

sudo du -sh /var/lib/docker/volumes/loki-data/_data
curl -k -u admin:<password> "https://localhost:9200/_cat/indices?v&h=index,docs.count,store.size"

जो भी आप पर लागू हो, उसे लगातार दो दिनों तक चलाएं। अंतर ही आपकी दैनिक वृद्धि है। इसे अपने रिटेंशन दिनों से गुणा करें, compaction और merges के लिए लगभग 30% अतिरिक्त जगह जोड़ें, और फिर इसकी तुलना वॉल्यूम से करें। यदि यह फिट नहीं होता है, तो डिस्क खरीदने से पहले रिटेंशन कम करें, क्योंकि बड़ा वॉल्यूम केवल उसी समस्या को कुछ हफ्तों के लिए आगे बढ़ा देता है।

छोटे सर्वर पर सबसे पहले क्या खराब होता है

सबसे पहले Memory खत्म होती है। kernel का OOM (out of memory) killer एक बड़ी process को चुनता है, और logging box पर सबसे बड़ी process अक्सर JVM होती है। journalctl -k | grep -i "killed process" ब्रैकेट में process के नाम के साथ kill होने की घटना को दर्शाता है। जरूरी नहीं कि शिकार हमेशा log stack ही हो: sshd या आपकी database को भी चुना जा सकता है, और इसी तरह एक logging experiment उस application को बंद कर देता है जिसके logs आप चाहते थे। containers को स्पष्ट सीमाएं (ceilings) दें ताकि विफलता वहीं हो जहां आपने तय किया है, जिसके लिए Docker Compose में memory limits का उपयोग किया जाता है।

दूसरे नंबर पर Disk भरती है, और search engines एक विशिष्ट और पहचानने योग्य तरीके से विफल होते हैं। Elasticsearch और OpenSearch कई स्तरों पर disk usage की निगरानी करते हैं। low watermark 85% पर और high watermark 90% पर होता है। 95% के flood stage पर, उस node पर shard वाला प्रत्येक index index.blocks.read_only_allow_delete block प्राप्त करता है, और फिर writes blocked by: [FORBIDDEN/12/index read-only / allow delete (api)] के साथ विफल हो जाते हैं। usage के high watermark से नीचे आने पर block को हटा दिया जाता है। पहले खाली जगह बनाएं, फिर यदि समस्या बनी रहती है तो ही हाथ से block को clear करें।

curl -k -u admin:<password> -X PUT "https://localhost:9200/_all/_settings" \
  -H 'Content-Type: application/json' \
  -d '{"index.blocks.read_only_allow_delete": null}'

Loki अधिक चुपचाप विफल होता है। इसमें कोई read-only mode नहीं होता, इसलिए full volume होने पर sender पर failed pushes और query results में gaps दिखाई देते हैं, और cardinality की समस्या error के बजाय memory में धीमी वृद्धि के रूप में सामने आती है। incident के बाद नहीं, बल्कि एक schedule के अनुसार chunks directory के आकार की निगरानी करें।

अंतिम विफलता गलत data डालना है। एक log system, metrics system नहीं होता: हर 10 second में sample किया गया और text के रूप में store किया गया CPU load रखना महंगा है और उसे graph करना कठिन है, और यह काम Ubuntu 24.04 पर Zabbix monitoring server जैसे किसी tool का है। Application exceptions को grouping, deduplication और stack trace view की आवश्यकता होती है, जो self-hosted error tracker का काम है। साइट down है या नहीं, यह जानना एक अलग काम है, जिसका उत्तर Uptime Kuma जैसे uptime और status page द्वारा दिया जाता है। log system को केवल text की उन पंक्तियों के लिए रखें जिन्हें कोई व्यक्ति पढ़ेगा।

FAQ

क्या मुझे अपने सर्वर लॉग्स खोजने के लिए Elasticsearch की आवश्यकता है?

एक या दो सर्वर के लिए इसकी आवश्यकता नहीं है। journalctl पहले से ही unit, priority, boot और time range के आधार पर फ़िल्टर करता है, और rotated फ़ाइलें grep और zgrep के माध्यम से देखी जा सकती हैं। सर्च क्लस्टर तब उपयोगी होता है जब आपके पास कई मशीनें हों, जब आपको उन सभी में एक साथ free text search करने की आवश्यकता हो, या जब कई लोगों को एक साझा इंटरफ़ेस की आवश्यकता हो। इससे कम स्तर पर, size limit और retention time के साथ journald बिना किसी अतिरिक्त RAM के वही काम करता है।

self-hosted लॉग मैनेजमेंट के लिए मुझे कितनी RAM चाहिए?

अंदाजा लगाने के बजाय प्रत्येक प्रोजेक्ट द्वारा प्रकाशित आंकड़ों का उपयोग करें। Loki और Alloy, Go प्रोग्राम हैं जिनमें पहले से heap रिज़र्व करने की आवश्यकता नहीं होती, और Grafana के दस्तावेज़ों के अनुसार monolithic Loki लगभग 20GB प्रति दिन तक का डेटा संभाल सकता है। OpenSearch के sample compose में डेमो के लिए 512 MB heap और प्रोडक्शन उदाहरण में 2 GB heap सेट की जाती है। Elastic का कहना है कि heap कुल मेमोरी के 50% या उससे कम होनी चाहिए, इसलिए 2 GB heap का मतलब है कि Kibana से पहले ही 4 GB की मशीन चाहिए। Logstash के दस्तावेज़ स्वयं कम से कम 4GB heap की अनुशंसा करते हैं। ये दस्तावेजी सेटिंग्स हैं, बेंचमार्क नहीं, इसलिए प्लान का आकार तय करने से पहले अपने लोड को मापें।

लॉग के लिए Loki और OpenSearch में वास्तविक अंतर क्या है?

इंडेक्स मॉडल। Loki केवल labels को इंडेक्स करता है और लॉग बॉडी को compressed chunks के रूप में रखता है जिन्हें query time पर स्कैन किया जाता है, इसलिए writes सस्ते होते हैं और व्यापक queries महंगी होती हैं। OpenSearch फ़ील्ड्स की सामग्री को इंडेक्स करता है, इसलिए arbitrary full text search तेज़ होती है, लेकिन इसके लिए मेमोरी और डिस्क दोनों का उपयोग होता है। जब आप जानते हों कि आपको किस सर्विस और समय सीमा को देखना है, तो Loki चुनें। जब आपको ऐसे टेक्स्ट को खोजना हो जिसका आप पहले से अनुमान नहीं लगा सकते, तो OpenSearch चुनें।

मुझे VPS पर लॉग्स कितने समय तक रखने चाहिए?

डिस्क द्वारा मजबूर किए जाने से पहले ही संख्या चुन लें। इसे प्रति सिस्टम केवल एक स्थान पर सेट करें: journald के लिए MaxRetentionSec= और SystemMaxUse=, Loki के लिए compactor सक्षम करके retention_period, और OpenSearch के लिए min_index_age के साथ ISM पॉलिसी। अधिकांश सिंगल सर्वर सेटअप के लिए, 14 से 30 दिन का समय डिबगिंग और incident review के लिए पर्याप्त है। जिसे भी आपको लंबे समय तक रखना है, उसकी एक कॉपी सर्वर से बाहर रखें, क्योंकि जो लॉग केवल उसी सर्वर पर रखा गया है जो फेल हो गया, वह रिकॉर्ड नहीं माना जा सकता।

क्या Promtail अभी भी Loki को लॉग भेजने का सही तरीका है?

नहीं। Promtail का जीवनकाल 2 March 2026 को समाप्त हो गया है, और Grafana Alloy ने इसकी जगह ले ली है। Loki का अपना Docker install उदाहरण अब Alloy कॉन्फ़िगरेशन का उपयोग करता है, और Grafana एक कनवर्टर प्रदान करता है जो मौजूदा Promtail कॉन्फ़िगरेशन को Alloy सिंटैक्स में बदल देता है। मौजूदा Promtail इंस्टॉल चलता रहेगा, लेकिन इसे कोई सुधार (fixes) नहीं मिलेंगे, इसलिए माइग्रेशन को एक ऐसे मेंटेनेंस कार्य के रूप में देखें जिसे अनिश्चित काल के लिए टाला नहीं जा सकता।

#logging#loki#opensearch#journald#मॉनिटरिंग