SQLite di VPS untuk Produksi: Kapan Tepat?
SQLite cocok untuk banyak aplikasi kecil di satu VPS. Pelajari WAL, busy_timeout, replikasi Litestream, serta batas satu penulis dan satu mesin.
Kapan SQLite menjadi database produksi yang tepat di VPS
Menjalankan SQLite di lingkungan produksi pada VPS merupakan pilihan yang tepat untuk sebagian besar aplikasi kecil. Alasannya sederhana: satu proses pada satu mesin yang menulis ke satu file tidak memerlukan server database. Tidak ada daemon yang harus diawasi, port yang harus dilindungi firewall, kata sandi yang harus dirotasi, atau mesin kedua yang harus tetap aktif. Kueri merupakan pemanggilan fungsi, bukan perjalanan pulang-pergi melalui jaringan. Jadi, halaman yang menjalankan empat puluh kueri memerlukan empat puluh pemanggilan fungsi.
Batasannya spesifik dan nyata. SQLite hanya mengizinkan satu penulis pada satu waktu untuk seluruh file database. File tersebut juga tidak dapat dibagikan oleh dua mesin. Kedua batasan ini tidak menjadi masalah untuk satu VPS yang menjalankan satu aplikasi. Namun, keduanya menjadi penghambat saat Anda melampaui bentuk penggunaan tersebut. Panduan ini membahas pengaturan yang membuat SQLite aman di server, pencadangan berkelanjutan dengan Litestream, dan titik saat Anda harus berhenti menggunakan SQLite.
Instal alat baris perintah terlebih dahulu. Semua langkah di bawah ini dijalankan pada Ubuntu 24.04.
sudo apt update
sudo apt install -y sqlite3
sqlite3 --versionPerintah tersebut mencetak versi yang diawali dengan 3., lalu tanggal build dan hash sumber. Ubuntu 24.04 menyertakan SQLite 3.45.1 per Juli 2026. Aplikasi Anda mungkin tidak menggunakan binary ini. Sebagian besar runtime bahasa memaketkan salinan library SQLite mereka sendiri, yang sering kali lebih baru. Karena itu, periksa versi yang dilaporkan oleh driver database Anda sebelum mengandalkan fitur terbaru.
Mengapa mode WAL adalah hal pertama yang Anda ubah
Secara default, SQLite menggunakan jurnal rollback. Sebelum mengubah sebuah halaman, SQLite menyalin halaman asli ke file -journal, lalu mengedit database secara langsung. Agar proses ini aman, SQLite mengambil kunci eksklusif pada seluruh file. Akibatnya, setiap pembaca harus menunggu selama ada operasi tulis. Pada laptop, hal ini biasanya tidak terlihat. Pada server web, satu operasi tulis yang lambat dapat menahan setiap permintaan yang mengakses database.
Mode WAL (write-ahead log) membalik urutan tersebut. Penulis menambahkan halaman baru ke file -wal terpisah dan membiarkan database utama tetap utuh. Pembaca terus membaca file utama berdasarkan snapshot saat pembacaan dimulai. Dengan demikian, pembaca tidak memblokir penulis, dan penulis tidak memblokir pembaca. Selanjutnya, checkpoint menyalin halaman WAL yang telah terkumpul kembali ke database utama. Perubahan tunggal ini merupakan faktor utama yang membuat SQLite dapat digunakan di balik aplikasi web.
Aktifkan mode WAL dan pastikan pengaturan tetap berlaku
mkdir -p ~/app
sqlite3 ~/app/app.db "PRAGMA journal_mode=WAL;"Perintah tersebut mencetak wal. Output itu bukan sekadar hiasan. PRAGMA journal_mode menampilkan mode yang benar-benar digunakan database, sehingga respons delete berarti perubahan gagal dan database masih menggunakan rollback journal.
Mode WAL bersifat persisten. Mode ini merupakan flag pada header database, bukan pengaturan koneksi. Karena itu, Anda cukup menjalankan perintah tersebut sekali untuk setiap file database, dan semua koneksi setelahnya akan mewarisi pengaturan itu, termasuk setelah reboot. Buktikan dengan koneksi baru.
sqlite3 ~/app/app.db "PRAGMA journal_mode;"Sekarang buat tabel dan periksa file yang muncul di 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/Sekarang ada tiga file: app.db, app.db-wal, dan app.db-shm. File -wal berisi halaman yang sudah di-commit tetapi belum menjalani checkpoint. File -shm adalah indeks shared memory yang dipetakan oleh setiap koneksi agar semua koneksi memiliki informasi yang sama tentang isi WAL. Keduanya merupakan bagian dari database, bukan file sementara. Jika Anda hanya menyalin app.db saat aplikasi sedang berjalan, file hasil salinan tersebut tidak akan berisi commit terbaru. Jika Anda menghapus app.db dan membiarkan dua file lainnya tetap ada, SQLite akan menerapkan halaman WAL lama itu ke file baru apa pun yang muncul dengan nama tersebut. Inilah cara database baru dapat rusak saat seseorang mencoba mereset database.
Setelan koneksi yang diperlukan setiap aplikasi produksi
Hanya journal_mode yang disimpan dalam database. Semua setelan lainnya di bawah ini berlaku per koneksi. Artinya, aplikasi Anda harus menjalankannya pada setiap koneksi yang dibuka, termasuk setiap koneksi yang dibuat pool di latar belakang.
PRAGMA journal_mode = WAL;
PRAGMA busy_timeout = 5000;
PRAGMA synchronous = NORMAL;
PRAGMA foreign_keys = ON;busy_timeout = 5000 memberi tahu SQLite untuk terus mencoba selama paling lama 5000 milidetik ketika database terkunci sebelum mengembalikan database is locked. Nilai default-nya adalah 0. Jadi, secara default, SQLite langsung gagal saat dua operasi tulis pertama kali berlangsung bersamaan. Menetapkan satu nilai ini menghilangkan sebagian besar error penguncian yang sering disalahkan pada SQLite.
synchronous = NORMAL adalah setelan yang tepat dalam mode WAL, dan komprominya perlu dipahami. Pada FULL, SQLite memanggil fsync pada WAL setiap kali commit. Pada NORMAL, SQLite melakukan sinkronisasi saat checkpoint. Dokumentasi SQLite menjelaskan dengan tegas konsekuensi yang Anda terima: transaksi tidak lagi tahan terhadap kehilangan daya atau reset paksa. Database tidak akan rusak akibat kehilangan daya tersebut. Anda hanya kehilangan commit terakhir yang belum tersimpan ke disk. Pada VPS, kompromi ini biasanya tepat karena menghapus satu pemanggilan fsync dari jalur setiap operasi tulis.
foreign_keys = ON secara default dinonaktifkan untuk menjaga kompatibilitas mundur, dan setelan ini berlaku per koneksi. Skema yang penuh dengan klausa REFERENCES tidak menegakkan apa pun sampai setiap koneksi mengaktifkan setelan ini.
Ada satu setelan lain yang baru penting kemudian. SQLite secara otomatis menjalankan checkpoint setelah WAL melebihi 1000 halaman. Pekerjaan ini dilakukan oleh koneksi mana pun yang kebetulan menyelesaikan transaksi pada saat itu. Hal tersebut tidak bermasalah dengan sendirinya. Namun, ini menjadi pertanyaan ketika Litestream berjalan karena Litestream ingin mengendalikan kapan checkpoint dijalankan.
Mengapa database is locked masih terjadi setelah Anda menetapkan busy_timeout
Ini adalah kegagalan yang membuat orang kembali ke Postgres, dan penyebabnya spesifik.
Batas waktu sibuk memasang busy handler, tetapi SQLite tidak menjamin bahwa handler tersebut akan dipanggil.
Jika SQLite menentukan bahwa pemanggilan busy handler dapat menyebabkan deadlock, SQLite akan langsung mengembalikan SQLITE_BUSY kepada aplikasi, bukan memanggil busy handler.
Deadlock yang dihindari terjadi saat transaksi ditingkatkan. BEGIN tanpa klausa tambahan di SQLite berarti BEGIN DEFERRED. Jika pernyataan pertama setelahnya adalah SELECT, Anda berada dalam transaksi baca. Jika UPDATE berikutnya dalam transaksi yang sama perlu diubah menjadi transaksi tulis, dan koneksi lain telah melakukan penulisan sejak pembacaan Anda dimulai, SQLite tidak dapat membuat Anda menunggu. Snapshot Anda sudah kedaluwarsa, dan menunggu hanya akan membuat kedua koneksi mengalami deadlock satu sama lain. Dokumentasi menyatakan hasilnya secara langsung:
Pernyataan tulis berikutnya akan meningkatkan transaksi menjadi transaksi tulis jika memungkinkan, atau mengembalikan SQLITE_BUSY.
Batas waktu 5000 milidetik Anda tidak pernah diperiksa. Kesalahan muncul segera, sehingga pengaturan tersebut tampak tidak berfungsi.
Perbaikannya hanya memerlukan satu kata.
BEGIN IMMEDIATE;
UPDATE notes SET body = 'edited' WHERE id = 1;
COMMIT;BEGIN IMMEDIATE mengambil kunci tulis di awal, sebelum membaca apa pun. Tidak ada peningkatan transaksi, sehingga tidak ada deadlock yang perlu dihindari. Karena itu, busy handler dapat diterapkan dan koneksi menunggu gilirannya, bukan gagal. Biarkan transaksi hanya-baca tetap ditangguhkan. Setiap transaksi yang berisi penulisan harus bersifat segera.
Penyebab kedua kesalahan kunci lebih sulit ditemukan: membiarkan transaksi tulis tetap terbuka selama pekerjaan yang lambat. SQLite membuat penulis berjalan secara serial, sehingga transaksi yang dibuka, memanggil API eksternal melalui jaringan, lalu melakukan commit akan memblokir semua penulis lain selama panggilan tersebut berlangsung. Baca data yang diperlukan, tutup transaksi, lakukan pekerjaan yang lambat, lalu buka transaksi tulis singkat untuk menyimpan hasilnya.
Pencadangan berkelanjutan dengan Litestream
Salinan setiap malam dapat kehilangan penulisan hingga satu hari, dan menjalankan cp terhadap database SQLite yang sedang digunakan dapat menghasilkan salinan yang tidak dapat dibuka. Ada dua cara yang aman. sqlite3 app.db ".backup /path/to/backup.db" menggunakan antarmuka pencadangan online SQLite dan dapat bekerja terhadap database yang sedang digunakan. Litestream bekerja lebih jauh: Litestream memantau WAL dan mengirimkan perubahan ke penyimpanan objek secara berkelanjutan. Dengan demikian, kehilangan data terburuk berkurang dari satu hari menjadi sekitar satu detik.
Litestream adalah satu binary Go yang berjalan di samping aplikasi Anda. Litestream tidak berada di antara aplikasi dan database. Aplikasi Anda menulis ke SQLite seperti biasa, sedangkan Litestream membaca WAL dan mengunggah perubahan.
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 versionv0.5.14 adalah rilis yang didokumentasikan pada halaman instalasi Linux resmi per Juli 2026, dan v0.5.15 dirilis pada 21 July 2026. Ubah versi pada kedua baris agar sesuai dengan tag saat ini di halaman releases. Gunakan paket arm64 yang sesuai jika VPS Anda menggunakan arm64.
File konfigurasi berada di /etc/litestream.yml. Mulailah dengan replica lokal. Cara ini membuktikan seluruh alur tanpa memerlukan kredensial cloud.
dbs:
- path: /home/appuser/app/app.db
replica:
type: file
path: /var/backups/litestream/appPerhatikan bahwa nama field tersebut adalah replica, dalam bentuk tunggal. Litestream 0.5 mengganti array replicas dari seri 0.3 dengan satu blok replica. Konfigurasi yang berisi dua entri sekarang gagal saat startup. Banyak panduan pihak ketiga masih menampilkan array lama. Karena itu, salin struktur di atas, bukan contoh pertama yang muncul dari pencarian. Seri 0.5 juga mengganti nama subperintah litestream wal menjadi litestream ltx karena format backup di disk berubah.
Periksa bahwa konfigurasi dapat di-parse sebelum mengaktifkan apa pun.
sudo litestream databases -config /etc/litestream.ymlKemudian buktikan round trip secara manual. Bentuk ini melewati file konfigurasi dan mereplikasi satu database ke satu path.
mkdir -p /tmp/replica
litestream replicate ~/app/app.db file:///tmp/replica/appPerintah tersebut berjalan di foreground dan terus berjalan. Di shell kedua, tulis satu baris, lalu restore replica ke file baru.
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;"Jumlah tersebut mencakup baris baru. Jika tidak, perubahan belum disinkronkan: Litestream melakukan push pada interval sync-interval yang secara default adalah 1 second. Tunggu, lalu lakukan restore lagi. Satu detik tersebut juga merupakan recovery point Anda. Jika terjadi crash, paling banyak penulisan selama interval sinkronisasi terakhir yang hilang. Tidak ada konfigurasi yang dapat membuat kehilangan tersebut menjadi nol.
Untuk penyimpanan sebenarnya, ganti blok replica dengan URL S3. Cara ini dapat digunakan dengan Amazon S3 dan penyimpanan objek yang kompatibel dengan S3 dari provider lain.
dbs:
- path: /home/appuser/app/app.db
replica:
url: s3://your-bucket-name/app
region: us-east-1
snapshot:
interval: 24h
retention: 24hJangan menyimpan kredensial di file tersebut. Litestream membaca LITESTREAM_ACCESS_KEY_ID dan LITESTREAM_SECRET_ACCESS_KEY dari environment. Karena itu, simpan kredensial tersebut dalam systemd drop-in yang dimiliki root dengan mode 600.
Nilai snapshot di atas adalah nilai default, dan default retention sering mengejutkan pengguna. Retention menentukan berapa lama Litestream menyimpan snapshot dan file yang terkait dengannya. Dengan demikian, retention juga menentukan seberapa jauh ke masa lalu Anda dapat melakukan restore. Dua puluh empat jam berarti migrasi yang bermasalah dan baru Anda sadari pada Rabu pagi sudah tidak dapat dipulihkan dari keadaan hari Senin. Tetapkan retention: 168h selama satu minggu dan tanggung biaya penyimpanan tambahan.
Buktikan pemulihan sebelum Anda membutuhkannya
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;"Dengan menggunakan path database, litestream restore mencari replica yang sesuai di /etc/litestream.yml lalu mengunduhnya. PRAGMA integrity_check menampilkan ok pada file yang sehat. Output lain apa pun berarti salinan hasil pemulihan tidak dapat digunakan. Jalankan proses ini secara berkala menggunakan service dan timer systemd, lalu baca outputnya. Sebelum Anda memulihkan backup setidaknya sekali, Anda tidak dapat memastikan bahwa backup tersebut berfungsi.
Menjalankan Litestream dengan systemd
Paket Debian memasang unit litestream yang membaca /etc/litestream.yml.
sudo systemctl enable litestream
sudo systemctl start litestream
sudo journalctl -u litestream -fOutput yang normal mencantumkan setiap database dari konfigurasi, lalu tidak menampilkan apa pun selain baris sinkronisasi berkala. Kesalahan no such file or directory pada path database berarti path dalam konfigurasi salah, atau proses tidak dapat membacanya. Secara default, unit berjalan sebagai root, sehingga memberikan hak istimewa yang lebih besar daripada yang dibutuhkan tugas ini. Litestream harus dapat membaca dan menulis database serta direktori yang memuatnya, karena Litestream menggunakan file -wal dan -shm di samping database. Oleh karena itu, gunakan akun yang sudah dipakai aplikasi Anda.
# /etc/systemd/system/litestream.service.d/override.conf
[Service]
User=appuser
Group=appuserTerapkan konfigurasi tersebut dengan sudo systemctl daemon-reload dan sudo systemctl restart litestream. Menyiapkan akun layanan khusus dengan hak istimewa minimum hanya memerlukan beberapa menit. Langkah ini membedakan agen pencadangan dari proses root kedua pada server.
Ada satu hal terkait urutan yang penting jika Anda membangun ulang mesin dari awal. Database harus dipulihkan sebelum aplikasi dimulai. litestream restore menerima -if-db-not-exists, yang keluar dengan kode 0 jika file tersebut sudah ada. Dengan demikian, perintah ini aman dijalankan pada setiap boot. Letakkan perintah tersebut dalam baris ExecStartPre pada unit aplikasi Anda. VPS baru akan mengunduh database, sedangkan VPS yang sudah memiliki database tidak melakukan apa pun. litestream replicate memiliki flag -restore-if-db-not-exists yang sesuai jika Anda ingin menyimpan konfigurasi ini di satu tempat.
Di mana SQLite tidak cocok pada VPS
Sistem berkas jaringan. Ini adalah batasan yang tidak dapat diatasi melalui konfigurasi. Mode WAL mengharuskan setiap proses yang menggunakan database berbagi area kecil dalam memori. Area tersebut disediakan oleh berkas -shm. Dokumentasi SQLite menyatakan aturan ini tanpa pengecualian:
Semua proses yang menggunakan database harus berada pada komputer host yang sama; WAL tidak berfungsi melalui sistem berkas jaringan.
Database pada NFS ter-mount (sistem berkas jaringan) atau share SMB dapat rusak, dan tidak ada pragma yang dapat mencegahnya. Ada perbedaan yang sering terlewatkan. Perangkat block jaringan, yang biasanya dipasang oleh penyedia VPS sebagai penyimpanan tambahan, tampil di Linux sebagai disk biasa dengan sistem berkas biasa. Kondisi itu tidak bermasalah. File share yang di-mount berbeda.
Server aplikasi kedua. Tidak ada pengaturan yang dapat membuat skenario ini berfungsi. Jika dua mesin perlu menyajikan data yang sama, Anda memerlukan database yang berkomunikasi melalui jaringan. Tentukan perpindahan tersebut selagi masih ada waktu untuk merencanakannya.
Beban kerja yang banyak melakukan penulisan. Satu penulis pada satu waktu merupakan karakteristik format berkas, bukan parameter yang dapat disesuaikan. Penulisan singkat tidak mahal karena setiap commit ditambahkan ke WAL. Karena itu, throughput lebih dipengaruhi latensi penulisan kecil pada disk daripada CPU. Lihat NVMe dibandingkan penyimpanan SATA SSD pada VPS untuk mengetahui perbedaan tersebut. Masalah sebenarnya adalah transaksi yang panjang karena transaksi tersebut mengantrekan semua penulis lainnya.
Kueri analitis. SQLite adalah row store yang dibuat untuk transaksi. Dashboard yang memindai seratus juta baris merupakan pekerjaan berbeda untuk alat yang berbeda. DuckDB dibandingkan dengan SQLite untuk pekerjaan server membahas batas perbedaannya.
VACUUM selama replikasi. VACUUM penuh menulis ulang seluruh berkas database. Artinya, Litestream harus mengunggahnya kembali secara keseluruhan. Dokumentasi Litestream menyarankan agar operasi ini tidak dijalankan saat replikasi aktif. Hentikan replicator, jalankan vacuum, mulai kembali, dan perkirakan snapshot penuh yang baru.
Dua replicator pada satu database. Jangan pernah menjalankan dua proses Litestream pada database yang sama atau tujuan replica yang sama. Dokumentasi menyatakan secara jelas bahwa pencegahan kondisi ini merupakan tanggung jawab Anda. Jika kondisi tersebut terjadi, replica tidak dapat dipulihkan.
Hal yang tidak dicakup Litestream
Litestream hanya melindungi file database. Litestream tidak melindungi hal lainnya. File yang diunggah, konfigurasi aplikasi, sertifikat TLS (transport layer security), dan file unit tetap harus Anda tangani sendiri. Padukan Litestream dengan backup terenkripsi di luar mesin menggunakan restic secara terjadwal agar kedua aspek tersebut tercakup. Jika mesin masih baru, sepuluh menit pertama pada VPS baru mencakup konfigurasi akun pengguna dan firewall yang diasumsikan sudah selesai dalam panduan ini.
FAQ
Apakah SQLite cukup untuk aplikasi produksi?
Untuk satu aplikasi pada satu server, ya, asalkan Anda mengaktifkan mode WAL, menetapkan batas waktu busy, dan mencadangkannya secara berkelanjutan. Batasan yang penting bersifat struktural: hanya satu penulis pada satu waktu dan hanya satu mesin host. Aplikasi yang masih berada dalam batas tersebut memperoleh database tanpa lompatan jaringan dan tanpa proses terpisah yang perlu dipantau. Aplikasi yang melampaui batas tersebut memerlukan database client-server, dan tidak ada tuning yang dapat mengubahnya.
Mengapa saya masih mendapatkan database is locked setelah menetapkan busy_timeout?
Karena SQLite melewati busy handler ketika menunggu dapat menyebabkan deadlock. Transaksi yang dimulai dengan BEGIN tanpa klausa lain bersifat deferred: SELECT pembuka menempatkannya dalam transaksi baca, dan penulisan berikutnya harus melakukan upgrade. Jika koneksi lain melakukan penulisan di antaranya, SQLite segera mengembalikan SQLITE_BUSY, bukan memanggil busy handler Anda, karena snapshot baca Anda sudah usang. Mulai setiap transaksi yang akan melakukan penulisan dengan BEGIN IMMEDIATE agar kunci penulisan diperoleh sejak awal dan batas waktu berlaku.
Dapatkah saya menyimpan database SQLite pada penyimpanan jaringan?
Tidak pada filesystem jaringan seperti NFS atau SMB. Mode WAL memerlukan semua proses untuk berbagi memori melalui file -shm, dan dokumentasi SQLite menyatakan bahwa setiap proses yang menggunakan database harus berada pada komputer host yang sama. Perangkat blok jaringan yang dipasangkan oleh penyedia layanan Anda adalah hal yang berbeda: Linux melihatnya sebagai disk normal dengan filesystem normal, dan SQLite dapat bekerja di sana.
Apakah saya memerlukan Litestream jika sudah menjalankan pencadangan setiap malam?
Hal ini bergantung pada jumlah data yang dapat Anda tanggung untuk hilang. Pekerjaan pencadangan setiap malam berarti Anda dapat kehilangan penulisan hingga dua puluh empat jam terakhir. Litestream melakukan sinkronisasi sekitar sekali per detik, sehingga kerusakan dapat menyebabkan hilangnya data kira-kira selama satu detik terakhir. Litestream juga lebih aman daripada menyalin file database dengan cp, yang dapat menangkap database saat penulisan sedang berlangsung. Litestream hanya mencakup database, jadi tetap jalankan pencadangan file umum secara bersamaan.