Tahun lalu saya kehilangan folder /var/www di satu VPS. Bukan karena hacker, bukan karena bencana. Saya cuma salah ketik rm -rf dengan path yang kurang satu slash. Tiga hari kerja hilang, dan yang paling menyakitkan: saya punya backup. Backup-nya ada di folder /backup di server yang sama. Jadi ketika file-nya hilang, backup-nya ikut hilang bareng.
Kalau kamu pernah merasa "saya sudah backup kok" tapi belum pernah benar-benar mencoba restore, artikel ini buat kamu. Backup itu bukan tentang punya salinan. Backup itu tentang bisa pulih dalam waktu yang bisa kamu terima. Dua hal itu jauh berbeda, dan banyak developer baru sadar bedanya saat sudah terlambat.
Saya akan tunjukkan strategi 3-2-1 yang jadi standar industri sejak era tape drive, lalu cara implementasinya hari ini pakai dua tool gratis: restic untuk membuat snapshot terenkripsi, dan rclone untuk mendorongnya ke storage offsite. Semua jalan di VPS murah, nggak butuh layanan berbayar.
Kenapa Copy File Biasa Gagal
Cara paling umum orang backup: taruh script rsync di cron, tarik folder penting ke direktori lain. Masalahnya muncul satu per satu.
Pertama, salinan itu mirror, bukan arsip. Kalau kamu rsync dan file sumber sudah korup, salinannya jadi korup juga. Kamu nggak punya versi kemarin. Kedua, nggak terenkripsi. Kalau kamu push ke object storage murah dan kredensialnya bocor, isi database-mu bisa dibaca siapa saja. Ketiga, nggak ada verifikasi. File berhasil dikopi belum tentu bisa dibuka.
Yang paling sering saya temui: backup ditaruh di server yang sama, atau di provider yang sama. Jadi saat akun billing kena suspend, hilang semuanya sekaligus. Inilah kenapa aturan 3-2-1 tetap relevan setelah puluhan tahun:
- 3 salinan data - satu produksi, dua backup
- 2 media berbeda - misalnya disk VPS dan object storage
- 1 offsite - lokasi atau provider yang berbeda
Saya tambahkan satu aturan yang menurut saya lebih penting dari ketiganya: 1 restore test per kuartal. Backup yang belum pernah di-restore statusnya "unknown", bukan "aman".
Sebelum masuk ke perintah, ada satu hal yang perlu saya jelaskan dulu karena sering terlewat: kenapa saya masih memakai cron padahal sekarang ada queue worker, Airflow, dan Temporal. Jawabannya, semua tool itu menyelesaikan masalah yang berbeda. Queue worker mengurus penjadwalan pekerjaan - memastikan job jalan berurutan, punya retry, dan bisa dipantau. Restic mengurus penyimpanan salinan data. Menyelesaikan yang satu tidak otomatis menyelesaikan yang lain.
Yang penting juga: kompleksitas punya biaya. Sistem backup yang terlalu canggih sering berakhir tidak dipakai. Saya pernah membangun arsip lengkap dengan dashboard dan retensi otomatis, lalu setelah dua bulan berhenti memperhatikannya karena rusak dan tidak ada yang memperbaiki. Backup sederhana yang jalan tiap hari jauh lebih berharga daripada sistem hebat yang mati.
Saya akan tunjukkan semua langkahnya: setup repo, script backup, dorong offsite, jebakan yang saya alami sendiri, dan yang paling penting - cara menguji restore supaya kamu benar-benar tahu backup-mu bisa dipakai.
Berapa Lama Sebaiknya Backup Disimpan
Pertanyaan retensi sering dijawab dengan angka asal, misalnya "simpan 30 hari". Padahal yang menentukan seharusnya dua hal: seberapa cepat kamu menyadari ada data rusak, dan seberapa lama kamu butuh riwayat untuk audit.
Kerusakan data yang tidak sengaja biasanya baru ketahuan berminggu-minggu kemudian - satu kolom terisi nilai salah sejak deploy tertentu, atau file gambar terhapus saat membersihkan folder upload. Kalau kamu hanya menyimpan 7 hari, riwayat untuk kasus itu tidak ada. Untuk itulah lapisan mingguan dan bulanan dipasang: bukan karena kamu restore tiap minggu, tapi karena kamu butuh titik balik dari kondisi dua bulan lalu.
Sebaliknya, menyimpan seluruh snapshot harian selamanya juga tidak perlu. Deduplikasi memang menekan ukuran, tapi metadata dan biaya listing tetap bertambah. Pola 7 harian, 4 mingguan, dan 12 bulanan sudah cukup untuk hampir semua aplikasi kecil dan menengah.
Kenapa Saya Pilih Restic
Sebelum sampai ke sana, saya sempat pakai tiga pendekatan lain dan semuanya punya kekurangan yang bikin saya pindah.
rsync + tar manual murah dan sederhana, tapi deduplikasi nol. Setiap snapshot menyalin ulang semuanya. Untuk folder 20 GB dengan backup harian, kamu bakar 600 GB per bulan. Borgbackup jauh lebih baik - deduplikasi, kompresi, enkripsi - tapi repo-nya harus diakses lewat SSH atau filesystem lokal. Push ke S3-compatible storage resmi nggak didukung. Snapshot provider (misal snapshot disk dari panel VPS) cepat tapi terikat provider dan mahal kalau disimpan lama.
Restic menutup celah itu: repo bisa berada di filesystem lokal, SFTP, REST server, sampai puluhan backend object storage. Deduplikasi berbasis content-defined chunking, jadi potongan file yang mirip tetap dikenali walau posisinya bergeser. Enkripsi AES-256 terjadi di sisi klien, jadi provider storage nggak pernah melihat data mentah. Dan yang paling saya suka: restore bisa granular. Saya nggak perlu tarik seluruh arsip untuk mengambil satu file config.
Setup Awal: Install dan Inisialisasi Repo
Restic tersedia di repo distro, dan versinya sudah cukup modern di Ubuntu 24.04. Rclone juga sama.
sudo apt update\nsudo apt install -y restic rclone\nrestic version\n
Sebelum inisialisasi, simpan password repo di file dengan permission ketat. Password ini nggak bisa di-recover - kalau hilang, seluruh arsip jadi sampah.
sudo
mkdir -p /etc/restic\nsudo sh -c 'openssl rand -base64 32 > /etc/restic/password'\nsudo
chmod 600 /etc/restic/password\n
Lalu inisialisasi repo lokal. Saya pakai /var/backups/restic sebagai staging, nanti didorong offsite oleh rclone.
export RESTIC_PASSWORD_FILE=/etc/restic/password\nsudo -E restic init --repo /var/backups/restic\n
Sekarang buat script backup. Saya sengaja pakai file terpisah, bukan one-liner di crontab, supaya gampang di-debug saat gagal di jam 3 pagi.
sudo tee /usr/local/bin/backup-kinara.sh <<'EOF'\n#!/usr/bin/env bash\nset -euo pipefail\n\nexport RESTIC_PASSWORD_FILE=/etc/restic/password\nexport RESTIC_REPOSITORY=/var/backups/restic\n\n# Buang snapshot lokal lebih tua dari 7 hari (offsite tetap simpan lama)\nrestic forget --keep-daily 7 --keep-weekly 4 --prune\n\n# Backup folder aplikasi dan config, exclude cache dan node_modules\nrestic backup \\\n /var/www/html/app \\\n /var/www/html/public \\\n /etc/nginx \\\n /etc/restic \\\n --exclude '*.log' \\\n --exclude '*/node_modules' \\\n --exclude '*/cache' \\\n --tag daily\nEOF\n\nsudo
chmod +x /usr/local/bin/backup-kinara.sh\n
Catatan penting soal database: jangan backup file .ibd MySQL yang sedang hidup. File-nya bisa dalam keadaan setengah tertulis dan restore-nya gagal. Dump dulu, baru masuk ke snapshot.
mysqldump --single-transaction --quick \\\n -u appuser -p'PASS' kinara_project \\\n | gzip > /var/backups/mysql-$(date +%F).sql.gz\n
Flag --single-transaction bikin dump konsisten tanpa mengunci tabel InnoDB, jadi aman dijalankan saat traffic jalan.
Dorong Offsite dengan Rclone
Bagian ini yang membedakan backup sungguhan dari sekadar salinan. Rclone mendukung banyak provider; saya pakai Cloudflare R2 karena egress-nya gratis dan kompatibel S3, tapi langkahnya sama untuk Backblaze B2 atau MinIO pribadi.
rclone config create r2 s3 \\\n provider=Cloudflare \\\n access_key_id=AKIAxxxx \\\n secret_access_key=xxxx \\\n endpoint=https://ACCOUNT_ID.r2.cloudflarestorage.com \\\n acl=private\n
Setelah terhubung, sinkronkan repo restic ke bucket. Perhatikan --fast-list supaya listing ribuan object nggak lambat, dan jangan pakai --delete di sini kalau kamu juga menjalankan pruning lokal - urutannya bisa bikin data offsite terhapus lebih dulu.
rclone sync /var/backups/restic r2:kinara-backup/restic \\\n --fast-list \\\n --transfers 8 \\\n --log-file /var/log/rclone-backup.log \\\n --log-level INFO\n
Kalau kamu ingin menjalankan restic langsung ke R2 tanpa staging lokal, restic mendukung backend s3: - tapi saya tetap pilih pola dua tahap. Alasannya: pruning di object storage jauh lebih lambat dan lebih mahal karena setiap operasi adalah request HTTP. Staging lokal membuat prune berjalan di disk cepat, lalu rclone hanya mengirim delta.
Jebakan yang Saya Alami Sendiri
Bagian ini bukan teori. Ini empat hal yang benar-benar bikin backup saya gagal dan tidak saya sadari selama berminggu-minggu.
Satu: cron berjalan tanpa environment. Script jalan sempurna saat saya ketik manual, tapi di cron selalu exit 1. Ternyata $PATH di cron cuma berisi /usr/bin:/bin, sehingga restic di /usr/local/bin nggak ditemukan. Solusinya pakai path absolut di dalam script, bukan mengandalkan PATH.
Dua: password file nggak terbaca oleh user cron. Saya taruh password di /root dan cron jalan sebagai user lain. Restore baru gagal berbulan-bulan kemudian, karena backup-nya sendiri sebenarnya tidak pernah berhasil. Sekarang saya verifikasi keberhasilan setiap hari, bukan mengasumsikan.
Tiga: repository lock tertinggal. Kalau proses restic mati di tengah jalan (OOM killer, reboot), lock tetap ada dan job berikutnya gagal terus. Perlu langkah restic unlock manual. Saya tambahkan penanganan ini ke wrapper.
Empat: tidak ada notifikasi. Ini yang paling berbahaya. Cron gagal dan tidak ada yang tahu. Sekarang setiap kegagalan mengirim pesan ke Telegram lewat satu baris curl, dan saya punya kebiasaan membaca laporan mingguan berisi ukuran repo serta snapshot terakhir.
#!/usr/bin/env bash\n# wrapper: jalankan backup, laporkan hasilnya\nset -uo pipefail\n\n/usr/bin/restic unlock --remove-all >/dev/null 2>&1 || true\n\nif /usr/local/bin/backup-kinara.sh >>/var/log/backup.log 2>&1; then\n MSG=\"Backup OK - $(date +%F)\"\nelse\n MSG=\"BACKUP GAGAL - cek /var/log/backup.log\"\nfi\n\ncurl -s -X POST \"https://api.telegram.org/bot$TOKEN/sendMessage\" \\\n -d chat_id=$CHAT_ID \\\n --data-urlencode \"text=$MSG\" >/dev/null\n
Cara Menguji Restore (Ini yang Wajib)
Tanpa langkah ini, semua di atas cuma ritual. Restore test artinya kamu benar-benar mengembalikan data ke direktori lain, lalu membuka file-nya untuk memastikan isinya masuk akal.
export RESTIC_PASSWORD_FILE=/etc/restic/password\nexport RESTIC_REPOSITORY=/var/backups/restic\n\n# 1. Lihat snapshot yang tersedia\nrestic snapshots\n\n# 2. Cek isi snapshot tanpa restore (cepat)\nrestic ls latest | head -20\n\n# 3. Restore seluruh snapshot ke folder uji\nrestic restore latest --target /tmp/restore-test\n\n# 4. Ambil satu file saja dari snapshot lama\nrestic restore latest --target /tmp/single \\\n --include /etc/nginx/sites-enabled/default\n
Setelah itu buka beberapa file hasil restore dan bandingkan dengan aslinya. Saya biasa pakai diff -r untuk folder kecil. Untuk database, restore dump-nya ke schema terpisah dan jalankan SELECT COUNT(*) pada tabel utama - kalau angkanya cocok dengan produksi, baru saya anggap backup-nya sah.
CREATE DATABASE kinara_restore_test;\n-- setelah import dump ke schema ini:\nSELECT COUNT(*) FROM kinara_restore_test.blog_posting;\nDROP DATABASE kinara_restore_test;\n
Jadwal dan Retensi yang Masuk Akal
Prinsipnya bukan "seberapa sering", tapi "berapa lama saya siap kehilangan data". Untuk blog dan aplikasi kecil, kehilangan 24 jam terakhir masih bisa diterima, jadi backup harian cukup. Untuk database transaksi, mungkin perlu binlog atau WAL shipping.
Untuk retensi, saya pakai pola klasik yang hemat ruang: simpan 7 harian, 4 mingguan, dan 12 bulanan. Total sekitar 23 snapshot, tapi karena deduplikasi, ruang yang terpakai jauh lebih kecil dari 23 kali ukuran data.
# Tambahkan ke crontab user root\n0 2 * * * /usr/local/bin/backup-wrapper.sh\n30 2 * * * /usr/bin/rclone sync /var/backups/restic r2:kinara-backup/restic --fast-list --log-file /var/log/rclone-backup.log\n
Perhatikan jeda 30 menit antara snapshot dan sinkronisasi. Kalau dijalankan bersamaan, rclone bisa membaca repo saat restic masih menulis file indeks, dan hasilnya upload yang tidak lengkap.
Ceklis Sebelum Kamu Tidur Tenang
Kalau kamu hanya ingin mengambil satu hal dari artikel ini, ambil daftar ini dan jalankan berurutan. Butuh sekitar satu jam pertama kali, dan setelah itu nggak perlu disentuh lagi.
- Pastikan backup berada di minimal dua provider berbeda, bukan dua folder di server yang sama.
- Simpan password repo di password manager, bukan hanya di server yang kamu backup.
- Dump database sebelum snapshot, jangan salin file data mentah.
- Pasang notifikasi kegagalan - backup diam-diam yang gagal lebih buruk daripada tidak punya backup.
- Jadwalkan restore test per kuartal dan catat hasilnya di kalender, bukan di kepala.
- Ukur RTO (berapa lama restore) dan RPO (berapa banyak data yang rela hilang). Angkanya harus kamu tahu sebelum insiden, bukan sesudahnya.
- Uji skenario kehilangan server total: kalau VPS-mu hilang sekarang, apakah kamu bisa online lagi dalam satu jam?
Setelah insiden rm -rf itu, saya mengubah satu kebiasaan kecil yang efeknya besar: sebelum mengetik perintah destruktif, saya jalankan pwd dan ls dulu, lalu bikin snapshot manual. Butuh lima detik. Sama sekali nggak sebanding dengan tiga hari kerja yang hilang.
Pertanyaan buat kamu: kapan terakhir kali kamu benar-benar mencoba restore dari backup-mu? Kalau jawabannya "belum pernah" atau "lupa", itu artinya kamu belum punya backup - kamu cuma punya salinan. Tulis di komentar tool apa yang kamu pakai sekarang, dan kalau ada yang sudah pernah restore dalam keadaan darurat, ceritakan apa yang paling mengejutkan.