SSD Nodes Learn Hosting plans →
Panduan Matt ConnorOleh Matt Connor · Diperbarui 2026-10-24

SQLite di VPS: Tepat untuk Produksi atau Tidak?

Pelajari kapan SQLite cocok untuk produksi di satu VPS, cara memakai WAL dan busy_timeout, replikasi Litestream, serta batas satu penulis dan satu mesin.

Verified Every command ran end-to-end on a fresh Ubuntu 24.04 server, July 30, 2026.

Kapan SQLite tepat digunakan sebagai database produksi pada 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 perlu diawasi, tidak ada port yang perlu dilindungi firewall, tidak ada password yang perlu dirotasi, dan tidak ada mesin kedua yang harus tetap aktif. Query merupakan pemanggilan fungsi, bukan perjalanan pulang-pergi melalui jaringan. Jadi, halaman yang menjalankan empat puluh query memerlukan empat puluh pemanggilan fungsi.

Keterbatasannya spesifik dan nyata. SQLite hanya mengizinkan satu writer pada satu waktu di seluruh file database, dan file tersebut tidak dapat digunakan bersama oleh dua mesin. Kedua batasan ini tidak menjadi masalah bagi satu VPS yang menjalankan satu aplikasi. Namun, keduanya menjadi penghambat serius segera setelah arsitektur Anda berkembang melampaui pola tersebut. Panduan ini membahas pengaturan yang membuat SQLite aman di server, pencadangan berkelanjutan dengan Litestream, serta kondisi ketika Anda harus berhenti menggunakan SQLite.

Instal alat command line terlebih dahulu. Semua perintah di bawah ini dijalankan pada Ubuntu 24.04.

sudo apt update
sudo apt install -y sqlite3
sqlite3 --version

Perintah tersebut menampilkan versi yang diawali dengan 3., kemudian tanggal build dan hash source. Ubuntu 24.04 menyediakan SQLite 3.45.1 per Oktober 2026. Aplikasi Anda kemungkinan tidak menggunakan binary ini. Sebagian besar runtime bahasa pemrograman menyertakan salinan library SQLite mereka sendiri, yang sering kali lebih baru. Karena itu, periksa versi yang dilaporkan oleh database driver Anda sebelum mengandalkan fitur terbaru.

Mengapa mode WAL menjadi perubahan pertama yang perlu dilakukan

Secara default, SQLite menggunakan rollback journal. Sebelum mengubah sebuah halaman, SQLite menyalin halaman asli ke file -journal, lalu mengedit database secara langsung. Agar proses ini aman, SQLite mengambil exclusive lock pada seluruh file. Akibatnya, semua pembaca harus menunggu selama ada proses penulisan. Pada laptop, hal ini biasanya tidak terlihat. Pada web server, satu proses penulisan yang lambat dapat menunda setiap request yang mengakses database.

Mode WAL (write-ahead log) membalik urutan tersebut. Writer menambahkan halaman baru ke file -wal terpisah dan membiarkan database utama tetap utuh. Reader tetap membaca file utama berdasarkan snapshot saat pembacaan dimulai. Dengan demikian, reader tidak memblokir writer, dan writer tidak memblokir reader. 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 mode tersebut tetap aktif

mkdir -p ~/app
sqlite3 ~/app/app.db "PRAGMA journal_mode=WAL;"

Perintah tersebut mencetak wal. Output itu bukan sekadar hiasan. PRAGMA journal_mode mengembalikan mode yang benar-benar digunakan database, sehingga respons delete berarti perubahan gagal dan database masih menggunakan rollback journal.

Mode WAL bersifat persisten. Mode ini disimpan sebagai flag pada header database, bukan sebagai pengaturan koneksi. Karena itu, Anda cukup menjalankannya sekali untuk setiap file database. Semua koneksi berikutnya akan mewarisi mode tersebut, termasuk setelah reboot. Buktikan dengan koneksi baru.

sqlite3 ~/app/app.db "PRAGMA journal_mode;"

Sekarang buat sebuah 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 semuanya mengetahui isi WAL. Keduanya merupakan bagian dari database, bukan file sementara. Jika Anda hanya menyalin app.db saat aplikasi masih berjalan, Anda akan mendapatkan file yang tidak berisi commit terbaru. Jika Anda menghapus app.db dan membiarkan dua file lainnya tetap ada, SQLite akan menerapkan halaman WAL yang sudah usang tersebut ke file baru apa pun yang muncul dengan nama itu. Inilah cara database baru dapat rusak saat seseorang mencoba mereset database.

Pengaturan koneksi yang dibutuhkan setiap aplikasi produksi

