
Backup VPS yang benar-benar bisa Anda restore
Hampir semua orang yang menjalankan server punya sesuatu yang mereka sebut backup. Jauh lebih sedikit yang pernah benar-benar me-restore-nya, dan jarak antara dua kalimat itulah tempat data sungguhan menghilang. Berikut cara membangun salinan yang bertahan dari tiga hal yang benar-benar menghancurkan server — tangan Anda sendiri, sebuah kompromi, dan akun yang berhenti Anda kendalikan — tanpa diam-diam menempelkan identitas terverifikasi ke mesin yang anonim.
Kami menyimpan tiga salinan dari setiap byte yang menjalankan jaringan ini, dan kami sudah me-restore darinya, dengan sengaja, pada hari Selasa biasa, lebih sering daripada yang pernah benar-benar kami butuhkan saat darurat. Itu bukan kehati-hatian demi kehati-hatian. Itu satu-satunya cara untuk tahu bahwa yang ada di repository benar-benar backup, bukan sekadar folder berisi noise terenkripsi dengan nama yang terdengar meyakinkan.
Saran di bawah ini sengaja dibuat tidak trendi. Tidak melibatkan produk backup dengan dashboard, langganan bulanan, atau antrean support, karena semua hal itu adalah sebuah akun, dan akun berarti nama, kartu, dan yurisdiksi — tiga hal yang mungkin sudah Anda upayakan keras untuk dijauhkan dari server ini. Yang dibutuhkan hanyalah mesin kedua, sekitar empat puluh baris shell, dan satu kebiasaan yang nyaris tidak pernah dijaga siapa pun.
Anda seharusnya bisa menyelesaikan semua ini dalam satu malam. Tooling-nya bernuansa Debian 13 dan Ubuntu 24.04; semuanya bisa dipindahkan ke turunan RHEL dengan substitusi yang sudah jelas. Kalau server Anda baru, kerjakan dulu hardening jam pertama sebelum halaman ini, bukan sesudahnya — membackup mesin yang sudah menjadi milik orang lain hanya akan melestarikan pekerjaan mereka.
Apa yang sebenarnya menghancurkan data di server sewaan
Tanyakan ke siapa pun kenapa mereka backup, dan jawabannya biasanya “kalau-kalau disknya mati”. Di VPS modern, itu justru hal yang paling tidak mungkin terjadi pada Anda. Volume Anda ada di NVMe dalam array redundan dengan hypervisor yang akan memindahkan Anda dari host yang bermasalah sebelum Anda sadar; kegagalan drive adalah masalah provider, dan itu masalah yang sudah terpecahkan. Membangun seluruh strategi backup Anda di sekitar itu sama seperti membeli alat pemadam kebakaran untuk rumah yang kebanjiran.
Berikut yang sebenarnya merenggut data orang, kurang lebih berurutan dari yang paling sering. Pertama, dan dengan selisih jauh: Anda sendiri. rm -rf dengan variabel yang ternyata kosong. DROP DATABASE di terminal yang salah karena dua tab terlihat identik. Skrip migrasi yang dijalankan dua kali. docker compose down -v di mana -v itu sudah jadi refleks tangan. Semua ini bukan hal eksotis; ini hari Selasa biasa.
Kedua, aplikasi yang menghapus datanya sendiri. Upgrade yang menjalankan migrasi destruktif lalu gagal di tengah jalan. Plugin yang membersihkan direktori cache yang ternyata bukan cache. Log rotation yang dikonfigurasi ke path yang salah. Ketiga, kompromi — dan perhatikan bahwa dalam kasus ini attacker justru ingin backup-nya lenyap, yang mengubah desain sistem dengan cara yang akan kita bahas lagi di bawah. Keempat, dan mudah terlupakan di hosting no-KYC: kehilangan akun. Saldo yang menyentuh nol saat Anda sedang pergi, entri password manager yang tidak pernah Anda catat, alamat email yang Anda tinggalkan. Kami men-suspend dalam hitungan jam setelah saldo nol dan menghancurkan tujuh hari kemudian, dan itu memang sengaja dibuat tanpa toleransi — itulah hitungan yang memungkinkan kami untuk tidak bertanya siapa Anda. Tidak ada support agent yang bisa mencari identitas Anda dan membuat pengecualian, karena memang tidak ada identitas yang bisa dicari.
Kategori terakhir itulah yang membedakan hosting privacy-first dari yang mainstream, dan seharusnya membentuk cara Anda merencanakan semua ini. Setiap kontrol yang membuat server ini sulit diatribusikan ke Anda juga membuatnya sulit bagi siapa pun — termasuk kami — untuk mengembalikannya ke Anda. Backup adalah hal yang mengubah trade-off itu dari risiko menjadi pilihan.
Kenapa snapshot bukan backup
Snapshot itu hal yang bagus dan Anda memang harus memakainya. Ambil satu sebelum setiap upgrade kernel, setiap migrasi database, setiap kali Anda akan melakukan sesuatu yang ingin bisa Anda batalkan. Snapshot pulih dalam hitungan detik, biayanya nyaris nol, dan akan menyelamatkan satu sore penuh Anda belasan kali dalam setahun.
Snapshot tetap bukan backup, karena tiga alasan struktural yang tidak akan berubah oleh materi marketing provider mana pun. Ia hidup di dalam akun yang sama: siapa pun yang bisa login ke panel Anda bisa menghapusnya, dan kalau akunnya berhenti berfungsi, snapshot itu ikut lenyap bersamanya. Ia hidup di failure domain yang sama: provider yang sama, control plane yang sama, sering kali storage cluster yang sama, yang berarti satu kejadian buruk bisa saja merenggut baik yang asli maupun salinannya sekaligus. Dan ia opak: snapshot dari mesin yang sedang berjalan menangkap database di tengah proses penulisan persis sesetia ia menangkap segala hal lainnya, jadi restore snapshot dari server MySQL yang sibuk memberi Anda skenario crash-recovery, bukan database yang bersih.
Aturan lama 3-2-1 — tiga salinan, di dua jenis media, salah satunya di luar lokasi — biasanya diulang-ulang tanpa ada yang sadar bahwa klausa tengahnya tidak berarti apa-apa di infrastruktur sewaan. Anda tidak punya dua jenis media. Anda punya satu virtual block device, dan satu virtual block device lagi, dan keduanya adalah SAN milik orang lain. Yang tetap relevan setelah diterjemahkan ke konteks VPS adalah bagian yang memang selalu jadi intinya: setidaknya satu salinan harus berada di tempat yang tidak bisa dijangkau oleh satu kejadian buruk yang sama. Provider yang berbeda, atau minimal region yang berbeda, kredensial yang berbeda, akun yang berbeda, dan — kalau alasan Anda hosting offshore bersifat legal, bukan teknis — yurisdiksi yang berbeda, supaya satu surat perintah pengadilan tidak bisa menjangkau kedua salinan sekaligus.
Tujuan backup adalah bagian dari threat model Anda
Ini adalah bagian yang tidak dimiliki tutorial backup lain, dan justru paling penting kalau Anda membayar server ini dengan Monero.
Saran default di mana-mana di internet adalah push backup Anda ke object store: Backblaze B2, Amazon S3, Wasabi, Google Drive lewat rclone. Murah, tahan lama, dan berfungsi. Tapi itu juga, dalam satu perintah, membatalkan banyak hal yang sudah Anda usahakan. Membuka akun itu membutuhkan kartu dan biasanya dokumen identitas. Sejak momen snapshot pertama diunggah, provider itu memegang rekaman berstempel waktu yang mengaitkan nama dan detail pembayaran Anda dengan alamat IP server Anda, diperbarui setiap jam, selamanya. Anda tidak memverifikasi identitas Anda ke kami; Anda memverifikasinya ke mereka, lalu Anda sendiri yang menarik garis penghubung antara keduanya.
Bagian keduanya lebih buruk lagi, dan inilah yang sering luput dari perhatian orang. API key yang mengotorisasi upload itu adalah sebuah file di server produksi. Siapa pun yang berhasil dapat root di mesin itu — atau siapa pun yang secara sah menyita disknya — tidak hanya mendapat data Anda. Mereka mendapat kredensial yang, dalam satu API call, langsung mengarah ke identitas billing Anda. Server anonim itu kini sudah berubah menjadi papan penunjuk arah menuju rekening bank Anda.
Semua ini tidak membuat object storage jadi pilihan yang salah. Ini hanya membuatnya jadi sebuah keputusan, bukan default begitu saja. Ada tiga jalan keluar, berurutan dari yang paling menjaga apa yang sudah Anda bangun dari awal:
- VPS no-KYC kedua, idealnya di yurisdiksi yang berbeda dari produksi, dibayar dari saldo kripto yang sama. Tujuan backup jadi mewarisi properti anonimitas dari sumbernya, bukan malah bertentangan dengannya. Ini yang kami lakukan sendiri, dan yang diasumsikan oleh sisa panduan ini.
- Storage provider yang menerima kripto tanpa identitas. Mereka memang ada; skalanya lebih kecil; periksa dulu apakah jalur pembayarannya benar-benar bebas identitas sebelum Anda percaya begitu saja pada materi marketing-nya, dan periksa harga egress-nya sebelum Anda baru menyadarinya saat sedang restore.
- Mesin yang Anda miliki secara fisik, melakukan pull lewat WireGuard dari balik koneksi rumah Anda sendiri. Sangat baik untuk anonimitas, buruk untuk availability, dan artinya kecepatan restore Anda dibatasi oleh kecepatan upload rumah Anda. Masuk akal sebagai salinan ketiga, lemah sebagai satu-satunya salinan.
Apa pun yang Anda pilih, akun yang menyimpan backup sebaiknya tidak berbagi kredensial, alamat email, atau recovery path dengan akun yang menyimpan produksi. Intinya adalah satu login yang dikompromikan tidak boleh sampai menjangkau keduanya sekaligus. Ini disiplin yang sama dengan menjaga identitas tetap terpisah di tempat lain, hanya saja diterapkan ke sudut sistem yang paling tidak glamor.
Push, pull, dan alasan ransomware selalu menemukan backup Anda
Hampir semua tutorial backup menghasilkan arsitektur yang sama: cron job di server produksi yang mengautentikasi ke repository remote dan menulis ke sana. Sederhana, berfungsi, dan punya satu sifat yang tidak pernah disebut siapa pun — server produksi memegang kredensial yang bisa menghapus seluruh repository. Pruning snapshot lama membutuhkan hak hapus, jadi key yang menjalankan job malam Anda adalah key yang sama yang bisa mengosongkan brankasnya.
Pikirkan apa artinya itu saat terjadi kompromi. Attacker yang sudah dapat root di mesin itu tidak perlu repot mencari backup Anda; Anda sudah baik hati meninggalkan kredensial yang berfungsi dan file konfigurasi yang menyebutkan lokasi repository-nya. Mendata lalu menghancurkan backup sebelum memicu apa pun yang terlihat bukan penyempurnaan hipotetis — itu sudah jadi praktik standar, karena itulah yang mengubah sebuah insiden menjadi negosiasi. Job malam Anda sendiri yang jadi pengintaiannya.
Ada tiga desain, dan perbedaan di antara ketiganya sepenuhnya soal mesin mana yang memegang key yang mana.
Plain push adalah yang dijelaskan di atas. Produksi punya hak read-write-delete. Praktis, dan gagal total justru pada skenario di mana Anda paling membutuhkan backup. Pakai ini hanya kalau satu-satunya ancaman yang benar-benar Anda khawatirkan adalah jari Anda sendiri.
Append-only push mempertahankan bentuk yang sama tapi menghilangkan kata kerja berbahayanya. Repository dilayani oleh sesuatu yang memahami protokolnya dan menolak penghapusan: rest-server --append-only untuk restic, atau borg serve --append-only yang dipaksakan lewat authorized_keys. Produksi bisa membuat snapshot baru dan tidak bisa menghapus yang lama. Pruning terjadi belakangan, dari tempat lain, memakai key yang berbeda. Ini peningkatan besar untuk sekitar dua puluh menit kerja, dan bagi kebanyakan orang, ini titik berhenti yang tepat.
Pull membalik arah koneksinya. Backup host yang menjangkau ke produksi lewat SSH, menyalin apa yang ia butuhkan, lalu menjalankan tool backup secara lokal di disknya sendiri. Produksi sama sekali tidak memegang kredensial backup apa pun — tidak ada apa pun di mesin itu yang bisa ditemukan, karena mesin itu bahkan tidak tahu di mana backup-nya berada. Ini susunan yang paling kuat, dan inilah yang dibangun oleh langkah-langkah di bawah.
Jujur saja soal biaya yang harus dibayar oleh pull, karena ini tidak gratis. Anda memindahkan key itu, bukan menghilangkannya: backup host sekarang memegang SSH key menuju produksi, jadi kompromi terhadap backup host bisa menjalar maju ke sistem live. Itu trade-off yang lebih baik — backup host tidak menjalankan apa pun, tidak mengekspos apa pun selain SSH, dan jadi target yang jauh lebih kecil dibanding web server publik — tapi tetap sebuah trade-off, dan Anda menutup sebagian besar celah yang tersisa dengan memaksa key itu hanya bisa menjalankan perintah read-only, sehingga tidak bisa dipakai untuk apa pun selain membaca file.
Memilih tool: restic, Borg, atau rsync biasa
Tiga tool ini mencakup hampir semua kasus, dan memilihnya jauh lebih tidak menyiksa dibanding kesan yang diberikan thread-thread forum.
restic mengenkripsi secara default, melakukan dedup lintas snapshot, mendukung SFTP, S3, REST, dan belasan backend lain, dan didistribusikan sebagai satu static binary yang bisa Anda taruh di mesin apa pun. Format repository-nya content-addressed, jadi snapshot jadi murah dan data yang identik hanya disimpan sekali berapa pun banyaknya mesin yang mengirimkannya. Biayanya nyata tapi tidak besar: ia butuh memori sebanding dengan ukuran index repository, dan run yang terganggu bisa meninggalkan lock basi yang ditolak dilangkahi oleh run berikutnya sampai Anda membersihkannya dengan restic unlock. Ini rekomendasi default kami, dan yang dipakai oleh langkah-langkah di bawah.
Borg dedup-nya lebih baik, kompresinya lebih baik, dan jauh lebih cepat pada repository dengan jutaan file kecil. Trade-off-nya adalah coupling: Borg harus terpasang di kedua ujung dan versinya harus benar-benar cocok, satu repository memang dirancang untuk satu client, dan tidak punya backend object-store native tanpa helper layer tambahan. Kalau sumber data Anda adalah mail spool besar atau filesystem penuh file kecil, dan Anda menguasai kedua mesin, Borg akan memakai separuh ruang penyimpanan. Mode --append-only-nya juga merupakan implementasi paling bersih dari ide ini dibanding kedua tool.
rsync bukan tool backup, dan berpura-pura seolah begitu adalah cara orang berakhir dengan satu salinan direktori corrupt yang ter-mirror dengan setia. Tidak ada versioning, tidak ada dedup, tidak ada enkripsi at rest; rsync --delete menyebarkan kesalahan Anda ke salinannya secepat kecepatan kabel. Meski begitu, ini tool yang tepat untuk memindahkan byte antara dua mesin yang Anda kuasai, yang persis pekerjaan yang ia lakukan dalam desain pull, dengan restic menyediakan versioning dan enkripsi setelah byte-nya sampai. Pakai masing-masing sesuai fungsinya.
Satu hal yang jangan dilakukan: jangan membuat solusi sendiri dengan tar dan nama file berstempel tanggal. Itu akan berfungsi selama kira-kira empat bulan, sampai hari disknya penuh karena tidak ada satu pun yang pernah expired, atau hari Anda sadar bahwa salinan penuh setiap malam dari dataset 40 GB berarti 1,2 TB penyimpanan per bulan yang Anda bayar hanya untuk menyimpan tiga puluh hal yang nyaris identik.
Apa yang harus dimasukkan — dan database yang akan mengkhianati Anda
Naluri pertama adalah mem-backup seluruh filesystem. Lawan naluri itu. Root filesystem sebagian besar isinya paket distribusi yang bisa Anda instal ulang dalam sembilan puluh detik, dan memasukkannya hanya menghabiskan storage, waktu transfer, dan yang lebih buruk, perhatian — backup 40 GB yang tidak ada yang mau mengujinya jauh kurang berguna dibanding backup 900 MB yang di-restore setiap kuartal.
Yang benar-benar tidak bisa dibangun ulang justru singkat daftarnya: /etc (seluruh konfigurasi Anda, dan alasan kenapa rebuild memakan waktu satu jam, bukan satu akhir pekan), /home dan /root, /srv dan /var/www, state aplikasi di bawah /var/lib/ dan /opt/, named Docker volume, unit cron dan systemd yang Anda tulis sendiri, serta dump database Anda. Lewati /proc, /sys, /dev, /run, /tmp, /var/cache, file swap, socket, dan /var/lib/docker/overlay2 — yang terakhir itu bisa dibangun ulang dari compose file Anda dan sering kali menjadi direktori tunggal terbesar di disk.
Sekarang bagian yang diam-diam merusak restore. Menyalin direktori data database saat database masih berjalan menghasilkan kumpulan file yang sangat mungkin tidak bisa dipakai. Engine-nya menyimpan state di memori, menulis ke banyak file dengan urutan yang penting, dan proses penyalinan Anda menyusuri tree itu selama beberapa menit, menangkap file yang berbeda pada momen yang berbeda pula. InnoDB kadang bisa crash-recover darinya, kadang tidak; kegagalannya baru muncul berbulan-bulan kemudian, saat restore, di hari yang sudah buruk buat Anda.
Dump lewat engine-nya sebagai gantinya, baru backup hasil dump-nya:
# MariaDB / MySQL — InnoDB gets a consistent snapshot from one transaction
mariadb-dump --single-transaction --quick --routines --triggers --events \
--all-databases | zstd -T0 > /var/backups/db/all-$(date -u +%F).sql.zst
# PostgreSQL — pg_dump is consistent by design; grab roles separately
pg_dumpall --globals-only > /var/backups/db/globals.sql
pg_dump -Fc -Z6 mydb > /var/backups/db/mydb.dump
# SQLite — never cp a live database; WAL mode makes that a torn file
sqlite3 /var/lib/app/app.db ".backup /var/backups/db/app.db"Ada dua catatan kaki yang layak diketahui. --single-transaction hanya memberi Anda konsistensi untuk InnoDB — kalau ada tabel yang masih MyISAM, tabel itu disalin di luar transaksi dan bisa jadi tidak konsisten dengan yang lain, jadi konversi tabel-tabel itu atau kunci saja. Dan begitu Anda sudah melakukan dump, exclude direktori data live dari file pass. Mem-backup keduanya berarti menyimpan satu salinan besar yang robek di samping salinan yang baik, dan memberi restore di masa depan dua kandidat padahal satu di antaranya diam-diam salah.
Container tidak mengubah prinsipnya, hanya path-nya: jalankan dump dengan docker compose exec -T db mariadb-dump … dan backup file hasilnya, bukan volume di baliknya.
Di mana key enkripsi seharusnya disimpan
Enkripsi sisi client adalah keseluruhan alasan kenapa boleh-boleh saja menyimpan data Anda di mesin yang bukan asalnya. restic dan Borg sama-sama mengenkripsi sebelum apa pun meninggalkan sumbernya, jadi backup host hanya memegang ciphertext dan bisa diperlakukan sebagai storage yang tidak tepercaya. Nilai properti itu persis sebesar seberapa baik Anda mengelola key-nya, tidak lebih.
Mode kegagalannya sangat umum, sampai menyedihkan. Password repository ada di /etc/restic/env di server produksi, yang memang wajar dan perlu untuk otomatisasi. Lalu produksi itulah yang justru hilang — dihancurkan, disita, atau sekadar lenyap bersama akunnya — dan Anda tinggal menatap ratusan gigabyte blok terenkripsi sambil perlahan sadar bahwa satu-satunya salinan key itu ada di dalam benda yang justru sedang Anda lindungi dari risiko hilang.
Jadi: password di server itu adalah salinan kerja, bukan pernah menjadi catatan utama. Catatan utamanya harus hidup di tempat yang tetap bertahan meski servernya hilang — password manager yang vault-nya sendiri di-backup di tempat lain, atau ditulis di kertas di dalam laci, atau keduanya. Catat lokasi repository dan perintah restore yang persis di sebelahnya, karena passphrase tanpa konteks adalah teka-teki yang harus Anda pecahkan dalam keadaan panik delapan belas bulan dari sekarang. Dan luangkan lima menit untuk memverifikasinya: dari mesin yang berbeda, hanya dengan apa yang ada di password manager, jalankan restic snapshots terhadap repository itu. Kalau berhasil, Anda punya backup. Kalau ia butuh sesuatu yang hanya ada di produksi, Anda cuma punya folder yang sangat mahal.
Kedua tool mendukung banyak key dalam satu repository (restic key add), yang merupakan cara bersih untuk memberi akses ke job pruning atau admin kedua tanpa membagikan passphrase asli. Dan kalau disk produksi Anda dienkripsi dengan LUKS, jaga agar kedua rahasia itu benar-benar terpisah — menyimpan passphrase restic di dalam volume LUKS, dan passphrase LUKS di dalam repository restic, adalah sebuah loop yang gagal tertutup di kedua sisi sekaligus.
Retention: jebakan hanya menyimpan satu minggu
Retention terlihat seperti soal biaya storage, padahal sebenarnya soal detection-latency. Angka yang penting bukan berapa banyak disk yang mau Anda habiskan; tapi berapa lama sebuah masalah bisa lolos tanpa disadari di sistem Anda. Berapa pun panjang periode itu, backup tertua Anda harus lebih tua dari itu.
Tujuh snapshot harian terasa banyak, tapi tipis dalam praktiknya. Tabel corrupt yang tidak pernah di-query siapa pun, penghapusan lambat oleh cron job yang bermasalah, intrusi yang diam-diam bersembunyi selama sebulan sebelum melakukan apa pun yang terlihat — semua ini biasa muncul ke permukaan lebih dari seminggu kemudian, dan kalau histori Anda hanya sedalam tujuh hari, maka setiap snapshot yang Anda punya sudah tercemar semua. Dwell time pada intrusi sungguhan biasa diukur dalam hitungan minggu. Snapshot bulanan yang murah adalah pertahanan spesifik untuk itu, dan dedup membuatnya benar-benar murah: satu snapshot bulanan yang disimpan setahun hanya menambah sebagian kecil dari ukuran satu salinan penuh, karena hanya blok yang benar-benar berubah yang disimpan dua kali.
restic forget \
--keep-daily 7 --keep-weekly 5 --keep-monthly 12 --keep-yearly 2 \
--pruneTangga retention itu — seminggu penuh harian, sebulan penuh mingguan, setahun penuh bulanan, beberapa tahun tahunan — jumlahnya sekitar dua puluh enam snapshot dan, pada data pada umumnya, jauh di bawah dua kali ukuran satu salinan penuh. Ini default yang akan kami perjuangkan kecuali Anda punya alasan khusus untuk menyimpang darinya.
Dua catatan operasional. forget tanpa --prune hanya menghapus label-nya, jadi ruang disknya tidak benar-benar kembali sampai Anda melakukan prune — yang sering mengejutkan orang yang mengamati disk yang tidak kunjung mengecil. Dan --prune membutuhkan hak hapus di repository, jadi dalam desain append-only atau pull, ini tidak dijalankan di server produksi. Ia dijalankan di backup host, atau dari laptop Anda, memakai key yang tidak pernah dilihat oleh produksi. Pemisahan itulah keseluruhan intinya; jangan batalkan demi kepraktisan di langkah terakhir.
Mengotomatisasi semua ini tanpa kegagalan yang diam-diam
Bencana backup yang klasik bukanlah job yang crash. Melainkan job yang berhenti berjalan dan tidak memberi tahu siapa pun, baru ditemukan sebelas bulan kemudian oleh seseorang yang sedang membutuhkannya. Setiap elemen di bawah ini ada untuk membuat hasil akhir spesifik itu jadi mustahil terjadi.
Pakai systemd timer, bukan cron. Anda mendapat log sungguhan di journal lengkap dengan exit status-nya, Persistent=true supaya run yang terlewat saat mesin mati akan dijalankan lagi di boot berikutnya alih-alih dilewati selamanya, dan RandomizedDelaySec supaya sekumpulan server tidak menyerbu backup host tepat pukul 03:00 secara bersamaan. Notifikasi kegagalan dari cron adalah email ke mailbox lokal yang, di server modern, tidak pernah sampai ke mana pun.
# /etc/systemd/system/backup.timer
[Unit]
Description=Nightly encrypted backup
[Timer]
OnCalendar=*-*-* 03:17:00
RandomizedDelaySec=1800
Persistent=true
[Install]
WantedBy=timers.targetLalu tambahkan dead-man's switch, satu baris bernilai paling tinggi di seluruh panduan ini. Di bagian paling akhir skrip — setelah backup berhasil, bukan sebelumnya — buat request HTTPS ke sebuah monitor yang mengharapkan kabar dari Anda setiap hari dan memberi alert kalau tidak menerimanya. Ini membalik logika notifikasinya: alih-alih mengandalkan kegagalan untuk memicu pesan, ketiadaan itu sendirilah yang menjadi alarmnya. Job yang sudah mati selama tiga hari jadi sebuah pesan di inbox Anda, bukan temuan di tengah krisis. Self-host monitor-nya di backup host kalau Anda tidak ingin membuat akun pihak ketiga lagi; cukup beberapa baris dan sebuah timer-nya sendiri.
Terakhir, bahaya-bahaya operasional kecil, yang semuanya pernah kami alami sendiri setidaknya sekali. Run yang terganggu meninggalkan lock, dan setiap run berikutnya gagal dengan pesan yang berhenti Anda baca setelah malam keempat — tangani restic unlock dengan sengaja, bukan refleks. Disk tujuan yang penuh menggagalkan semua job sampai ada yang memeriksanya; pasang alert untuk free space, bukan cuma untuk status job. SSH key yang punya masa berlaku, host key yang di-rotate, rule nftables yang ditambahkan saat mengerjakan hal lain yang tidak berhubungan — masing-masing bisa diam-diam memutus pull-nya. Dan systemctl enable --now, bukan cuma start: timer yang tidak pernah di-enable akan berfungsi sempurna sampai reboot pertama, lalu tidak pernah berjalan lagi.
Latihan restore
Semua yang di atas hanyalah persiapan. Ini bagian yang benar-benar mengubahnya menjadi backup, dan ini bagian yang nyaris selalu dilewatkan semua orang.
Sekali per kuartal, deploy VPS baru — paket Starter $5 sudah lebih dari cukup dan billing-nya per jam, prorata sampai ke detik, jadi seluruh latihan ini cuma menghabiskan beberapa sen. Restore ke dalamnya hanya dengan apa yang akan Anda punya saat bencana sungguhan: alamat repository, passphrase dari password manager Anda, dan prosedur tertulisnya. Sengaja jangan pakai apa pun dari produksi, karena dalam skenario yang sedang Anda latih ini, produksi tidak lagi ada. Nyalakan aplikasinya, arahkan entri hosts-file ke IP baru, dan coba klik-klik. Lalu hancurkan mesinnya.
# on a throwaway machine, with nothing but the passphrase
export RESTIC_REPOSITORY=sftp:[email protected]:/srv/restic/prod
restic snapshots # can you even see the history?
restic restore latest --target /restore
restic check --read-data-subset=10% # verify stored blocks, not just metadataYang tertangkap oleh latihan ini tidak pernah sesuai dugaan Anda. Bisa jadi file konfigurasi yang ternyata ada di direktori yang tidak tercakup include list. Dump database yang sudah nol byte selama lima minggu karena sebuah password berubah dan skrip-nya tidak pernah memeriksa exit status-nya. Aplikasi yang tidak mau start karena sebuah secret hidup di environment variable yang dipegang orchestrator dan memang tidak pernah ada di filesystem sama sekali. Record DNS yang tidak terdokumentasi. Sertifikat yang harus diterbitkan ulang sebelum ada apa pun yang merespons di port 443. Masing-masing dari itu adalah perbaikan sepuluh menit di sore yang tenang, atau dua jam yang mengerikan jam tiga pagi.
Catat berapa lama latihan ini berlangsung, dari awal sampai akhir. Angka itu — bukan frekuensi backup Anda — adalah recovery time Anda yang sesungguhnya, dan itulah satu-satunya jawaban jujur ketika ada yang bertanya berapa lama Anda akan down. Tambahkan juga restic check --read-data-subset=5% ke timer bulanan: perintah ini membaca dan memverifikasi sampel bergilir dari blok yang benar-benar tersimpan, bukan cuma index-nya, dan begitulah cara Anda menemukan corruption yang diam-diam terjadi selagi masih ada salinan baik untuk jadi cadangan. Kalau suatu saat Anda memindahkan seluruh server ke host baru, restore yang sudah dilatih ini juga berarti sebagian besar migrasinya sudah selesai duluan.
- Inventarisasi apa yang benar-benar tidak bisa dibangun ulang
Sebelum menyentuh tooling apa pun, tulis dulu daftarnya. Susuri mesin itu dan tanyakan untuk setiap direktori: kalau ini lenyap, bisakah saya membuatnya ulang dari package manager, repository git, atau compose file? Kalau ya, direktori itu tidak perlu masuk backup. Yang tersisa biasanya jauh lebih kecil dari yang dikira orang — konfigurasi, data user, state aplikasi, dump database.
# the fast way to find what is actually big and stateful du -x -h -d2 / 2>/dev/null | sort -rh | head -30 docker volume ls # named volumes are state; overlay2 is notUbah hasilnya menjadi dua file eksplisit,
/etc/restic/include.txtdan/etc/restic/exclude.txt. Daftar eksplisit lebih baik daripada ekspresifindyang rumit karena bisa direview, dan karena direktori baru yang muncul di server seharusnya jadi keputusan yang disengaja, bukan ikut ter-include secara diam-diam. - Deploy target backup di yurisdiksi yang berbeda
Pesan VPS kedua di region yang berbeda dari tempat produksi berjalan — kalau produksi ada di Paris, taruh salinannya di Reykjavík atau Bucharest. Intinya supaya tidak ada satu pun kejadian hukum atau fisik yang bisa menjangkau kedua mesin sekaligus. Starter seharga $5/bulan membawa 80 GB NVMe, yang setelah dedup mampu menyimpan histori yang sangat panjang untuk server kecil pada umumnya; siklus billing 12 bulan memotong harganya jadi separuh. Bayar dari saldo kripto yang sama supaya tujuan backup mewarisi anonimitas sumbernya, bukan malah bertentangan dengannya.
Berikan akun yang terpisah dari produksi kalau Anda ingin isolasi kredensial penuh. Lalu harden mesin ini persis seperti mesin lainnya — SSH keys-only, firewall default-deny — dan jangan instal apa pun lagi di atasnya. Nilai mesin ini justru terletak pada kebosanannya: tidak ada web server, tidak ada port terbuka selain SSH, tidak ada apa pun yang bisa dieksploitasi dari internet.
- Buat pintu read-only dari backup host menuju produksi
Ini langkah yang mengubahnya jadi pull. Di host backup, generate key khusus. Lalu pasang bagian publiknya di produksi dengan forced command supaya key itu hanya bisa melakukan satu hal: membaca file. Ia tidak bisa membuka shell, forward port, atau menulis apa pun.
# on the backup host ssh-keygen -t ed25519 -a 100 -f ~/.ssh/pull_prod -C "pull@backup" # on production, in /root/.ssh/authorized_keys — one line command="/usr/bin/rrsync -ro /",restrict ssh-ed25519 AAAAC3Nz… pull@backuprrsyncsudah termasuk dalam paket rsync (/usr/bin/rrsyncdi Debian 13;/usr/share/doc/rsync/scripts/rrsyncdi rilis yang lebih lama) dan-romembuatnya menolak apa pun selain pembacaan.restrictmenonaktifkan port forwarding, agent forwarding, alokasi PTY, dan X11 hanya dengan satu kata. Verifikasi dulu bahwa sangkarnya benar-benar menahan sebelum Anda mengandalkannya —ssh -i ~/.ssh/pull_prod root@productionharus gagal memberi Anda shell. - Dump database di produksi, dengan jadwalnya sendiri
Produksi masih memegang satu pekerjaan: menghasilkan dump yang konsisten ke direktori staging yang nantinya akan diambil oleh pull. Ini tidak butuh kredensial backup sama sekali, dan itulah persis alasan kenapa desain ini berfungsi.
# /usr/local/sbin/dump-db.sh (chmod 700) set -euo pipefail D=/var/backups/db; install -d -m 700 "$D" mariadb-dump --single-transaction --quick --routines --triggers --events \ --all-databases | zstd -T0 > "$D/all.sql.zst.tmp" mv "$D/all.sql.zst.tmp" "$D/all.sql.zst"Perhatikan pola write-to-temp-then-rename ini: artinya pull tidak akan pernah mengambil dump yang setengah tertulis, kapan pun timing-nya terjadi.
set -euo pipefailbukan hiasan — tanpa itu,mariadb-dumpyang gagal akan mengalirkan stream kosong kezstd, yang tetap sukses, dan Anda mendapat file terkompresi yang valid tapi isinya sama sekali kosong. Itulah cara paling umum sebuah backup jadi diam-diam tidak berguna. Jalankan ini dari timer-nya sendiri, setengah jam sebelum pull dijalankan. - Pull data turun ke backup host
Di backup host, rsync-kan include list produksi ke sebuah staging tree. Hanya blok yang berubah yang lewat jaringan, jadi setelah run pertama, proses ini jadi cepat dan murah.
# /usr/local/sbin/pull.sh (chmod 700, runs on the BACKUP host) set -euo pipefail [email protected] # -r is spelled out on purpose: with --files-from, -a does NOT imply # recursion, and without it you silently copy empty directories. rsync -aHAX -r --delete --numeric-ids \ -e "ssh -i /root/.ssh/pull_prod -o BatchMode=yes" \ --files-from=/etc/backup/include.txt \ --exclude-from=/etc/backup/exclude.txt \ "$SRC:/" /srv/staging/prod/-aHAXmenjaga hardlink, ACL, dan extended attribute, dan--numeric-idsmembuat ownership tetap bermakna di antara mesin-mesin yang/etc/passwd-nya berbeda.--deleteaman dipakai di sini justru karena staging bukan backup-nya — histori yang ter-versioning hidup di repository restic yang dibangun di langkah berikutnya, jadi penghapusan yang ikut menjalar ke staging tetap bisa dipulihkan dari snapshot kemarin. - Inisialisasi repository dan simpan key-nya secara offline
Masih di backup host, buat repository restic lokal dan backup staging tree ke dalamnya. Lokal artinya tidak ada jaringan di hot path, tidak ada kredensial remote yang bisa dicuri, dan restore yang berjalan secepat kecepatan disk.
apt install -y restic install -d -m 700 /etc/backup openssl rand -base64 32 > /etc/backup/pass # write this into your password manager NOW chmod 600 /etc/backup/pass export RESTIC_REPOSITORY=/srv/restic/prod export RESTIC_PASSWORD_FILE=/etc/backup/pass restic initSalin passphrase itu ke password manager, dan di sebelahnya tulis path repository dan perintah restore-nya. Lalu buktikan: dari mesin ketiga, hanya memakai apa yang ada di password manager, jalankan
restic -r sftp:backup@…:/srv/restic/prod snapshots. Kalau itu menampilkan daftar snapshot, key-nya benar-benar bisa dipulihkan. Kalau ia butuh sesuatu yang hanya ada di salah satu server, perbaiki itu sekarang, jangan menunggu ditemukan belakangan. - Jadwalkan, dan jadikan kesunyian sebagai alarm
Bungkus pull, run restic, dan prune-nya jadi satu skrip dan jalankan lewat systemd timer. Perhatikan bahwa
forget --pruneaman dijalankan di sini karena backup host memang secara sah memiliki repository-nya — produksi tidak pernah memegang kredensial hapus sama sekali.# tail of /usr/local/sbin/backup.sh restic backup /srv/staging/prod --tag nightly restic forget --keep-daily 7 --keep-weekly 5 --keep-monthly 12 \ --keep-yearly 2 --prune curl -fsS -m 10 --retry 3 https://hc.example.net/ping/<uuid> # only on successsystemctl daemon-reload systemctl enable --now backup.timer systemctl list-timers backup.timer # confirm the next run is where you expectcurlhanya berjalan kalau semua perintah sebelumnya sukses, berkatset -e. Monitor di ujung sana mengharapkan ping harian dan memberi alert kalau tidak menerimanya, yang mengubah job yang diam-diam berhenti menjadi sebuah email, bukan temuan arkeologis.enable --now, bukanstart— timer yang tidak pernah di-enable hanya bertahan sampai reboot pertama. - Jalankan latihan restore, dan catat berapa lama waktunya
Pasang pengingat berulang di mana pun yang benar-benar Anda baca, sekali per kuartal. Deploy Starter sekali pakai, restore ke dalamnya hanya dengan passphrase dan prosedur tertulis, nyalakan aplikasinya, verifikasi bahwa ia benar-benar menyajikan data sungguhan, lalu hancurkan mesinnya. Billing per jam berarti seluruh latihan ini cuma menghabiskan beberapa sen.
restic restore latest --target /restore zstd -dc /restore/var/backups/db/all.sql.zst | mariadb systemctl start nginx app curl -H 'Host: example.com' http://127.0.0.1/healthCatat waktu yang terpakai dan setiap kejutan yang Anda temui, lalu perbaiki kejutan-kejutan itu di skrip backup-nya, bukan cuma di ingatan Anda. Waktu yang terpakai itulah recovery objective Anda yang sesungguhnya; sebelum Anda pernah mengukurnya sekali, angka apa pun yang Anda sebutkan hanyalah tebakan.
Ke mana sebaiknya salinan ini disimpan
| Tujuan | Biaya anonimitas | Biaya uang | Kecepatan restore | Bertahan dari |
|---|---|---|---|---|
| VPS no-KYC kedua, region berbeda | Nihil — dibayar dari saldo kripto yang sama, tidak ada identitas di mana pun | $5/bulan, dipotong separuh pada siklus 12 bulan | Cepat — datacenter-ke-datacenter lewat 1–10 Gbps | Kehilangan akun, kompromi, satu yurisdiksi, kesalahan Anda sendiri |
| Snapshot provider | Nihil | Murah | Hitungan detik | Nyaris tidak ada — akun sama, failure domain sama, ikut mati bersama keduanya |
| Object store (B2 / S3 / Wasabi) | Tinggi — kartu dan identitas ada di akun, dan API key di server Anda mengarah langsung ke sana | Sangat murah untuk disimpan, biaya egress baru terasa saat restore | Cepat, kalau Anda rela membayar tagihan egress-nya | Kompromi dan kehilangan akun, dengan harga berupa nama yang menempel pada mesin |
| Storage provider yang menerima kripto | Rendah, kalau jalur pembayarannya benar-benar bebas identitas — verifikasi, jangan asumsikan begitu saja | Sedang | Sangat bervariasi — periksa batas egress-nya sebelum Anda benar-benar membutuhkannya | Sebagian besar hal, dengan counterparty risk yang lebih tinggi dibanding VPS yang Anda kuasai sendiri |
| NAS rumahan, di-pull lewat WireGuard | Nihil — tidak ada apa pun yang meninggalkan jaringan Anda sendiri dalam keadaan tidak terenkripsi atau bisa diatribusikan | Hardware yang sudah Anda miliki | Lambat — dibatasi oleh kecepatan upload rumah Anda | Segalanya, kecuali rumah Anda sendiri; lemah sebagai satu-satunya salinan, sangat baik sebagai salinan ketiga |
| Tidak ada / “kan sudah ada di git” | Nihil | Gratis | Tidak pernah | Tidak ada apa-apa. Git menyimpan kode Anda; ia tidak menyimpan database Anda, upload Anda, atau /etc Anda |
Pertanyaan yang perlu dijawab
Bukankah snapshot dari provider sudah cukup sebagai backup?
Tidak, dan perbedaannya bukan sekadar mengada-ada. Snapshot hidup di akun yang sama, di bawah kredensial yang sama, dalam failure domain yang sama dengan server yang disalinnya — jadi ia tidak bertahan kalau akunnya hilang, dan siapa pun yang bisa mengakses panel Anda bisa menghapusnya bersama yang asli. Snapshot juga diambil dari mesin yang sedang berjalan, artinya database yang sibuk akan tertangkap di tengah proses penulisan. Pakai snapshot sebagai tombol undo sebelum perubahan berisiko; pakai repository terenkripsi off-site sebagai backup-nya.
restic atau Borg — mana yang sebaiknya saya pakai?
restic, kecuali Anda punya alasan khusus untuk tidak memakainya. Ia mengenkripsi secara default, tidak butuh apa pun terpasang di tujuan, mendukung hampir semua backend yang layak dipakai, dan didistribusikan sebagai satu static binary. Borg dedup dan kompresinya lebih baik dan jauh lebih cepat pada filesystem dengan jutaan file kecil, tapi harus terpasang dan versinya cocok di kedua ujung, dan memang dirancang untuk satu client per repository. Mail spool besar dan kedua mesin di bawah kendali Anda: pakai Borg. Selain itu: pakai restic.
Target backup butuh disk seberapa besar?
Untuk repository yang di-dedup dengan tangga retention di panduan ini — 7 harian, 5 mingguan, 12 bulanan, 2 tahunan — siapkan kira-kira dua sampai tiga kali ukuran data yang benar-benar Anda backup, bukan ukuran seluruh server. Dua puluh enam snapshot tidak berarti dua puluh enam salinan, karena hanya blok yang berubah yang disimpan lagi. Situs kecil dengan 15 GB state sungguhan akan muat dengan nyaman di Starter 80 GB, dengan sisa ruang untuk histori bertahun-tahun.
Bukankah VPS kedua akan menggandakan tagihan hosting saya?
Hanya kalau target backup-nya disamakan dengan produksi, dan memang seharusnya tidak begitu. Ia tidak menjalankan aplikasi apa pun dan tidak melayani traffic apa pun; yang ia butuhkan adalah disk, bukan CPU. Starter $5 di belakang server produksi $30 hanya seperenam dari total tagihan, dan siklus 12 bulan memotongnya lagi 50%. Dibandingkan biaya kehilangan segalanya, ini justru baris termurah di invoice Anda — dan target ini sekaligus jadi mesin latihan untuk restore drill.
Di mana sebaiknya password repository disimpan?
Di password manager yang vault-nya sendiri di-backup di tempat lain, atau di kertas, atau keduanya — dan di sebelahnya, alamat repository serta perintah restore yang persis. Salinan di /etc pada server hanyalah salinan kerja untuk otomatisasi, bukan pernah jadi catatan utama. Verifikasi dengan benar: dari mesin yang bukan produksi maupun backup host, hanya memakai apa yang ada di password manager, jalankan restic snapshots. Kalau ada hal lain yang dibutuhkan, berarti Anda belum punya backup yang benar-benar bisa dipulihkan.
Seberapa sering backup harus dijalankan?
Tanyakan dulu berapa banyak pekerjaan yang rela Anda ulangi. Setiap malam sudah tepat untuk kebanyakan server: paling banyak kehilangan data satu hari, dan satu run semalam mudah dipikirkan logikanya. Database yang sibuk butuh lebih sering — dump database saja setiap jam, sementara file pass penuh tetap setiap malam, adalah pola yang biasa dipakai. Frekuensi di atas itu memberi hasil yang lebih kecil dibanding usaha yang sama kalau dialihkan ke retention yang lebih panjang dan restore drill sungguhan, karena di situlah risiko sebenarnya berada.
Bisakah saya membackup server yang disknya dienkripsi dengan LUKS?
Bisa, dan keduanya saling melengkapi, bukan saling menduplikasi. LUKS melindungi disk selagi mesin mati; backup melindungi Anda dari penghapusan, corruption, dan kehilangan mesinnya sepenuhnya. Backup filesystem yang sudah di-mount persis seperti Anda memback-up yang tidak terenkripsi — restic akan mengenkripsi lagi saat data keluar, jadi repository-nya tetap aman meski di storage yang tidak tepercaya. Jaga kedua rahasia itu di tempat yang benar-benar terpisah: passphrase LUKS yang hanya disimpan di repository restic, atau passphrase restic yang hanya disimpan di dalam volume LUKS, adalah sebuah loop yang gagal tertutup di kedua sisi.
Kalau server saya dikompromikan, apakah backup-nya tetap aman?
Itu sepenuhnya bergantung pada satu keputusan yang sudah Anda ambil berbulan-bulan sebelumnya. Kalau server produksi memegang kredensial dengan hak hapus di repository-nya, maka tidak — attacker akan mendata lalu menghancurkan backup-nya lebih dulu, karena itulah yang mengubah sebuah insiden menjadi negosiasi. Kalau repository-nya append-only, atau backup host yang melakukan pull dan produksi sama sekali tidak memegang kredensial backup, maka histori-nya tetap bertahan dan Anda bisa restore ke snapshot dari sebelum intrusi terjadi. Itulah keseluruhan argumen untuk desain pull, dan alasan untuk memilihnya sebelum Anda benar-benar membutuhkannya.
Keep exploring
Migrasi VPS tanpa downtime
Restore yang sudah dilatih berarti sebagian besar migrasi sudah selesai duluan — ini sisanya, lengkap dengan timing DNS yang sebenarnya menentukan downtime Anda.
Mengenkripsi disk VPS dengan LUKS
Pelengkap dari halaman ini: backup melindungi Anda dari kehilangan data, LUKS melindunginya di disk yang sudah tidak lagi Anda kuasai secara fisik.
Mengamankan VPS baru di jam pertama
Lakukan ini ke target backup sebelum Anda mempercayainya dengan apa pun — SSH keys-only dan firewall default-deny, dalam waktu sekitar satu jam.
Deploy your offshore server.
Pilih wilayah. Pilih paket. Tempel kunci. Bayar. 47 detik berikutnya tanggung jawab kami.