VPS पर SQLite को production में कब चलाएँ?
एक VPS पर छोटे apps के लिए SQLite क्यों सही है, जानें। WAL mode, busy_timeout, Litestream replication और single-writer सीमा को सुरक्षित तरीके से समझें।
VPS पर SQLite सही production database कब है
VPS पर अधिकांश छोटे applications के लिए production में SQLite चलाना सही विकल्प है। कारण सरल है: एक machine पर एक process द्वारा एक file में लिखने के लिए database server की आवश्यकता नहीं होती। निगरानी के लिए कोई daemon नहीं होता। firewall में खोलने के लिए कोई port नहीं होता। बदलने के लिए कोई password नहीं होता। चालू रखने के लिए दूसरी machine नहीं होती। कोई query network round trip के बजाय function call होती है। इसलिए चालीस queries चलाने वाले page की लागत चालीस function calls होती है।
इसकी सीमाएँ स्पष्ट और वास्तविक हैं। पूरे database file में एक समय में केवल एक writer की अनुमति होती है। File को दो machines के बीच share नहीं किया जा सकता। एक application चलाने वाले single VPS के लिए दोनों सीमाएँ ठीक हैं। जैसे ही आपका setup इस आकार से बड़ा होता है, दोनों सीमाएँ गंभीर समस्या बन जाती हैं। यह guide server पर SQLite को सुरक्षित बनाने वाली settings, Litestream के साथ continuous backup और उस बिंदु को कवर करती है जहाँ आपको रुक जाना चाहिए।
पहले command line tool install करें। नीचे की सभी commands Ubuntu 24.04 पर चलाई गई हैं।
sudo apt update
sudo apt install -y sqlite3
sqlite3 --versionयह 3. से शुरू होने वाला version, उसके बाद build date और source hash दिखाता है। July 2026 तक Ubuntu 24.04 में SQLite 3.45.1 उपलब्ध है। आपका application संभवतः इस binary का उपयोग नहीं करता। अधिकांश language runtimes SQLite library की अपनी copy शामिल करते हैं, जो अक्सर इससे नई होती है। इसलिए किसी recent feature पर निर्भर करने से पहले अपने database driver द्वारा report किया गया version जाँचें।
WAL mode में सबसे पहले यही बदलाव क्यों करते हैं
डिफ़ॉल्ट रूप से SQLite rollback journal का उपयोग करता है। किसी page को बदलने से पहले यह मूल page को -journal file में कॉपी करता है, फिर database को उसी स्थान पर संपादित करता है। इसे सुरक्षित रूप से करने के लिए यह पूरी file पर exclusive lock लगाता है। इसलिए कोई भी write चलने के दौरान हर reader को प्रतीक्षा करनी पड़ती है। Laptop पर इसका पता नहीं चलता। Web server पर कोई धीमा write database को access करने वाली हर request को रोक देता है।
WAL (write-ahead log) mode इस क्रम को उलट देता है। Writer नए pages को अलग -wal file में जोड़ता है और मुख्य database को अपरिवर्तित छोड़ देता है। Readers उस snapshot पर मुख्य file को पढ़ते रहते हैं, जिस पर उन्होंने पढ़ना शुरू किया था। इसलिए readers writer को block नहीं करते और writer readers को block नहीं करता। बाद में checkpoint संचित WAL pages को मुख्य database में कॉपी करता है। यह एक बदलाव SQLite को web application के पीछे उपयोग करने योग्य बनाने में सबसे बड़ा योगदान देता है।
WAL मोड चालू करें और पुष्टि करें कि यह स्थायी है
mkdir -p ~/app
sqlite3 ~/app/app.db "PRAGMA journal_mode=WAL;"कमांड wal प्रिंट करती है। यह आउटपुट केवल सजावटी नहीं है। PRAGMA journal_mode वह मोड लौटाता है जिसमें डेटाबेस वास्तव में चल रहा है। इसलिए delete का उत्तर मिलने का अर्थ है कि बदलाव विफल हुआ और आप अभी भी rollback journal पर हैं।
WAL मोड स्थायी होता है। यह connection setting के बजाय डेटाबेस header में मौजूद flag है। इसलिए इसे प्रत्येक database file के लिए केवल एक बार चलाना होता है। इसके बाद हर connection इसे अपनाता है, reboot के बाद भी। नई connection से इसकी पुष्टि करें।
sqlite3 ~/app/app.db "PRAGMA journal_mode;"अब एक table बनाएँ और देखें कि disk पर क्या दिखाई देता है।
sqlite3 ~/app/app.db <<'SQL'
CREATE TABLE IF NOT EXISTS notes (id INTEGER PRIMARY KEY, body TEXT NOT NULL);
INSERT INTO notes (body) VALUES ('first row');
SQL
ls -l ~/app/अब तीन files हैं: app.db, app.db-wal और app.db-shm। -wal file में वे committed pages रहती हैं जिनका अभी तक checkpoint नहीं हुआ है। -shm file एक shared memory index है, जिसे हर connection map करता है, ताकि सभी connections WAL की सामग्री पर सहमत रहें। दोनों डेटाबेस का हिस्सा हैं और scratch files नहीं हैं। Application चलने के दौरान केवल app.db की copy बनाने पर ऐसी file मिलेगी जिसमें हाल की सभी commits अनुपस्थित होंगी। app.db को delete करके बाकी दोनों files को वहीं छोड़ने पर SQLite उन पुराने WAL pages को उस नाम से बनने वाली नई file पर लागू कर देगा। इसी कारण database को reset करने का प्रयास करते समय fresh database corrupt हो जाता है।
हर production app के लिए आवश्यक connection settings
केवल journal_mode database में संग्रहीत होता है। नीचे दी गई हर दूसरी setting प्रति connection लागू होती है। इसका अर्थ है कि आपका application इसे खोले गए हर connection पर चलाए। इसमें pool द्वारा background में बनाए गए सभी connection भी शामिल हैं।
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;busy_timeout = 5000 SQLite को locked database के लिए 5000 milliseconds तक पुनः प्रयास करते रहने का निर्देश देता है। इसके बाद यह database is locked लौटाता है। डिफ़ॉल्ट मान 0 है। इसलिए डिफ़ॉल्ट रूप से, दो writers के overlap होते ही SQLite पहली बार में तुरंत fail हो जाता है। यह एक setting SQLite से संबंधित बताए जाने वाले अधिकांश lock errors को हटा देती है।
synchronous = NORMAL WAL mode में सही setting है। इसके trade-off को समझना आवश्यक है। FULL पर SQLite हर commit के समय WAL पर fsync चलाता है। NORMAL पर यह checkpoints के समय sync करता है। SQLite documentation स्पष्ट रूप से बताता है कि इससे क्या खोता है: power failure या hard reset के बाद transactions durable नहीं रहते। उस power loss से database corrupt नहीं हो सकता। केवल वे अंतिम commits खोते हैं जो disk तक नहीं पहुंच पाए थे। VPS पर यह सामान्यतः सही trade-off है, क्योंकि इससे हर write के path से एक fsync हट जाता है।
foreign_keys = ON backwards compatibility के लिए डिफ़ॉल्ट रूप से off है और यह प्रति connection लागू होता है। REFERENCES clauses वाली schema तब तक कुछ भी enforce नहीं करती, जब तक हर connection इसे on न कर दे।
एक और setting बाद में महत्वपूर्ण होती है। WAL का आकार 1000 pages से अधिक होने पर SQLite अपने-आप checkpoints करता है। उस समय transaction पूरा करने वाला कोई भी connection यह काम करता है। अपने-आप में यह ठीक है। Litestream चलने पर यह एक प्रश्न बन जाता है, क्योंकि Litestream checkpoints के समय पर नियंत्रण रखना चाहता है।
database is locked सेट करने के बाद भी database is locked क्यों होता है
यह वह विफलता है जिसके कारण लोग Postgres पर वापस चले जाते हैं। इसका एक विशिष्ट कारण है।
Busy timeout एक busy handler स्थापित करता है, लेकिन SQLite यह गारंटी नहीं देता कि वह उसे कॉल करेगा।
यदि SQLite यह निर्धारित करता है कि busy handler को कॉल करने से deadlock हो सकता है, तो वह busy handler को कॉल करने के बजाय सीधे application को SQLITE_BUSY लौटाता है।
जिस deadlock से SQLite बच रहा है, वह transaction के upgrade होने पर होता है। SQLite में अकेला BEGIN का अर्थ BEGIN DEFERRED है। यदि उसके बाद पहला statement SELECT है, तो आप read transaction में हैं। जब उसी transaction में बाद का UPDATE write transaction में बदलना चाहता है और आपके read शुरू होने के बाद किसी अन्य connection ने write किया है, तो SQLite आपको प्रतीक्षा नहीं करा सकता। आपका snapshot पहले ही पुराना हो चुका है और प्रतीक्षा कराने से दोनों connections एक-दूसरे के विरुद्ध deadlock में फंस जाएंगे। Documentation इसका परिणाम सीधे बताती है:
बाद के write statements, यदि संभव हो, तो transaction को write transaction में upgrade करेंगे। अन्यथा वे SQLITE_BUSY लौटाएंगे।
आपका 5000 millisecond timeout कभी उपयोग ही नहीं किया जाता। Error तुरंत आ जाता है। इसलिए ऐसा लगता है कि setting ने कोई प्रभाव नहीं डाला।
इसका समाधान एक शब्द है।
BEGIN IMMEDIATE;
UPDATE notes SET body = 'edited' WHERE id = 1;
COMMIT;BEGIN IMMEDIATE शुरुआत में ही write lock लेता है, किसी भी चीज को पढ़ने से पहले। कोई upgrade नहीं होता। इसलिए ऐसा कोई deadlock नहीं होता जिससे बचना पड़े। Busy handler लागू होता है और connection विफल होने के बजाय अपनी बारी की प्रतीक्षा करता है। केवल read करने वाले transactions को deferred रखें। जिस transaction में write शामिल हो, उसे immediate होना चाहिए।
Lock errors का दूसरा कारण पहचानना अधिक कठिन है: slow work के दौरान write transaction को खुला रखना। SQLite writers को serialise करता है। इसलिए ऐसा transaction जो खुलता है, network पर किसी external API को call करता है और फिर commit करता है, उस call की पूरी अवधि तक हर दूसरे writer को block करेगा। आवश्यक data पढ़ें, transaction बंद करें, slow work करें, फिर परिणाम store करने के लिए एक छोटा write transaction खोलें।
Litestream के साथ निरंतर बैकअप
रात में बनाई गई कॉपी से अधिकतम एक दिन के लेखन का डेटा खो सकता है। सक्रिय SQLite database पर cp चलाने से ऐसी कॉपी बन सकती है जो खुल ही न सके। दो तरीके सुरक्षित हैं। sqlite3 app.db ".backup /path/to/backup.db" SQLite के online backup interface का उपयोग करता है और उपयोग में चल रहे database पर काम करता है। Litestream इससे आगे जाता है। यह WAL की निगरानी करता है और परिवर्तनों को लगातार object storage पर अपलोड करता है। इससे डेटा-हानि की अधिकतम स्थिति एक दिन से घटकर लगभग एक सेकंड रह जाती है।
Litestream एक Go binary है, जो आपके application के साथ चलता है। यह app और database के बीच नहीं बैठता। आपका application पहले की तरह SQLite में लिखता है। Litestream WAL को पढ़कर बदले हुए डेटा को अपलोड करता है।
cd /tmp
curl -fsSL -O https://github.com/benbjohnson/litestream/releases/download/v0.5.14/litestream-0.5.14-linux-x86_64.deb
sudo dpkg -i litestream-0.5.14-linux-x86_64.deb
litestream versionJuly 2026 तक official Linux install page में v0.5.14 release दर्ज है। इसके बाद 21 July 2026 को v0.5.15 जारी हुआ। releases page पर मौजूद current tag के अनुसार दोनों lines में version बदलें। यदि आपका VPS arm64 है, तो इसके बजाय matching arm64 package लें।
Configuration file /etc/litestream.yml पर रहती है। शुरुआत local file replica से करें। इससे cloud credentials के बिना पूरी प्रक्रिया की पुष्टि हो जाती है।
dbs:
- path: /home/appuser/app/app.db
replica:
type: file
path: /var/backups/litestream/appध्यान दें कि field replica है, singular। Litestream 0.5 ने 0.3 series के replicas array को एक single replica block से बदल दिया। अब दो entries वाली config startup पर fail होती है। कई third-party guides अभी भी पुराना array दिखाती हैं। इसलिए search में मिले पहले example के बजाय ऊपर दिया गया shape copy करें। 0.5 series ने litestream wal subcommand का नाम बदलकर litestream ltx कर दिया, क्योंकि on-disk backup format बदल गया था।
कुछ enable करने से पहले जाँच लें कि config parse हो रही है।
sudo litestream databases -config /etc/litestream.ymlइसके बाद round trip को manually साबित करें। यह form config file को छोड़कर एक database को एक path पर replicate करता है।
mkdir -p /tmp/replica
litestream replicate ~/app/app.db file:///tmp/replica/appयह foreground में चलता रहता है। दूसरे shell में एक row लिखें और replica को नई file में restore करें।
sqlite3 ~/app/app.db "INSERT INTO notes (body) VALUES ('written after replication started');"
litestream restore -o /tmp/restored.db file:///tmp/replica/app
sqlite3 /tmp/restored.db "SELECT count(*) FROM notes;"इस count में नई row शामिल है। यदि ऐसा नहीं है, तो change अभी sync नहीं हुआ है। Litestream sync-interval पर push करता है, जिसका default 1 second है। इसलिए प्रतीक्षा करके फिर restore करें। यही एक second आपका recovery point भी है। Crash होने पर पिछले sync interval में लिखे गए डेटा का अधिकतम नुकसान हो सकता है। कोई भी configuration इस अंतराल को zero नहीं कर सकती।
वास्तविक storage के लिए replica block को S3 URL से बदलें। यह Amazon S3 और अन्य providers के S3-compatible object storage, दोनों के साथ काम करता है।
dbs:
- path: /home/appuser/app/app.db
replica:
url: s3://your-bucket-name/app
region: us-east-1
snapshot:
interval: 24h
retention: 24hCredentials को उस file में न रखें। Litestream environment से LITESTREAM_ACCESS_KEY_ID और LITESTREAM_SECRET_ACCESS_KEY पढ़ता है। इसलिए इन्हें root के स्वामित्व वाले mode 600 के systemd drop-in में रखें।
ऊपर दिए गए snapshot values defaults हैं। Retention का default अक्सर लोगों को चौंकाता है। Retention यह निर्धारित करता है कि Litestream snapshots और उनसे संबंधित files को कितने समय तक रखता है। इससे यह भी तय होता है कि आप कितने पुराने समय तक restore कर सकते हैं। Twenty-four hours का अर्थ है कि Monday की state से हुई खराब migration, जिसे आप Wednesday morning को देखते हैं, अब recover नहीं की जा सकती। retention: 168h को एक week पर सेट करें और अतिरिक्त storage की लागत स्वीकार करें।
आपको इसकी आवश्यकता पड़ने से पहले पुनर्स्थापना सत्यापित करें
litestream restore -o /tmp/check.db /home/appuser/app/app.db
sqlite3 /tmp/check.db "PRAGMA integrity_check;"
sqlite3 /tmp/check.db "SELECT count(*) FROM notes;"डेटाबेस पथ देने पर, litestream restore /etc/litestream.yml में संबंधित प्रतिकृति खोजता है और उसे डाउनलोड करता है। स्वस्थ फ़ाइल पर PRAGMA integrity_check, ok प्रिंट करता है। कोई भी अन्य आउटपुट यह दर्शाता है कि पुनर्स्थापित प्रति उपयोग योग्य नहीं है। इसे systemd service और timer के साथ निर्धारित समय पर चलाएँ और आउटपुट पढ़ें। जब तक आप किसी बैकअप को एक बार पुनर्स्थापित नहीं कर लेते, तब तक आपको यह ज्ञात नहीं होता कि वह काम करता है।
systemd के अंतर्गत Litestream चलाएँ
Debian पैकेज एक litestream यूनिट इंस्टॉल करता है, जो /etc/litestream.yml को पढ़ती है।
sudo systemctl enable litestream
sudo systemctl start litestream
sudo journalctl -u litestream -fस्वस्थ आउटपुट कॉन्फ़िगरेशन में मौजूद प्रत्येक डेटाबेस का नाम दिखाता है और फिर समय-समय पर होने वाली sync लाइनों के अलावा शांत रहता है। आपके डेटाबेस path पर no such file or directory की त्रुटि का अर्थ है कि कॉन्फ़िगरेशन में path गलत है या process उसे पढ़ नहीं सकता। डिफ़ॉल्ट रूप से यूनिट root के रूप में चलती है, जो इस कार्य के लिए आवश्यक से अधिक privilege है। Litestream को डेटाबेस और उसे रखने वाली directory, दोनों को पढ़ने और लिखने में सक्षम होना चाहिए, क्योंकि यह आपके डेटाबेस के पास मौजूद -wal और -shm files के साथ काम करता है। इसलिए इसे वही account दें, जिसका उपयोग आपका application पहले से करता है।
# /etc/systemd/system/litestream.service.d/override.conf
[Service]
User=appuser
Group=appuserइसे sudo systemctl daemon-reload और sudo systemctl restart litestream के साथ लागू करें। कम से कम privilege वाला dedicated service account सेट करने में कुछ मिनट लगते हैं। इससे backup agent और server पर चलने वाली दूसरी root process के बीच स्पष्ट अंतर रहता है।
यदि आप machine को पूरी तरह से फिर से बनाते हैं, तो एक ordering detail महत्वपूर्ण है। Application शुरू होने से पहले डेटाबेस restore होना चाहिए। litestream restore, -if-db-not-exists स्वीकार करता है। फ़ाइल पहले से मौजूद होने पर यह 0 के साथ exit करता है, इसलिए इसे प्रत्येक boot पर चलाना सुरक्षित है। इसे अपने application की unit में ExecStartPre line के रूप में रखें। इससे नया VPS डेटाबेस डाउनलोड कर लेता है, जबकि मौजूदा VPS पर कोई कार्य नहीं होता। यदि आप इसे एक ही स्थान पर रखना चाहते हैं, तो litestream replicate में संबंधित -restore-if-db-not-exists flag मौजूद है।
VPS पर SQLite की सीमाएँ
नेटवर्क फ़ाइल सिस्टम। यह ऐसी सीमा है जिसे configuration से दूर नहीं किया जा सकता। WAL mode के लिए database का उपयोग करने वाली प्रत्येक process को memory के एक छोटे क्षेत्र को साझा करना आवश्यक है। यही क्षेत्र -shm file उपलब्ध कराती है। SQLite documentation यह नियम स्पष्ट रूप से बताता है:
Database का उपयोग करने वाली सभी processes एक ही host computer पर होनी चाहिए; WAL network filesystem पर काम नहीं करता।
इसलिए mounted NFS (network file system) या SMB share पर मौजूद database corrupt हो सकता है। कोई pragma इसे रोक नहीं सकता। यहाँ एक अंतर है जिसे लोग अक्सर नज़रअंदाज़ करते हैं। Network block device, जिसे अधिकांश VPS providers अतिरिक्त storage के रूप में attach करते हैं, Linux को उस पर मौजूद सामान्य filesystem वाली सामान्य disk के रूप में दिखाई देता है। यह ठीक है। Mounted file share ठीक नहीं है।
दूसरा application server। कोई setting इसे संभव नहीं बनाती। जैसे ही एक ही data को serve करने के लिए दो machines की आवश्यकता होती है, आपको network पर communication करने वाले database की आवश्यकता होती है। इस बदलाव की योजना तब बनाएं, जब आपके पास अभी इसकी तैयारी के लिए समय हो।
Write-heavy workloads। एक समय में केवल एक writer होना file format की property है, tunable setting नहीं। Short writes सस्ते होते हैं, क्योंकि प्रत्येक commit WAL में append होता है। इसलिए throughput आपके CPU की तुलना में disk की small-write latency पर अधिक निर्भर करता है। यह अंतर कैसा दिखता है, इसके लिए VPS पर NVMe और SATA SSD storage की तुलना देखें। वास्तविक समस्या long transactions हैं, क्योंकि वे अपने पीछे हर दूसरे writer को queue में लगाते हैं।
Analytical queries। SQLite transactions के लिए बनाया गया row store है। Hundred million rows को scan करने वाला dashboard अलग tool के लिए अलग प्रकार का काम है। server work के लिए DuckDB और SQLite की तुलना बताता है कि यह सीमा कहाँ आती है।
Replication के दौरान VACUUM। पूर्ण VACUUM पूरे database file को फिर से लिखता है। इसका अर्थ है कि Litestream को इसे फिर से पूरा upload करना पड़ता है। Litestream documentation replication सक्रिय रहने के दौरान इसे चलाने से मना करती है। Replicator को stop करें, vacuum चलाएं, फिर उसे दोबारा start करें। इसके बाद fresh full snapshot की अपेक्षा रखें।
एक database पर दो replicators। एक ही database या उसी replica destination पर कभी भी दो Litestream processes न चलाएं। Documentation स्पष्ट रूप से बताता है कि इसे रोकना आपकी जिम्मेदारी है। इसका परिणाम ऐसा replica होता है जिसे आप restore नहीं कर सकते।
Litestream क्या कवर नहीं करता
Litestream केवल database file की सुरक्षा करता है। इसके अलावा किसी और चीज़ की नहीं। Uploaded files, application config, TLS (transport layer security) certificates और unit files का प्रबंधन आपको स्वयं करना होगा। इसे निर्धारित समय-सारणी पर restic का उपयोग करके encrypted off-box backups के साथ जोड़ें, ताकि दोनों हिस्से कवर हो जाएँ। यदि machine नई है, तो new VPS पर पहले दस मिनट में वह user account और firewall का काम कवर किया गया है, जिसे यह guide पहले से पूरा मानती है।
FAQ
क्या production application के लिए SQLite पर्याप्त है?
एक server पर एक application के लिए हाँ, बशर्ते आप WAL mode चालू करें, busy timeout सेट करें और लगातार backup लेते रहें। महत्वपूर्ण सीमाएँ इसकी संरचना से जुड़ी हैं: एक समय में केवल एक writer और केवल एक host machine। इन सीमाओं के भीतर आने वाली application को बिना network hop और monitor करने के लिए किसी अलग process के बिना database मिलता है। जो application इन सीमाओं के भीतर नहीं आती, उसे client-server database चाहिए। कोई भी tuning इसे नहीं बदल सकती।
busy_timeout सेट करने के बाद भी मुझे database is locked क्यों मिलता है?
क्योंकि प्रतीक्षा करने से deadlock हो सकता है, इसलिए SQLite busy handler को छोड़ देता है। bare BEGIN से शुरू होने वाला transaction deferred होता है: शुरुआती SELECT उसे read transaction में डालता है और बाद में होने वाले write को upgrade करना पड़ता है। यदि इस बीच किसी अन्य connection ने write किया हो, तो आपका read snapshot पहले ही stale हो चुका होता है। इसलिए SQLite आपके busy handler को call करने के बजाय तुरंत SQLITE_BUSY लौटाता है। जो transaction write करेगा, उसे BEGIN IMMEDIATE से शुरू करें। इससे write lock पहले ही ले लिया जाता है और timeout लागू होता है।
क्या मैं अपना SQLite database network storage पर रख सकता हूँ?
NFS या SMB जैसे network filesystem पर नहीं। WAL mode में सभी processes को -shm file के माध्यम से shared memory का उपयोग करना पड़ता है। SQLite documentation के अनुसार database का उपयोग करने वाला प्रत्येक process उसी host computer पर होना चाहिए। आपके provider से जुड़ा network block device अलग होता है। Linux उसे सामान्य filesystem वाली सामान्य disk के रूप में देखता है और SQLite वहाँ काम करता है।
यदि मैं nightly backups पहले से चलाता हूँ, तो क्या मुझे Litestream की आवश्यकता है?
यह इस बात पर निर्भर करता है कि आप कितना data खो सकते हैं। Nightly job का अर्थ है कि अधिकतम twenty-four hours के writes खो सकते हैं। Litestream लगभग once a second sync करता है, इसलिए crash में लगभग last second का data खोता है। यह cp से database file copy करने से भी सुरक्षित है, क्योंकि इससे database write के बीच में capture हो सकता है। Litestream केवल database को cover करता है। इसलिए इसके साथ सामान्य file backup भी चलाते रहें।