Hanya journal_mode yang disimpan dalam database. Semua pengaturan lain 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 kembali selama maksimal 5000 milidetik saat database terkunci sebelum mengembalikan database is locked. Nilai default-nya adalah 0. Artinya, secara default, SQLite langsung gagal saat dua operasi tulis pertama kali berjalan bersamaan. Menetapkan satu nilai ini menghilangkan sebagian besar error penguncian yang sering dianggap sebagai masalah SQLite itu sendiri.

synchronous = NORMAL adalah pengaturan yang tepat dalam mode WAL, dan konsekuensinya perlu dipahami. Pada FULL, SQLite memanggil fsync pada WAL setiap kali commit. Pada NORMAL, SQLite melakukan sinkronisasi saat checkpoint. Dokumentasi SQLite menjelaskan dengan tegas hal yang dikorbankan: transaksi tidak lagi tahan terhadap kegagalan daya atau hard reset. Database tidak akan rusak akibat kehilangan daya tersebut. Anda hanya kehilangan commit terakhir yang belum tersimpan ke disk. Pada VPS, ini biasanya merupakan kompromi yang tepat karena fsync dihapus dari jalur setiap operasi tulis.

foreign_keys = ON dinonaktifkan secara default demi kompatibilitas dengan versi sebelumnya, dan pengaturan ini berlaku per koneksi. Skema yang penuh dengan klausa REFERENCES sama sekali tidak menegakkan aturan sampai setiap koneksi mengaktifkan pengaturan ini.

Ada satu pengaturan lain yang baru penting pada tahap berikutnya. SQLite secara otomatis menjalankan checkpoint setelah WAL berukuran lebih dari 1000 halaman. Proses tersebut dijalankan oleh koneksi mana pun yang kebetulan menyelesaikan transaksi pada saat itu. Hal ini tidak bermasalah dengan sendirinya. Namun, situasinya perlu dipertimbangkan saat Litestream berjalan karena Litestream ingin mengendalikan waktu pelaksanaan checkpoint.

Mengapa database is locked masih terjadi setelah Anda menetapkan busy_timeout

Inilah kegagalan yang membuat orang kembali menggunakan Postgres, dan penyebabnya spesifik.

Batas waktu busy memasang busy handler, tetapi SQLite tidak menjamin akan memanggilnya.

Jika SQLite menentukan bahwa pemanggilan busy handler dapat menyebabkan deadlock, SQLite akan langsung mengembalikan SQLITE_BUSY ke aplikasi, bukan memanggil busy handler.

Deadlock yang dihindari terjadi saat transaksi mengalami peningkatan level. BEGIN tanpa klausa tambahan di SQLite berarti BEGIN DEFERRED. Jika pernyataan pertama setelahnya adalah SELECT, Anda berada dalam transaksi baca. Saat UPDATE berikutnya dalam transaksi yang sama perlu berubah 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 menyebabkan kedua koneksi saling menunggu hingga deadlock. 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 digunakan. Error langsung muncul, sehingga tampak seolah-olah pengaturan tersebut tidak berpengaruh.

Perbaikannya hanya memerlukan satu kata.

BEGIN IMMEDIATE;
UPDATE notes SET body = 'edited' WHERE id = 1;
COMMIT;

BEGIN IMMEDIATE mengambil write lock pada awal transaksi, sebelum membaca apa pun. Tidak ada peningkatan level, sehingga tidak ada deadlock yang perlu dihindari. Karena itu, busy handler akan diterapkan dan koneksi menunggu gilirannya, bukan gagal. Biarkan transaksi hanya-baca tetap menggunakan mode deferred. Setiap transaksi yang berisi penulisan harus menggunakan mode immediate.

Penyebab kedua error lock 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 setiap penulis lain selama pemanggilan tersebut berlangsung. Baca data yang diperlukan, tutup transaksi, lakukan pekerjaan yang lambat, lalu buka transaksi tulis singkat untuk menyimpan hasilnya.

Pencadangan kontinu dengan Litestream

Salinan malam hari dapat kehilangan penulisan hingga satu hari, sedangkan menjalankan cp pada 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 pada database yang sedang digunakan. Litestream melangkah lebih jauh: Litestream memantau WAL dan terus-menerus mengirimkan perubahan ke penyimpanan objek. Dengan demikian, potensi kehilangan data terburuk berkurang dari satu hari menjadi sekitar satu detik.

Litestream adalah satu biner Go yang berjalan di samping aplikasi. Litestream tidak berada di antara aplikasi dan database. Aplikasi tetap menulis ke SQLite seperti sebelumnya, 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 version

v0.5.14 masih merupakan rilis yang didokumentasikan pada halaman instalasi Linux resmi per Oktober 2026. Setelah itu, tersedia rilis yang lebih baru: v0.5.15 pada 21 July 2026 dan v0.5.17, tag terbaru, pada 31 August 2026. Ubah versi pada kedua baris agar sesuai dengan tag saat ini pada halaman releases. Jika VPS Anda menggunakan arm64, gunakan paket arm64 yang sesuai.

File konfigurasi berada di /etc/litestream.yml. Mulailah dengan replica file lokal. Cara ini membuktikan seluruh alur tanpa memerlukan kredensial cloud.

dbs:
  - path: /home/appuser/app/app.db
    replica:
      type: file
      path: /var/backups/litestream/app

Perhatikan bahwa field tersebut adalah replica, dalam bentuk tunggal. Litestream 0.5 mengganti array replicas dari seri 0.3 dengan satu blok replica. Konfigurasi yang memuat 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 dalam hasil pencarian. Seri 0.5 juga mengganti nama subcommand litestream wal menjadi litestream ltx karena format backup pada disk berubah.

Periksa apakah konfigurasi dapat diparse sebelum mengaktifkan apa pun.

sudo litestream databases -config /etc/litestream.yml

Kemudian buktikan alur pulang-pergi 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/app

Perintah tersebut berjalan di foreground dan terus aktif. Pada 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 tersinkronisasi: Litestream mengirimkan perubahan berdasarkan sync-interval yang secara default bernilai 1 detik. Tunggu, lalu lakukan restore lagi. Satu detik tersebut juga merupakan titik pemulihan 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: 24h

Jangan menyimpan kredensial dalam file tersebut. Litestream membaca LITESTREAM_ACCESS_KEY_ID dan LITESTREAM_SECRET_ACCESS_KEY dari environment. Karena itu, simpan keduanya 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. Jadi, retention juga menentukan seberapa jauh ke masa lalu Anda dapat melakukan restore. Dua puluh empat jam berarti migrasi yang bermasalah dan baru diketahui pada Rabu pagi sudah tidak dapat dipulihkan dari kondisi Senin. Dalam Litestream 0.5, retention merupakan pengaturan global dalam blok root snapshot, bukan pengaturan per replica. Untuk menyimpan data selama satu minggu, ubah blok tersebut menjadi retention: 168h dan sediakan ruang penyimpanan tambahan. Baris retention: di dalam blok replica adalah sintaks seri 0.3. Litestream 0.5 mengabaikannya tanpa error dan tetap menggunakan default 24 jam.

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 path database, litestream restore mencari replika yang sesuai di /etc/litestream.yml lalu mengunduhnya. PRAGMA integrity_check menampilkan ok pada file yang sehat. Output lainnya berarti salinan hasil pemulihan tidak dapat digunakan. Jalankan proses ini secara terjadwal dengan service dan timer systemd, lalu baca outputnya. Sebelum Anda memulihkan backup setidaknya sekali, Anda belum tahu apakah proses tersebut berfungsi.

Jalankan 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 -f

Output yang normal mencantumkan setiap database dari konfigurasi, lalu tetap hening selain baris sinkronisasi berkala. Error no such file or directory pada path database Anda berarti path dalam konfigurasi salah, atau proses tidak dapat membacanya. Secara default, unit berjalan sebagai root, yang memberikan hak akses lebih besar daripada yang dibutuhkan tugas ini. Litestream harus dapat membaca dan menulis database serta direktori tempat database tersebut berada, karena Litestream menggunakan file -wal dan -shm yang berada di sebelah database Anda. Karena itu, jalankan Litestream sebagai akun pengguna yang sudah digunakan aplikasi Anda, yaitu appuser dalam contoh ini.

# /etc/systemd/system/litestream.service.d/override.conf
[Service]
User=appuser
Group=appuser

Terapkan konfigurasi tersebut dengan sudo systemctl daemon-reload dan sudo systemctl restart litestream. Menyiapkan akun service khusus dengan hak akses minimum hanya memerlukan beberapa menit. Langkah ini membedakan agen pencadangan dari proses root kedua pada server.

Urutan proses juga penting jika Anda membangun ulang mesin dari awal. Database harus dipulihkan sebelum aplikasi dijalankan. litestream restore menerima -if-db-not-exists, yang mengembalikan kode keluar 0 jika file sudah ada, sehingga aman dijalankan pada setiap boot. Tambahkan perintah tersebut ke baris ExecStartPre pada unit aplikasi Anda. Dengan demikian, VPS baru akan mengunduh database, sedangkan VPS yang sudah memiliki database tidak melakukan apa-apa. litestream replicate memiliki flag -restore-if-db-not-exists yang sesuai jika Anda ingin menyimpan pengaturan tersebut di satu tempat.

Batasan SQLite pada VPS

Sistem file jaringan. Ini adalah batasan yang tidak dapat diatasi melalui konfigurasi. Mode WAL mengharuskan setiap proses yang menggunakan database berbagi area memori kecil. Area tersebut disediakan oleh file -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 file jaringan.

Database pada NFS (network file system) yang di-mount atau share SMB dapat rusak, dan tidak ada pragma yang dapat mencegahnya. Ada perbedaan yang sering terlewatkan. Perangkat block jaringan, yang digunakan sebagian besar penyedia VPS sebagai penyimpanan tambahan, terlihat oleh Linux sebagai disk biasa dengan sistem file biasa. Konfigurasi tersebut aman. Sebaliknya, file share yang di-mount tidak aman untuk digunakan SQLite.

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. Rencanakan perpindahan tersebut selagi masih ada waktu.

Beban kerja dengan banyak operasi tulis. Hanya satu proses yang dapat menulis pada satu waktu. Ini adalah sifat format file, bukan parameter yang dapat disetel. Operasi tulis singkat tidak mahal karena setiap commit ditambahkan ke WAL. Karena itu, throughput lebih bergantung pada latensi operasi tulis kecil pada disk daripada CPU. Lihat NVMe dibandingkan penyimpanan SATA SSD pada VPS untuk melihat perbedaannya. Transaksi panjang adalah masalah sebenarnya karena semua proses penulis lain harus menunggu di belakangnya.

Kueri analitis. SQLite adalah row store yang dibuat untuk transaksi. Dashboard yang memindai seratus juta baris adalah pekerjaan berbeda untuk alat yang berbeda. DuckDB dibandingkan dengan SQLite untuk pekerjaan server membahas batas penggunaan keduanya.

VACUUM saat replikasi aktif. VACUUM secara penuh menulis ulang seluruh file database. Tindakan ini membatalkan pelacakan tingkat halaman yang diandalkan Litestream. Dokumentasi Litestream menyarankan agar tindakan tersebut tidak dijalankan di tempat saat replikasi aktif. Dokumentasi tersebut merekomendasikan VACUUM INTO, yang menulis salinan hasil pemadatan ke file terpisah dan membiarkan file asli tetap utuh. Jika Anda harus menjalankan vacuum di tempat, prosedur yang didokumentasikan lebih panjang daripada sekadar melakukan restart: hentikan replicator, jalankan vacuum, hapus direktori pelacakan tersembunyi Litestream di sebelah database serta data replica remote yang ada, lalu jalankan kembali replicator agar memulai dari snapshot penuh baru. Langkah terakhir tersebut menghapus riwayat pemulihan yang sebelumnya tersedia.

Dua replicator pada satu database. Jangan pernah menjalankan dua proses Litestream pada database yang sama atau tujuan replica yang sama. Dokumentasi menyatakan dengan jelas bahwa Anda bertanggung jawab mencegah kondisi ini. Jika tidak, replica yang dihasilkan tidak dapat dipulihkan.

Hal yang tidak ditangani Litestream

Litestream melindungi file database dan tidak menangani hal lain. File yang diunggah, konfigurasi aplikasi, sertifikat TLS (transport layer security), dan file unit tetap harus Anda tangani sendiri. Gunakan bersama backup terenkripsi di luar server menggunakan restic secara terjadwal agar kedua bagian tersebut terlindungi. Jika mesin masih baru, sepuluh menit pertama pada VPS baru membahas 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 ada satu writer 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 harus dipantau. Aplikasi yang melampaui batas tersebut memerlukan database client-server, dan tuning apa pun tidak akan mengubahnya.

Mengapa saya masih mendapatkan database is locked setelah menetapkan busy_timeout?

Karena SQLite melewati busy handler ketika penantian dapat menyebabkan deadlock. Transaksi yang dimulai dengan BEGIN tanpa klausa lain bersifat deferred: SELECT pembuka menempatkannya dalam transaksi baca, lalu penulisan berikutnya harus meningkatkan tingkat transaksi. Jika koneksi lain melakukan penulisan di antaranya, SQLite segera mengembalikan SQLITE_BUSY, bukan memanggil busy handler Anda, karena snapshot baca Anda sudah kedaluwarsa. Mulai setiap transaksi yang akan melakukan penulisan dengan BEGIN IMMEDIATE agar write lock diambil 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. Network block device yang dipasang oleh provider Anda merupakan hal yang berbeda: Linux melihatnya sebagai disk biasa dengan filesystem biasa, dan SQLite dapat bekerja di sana.

Apakah saya memerlukan Litestream jika sudah menjalankan pencadangan setiap malam?

Hal ini bergantung pada seberapa banyak 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 hanya menyebabkan hilangnya data sekitar 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 berdampingan.

Apakah artikel ini bermanfaat?

#sqlite#wal#litestream#backups#production