Backup Database Otomatis ke Cloudflare R2 dengan Rclone dan Systemd Timer di VPS Ubuntu 24.04

Backup yang hanya tinggal di mesin yang sama dengan database-nya tidak menyelesaikan banyak kasus bencana: disk rusak, ransomware mengenkripsi seluruh volume, atau akun server kena lupa membayar. Pindah ke object storage di lokasi berbeda adalah minimum standar, dan Cloudflare R2 menarik untuk peran itu karena kompatibel dengan API S3 dan tidak menarik biaya egress saat restore. Artikel ini menyusun jalur lengkapnya di Ubuntu 24.04: dump database, enkripsi di sisi klien dengan rclone crypt, jadwal systemd timer, rotasi otomatis, dan uji restore yang sebenarnya menghitung.

Mengapa Enkripsi Di Sisi Klien Dulu

Object storage mengenkripsi data saat tersimpan (encryption at rest), tetapi kunci itu dikelola penyedia. Untuk backup database yang berisi data pelanggan, lapisan yang berarti adalah enkripsi sebelum data keluar dari server, sehingga kredensial storage yang bocor hanya memberi akses ke berkas terenkripsi. Rclone menyediakan ini lewat remote crypt: satu remote pembungkus yang mengenkripsi nama berkas dan isinya sebelum meneruskan ke remote S3/R2 di bawahnya. Kunci enkripsi tinggal di server dan di tempat aman terpisah; kehilangan kunci itu berarti backup tidak bisa dibuka, jadi perlakukan kuncinya seperti database itu sendiri.

Konfigurasi Rclone dan R2

Pasang rclone, siapkan kredensial R2 dari dashboard Cloudflare (buat API token untuk R2 dengan scope Object Read and Write, catat Access Key ID, Secret, dan Account ID), lalu definisikan dua remote di /root/.config/rclone/rclone.conf:

[r2]
type = s3
provider = Cloudflare
access_key_id = AKID-CONTOH
secret_access_key = SECRET-CONTOH
endpoint = https://ACCOUNT-ID.r2.cloudflarestorage.com

[backupcrypt]
type = crypt
remote = r2:db-backup
password = HASH-CONTOH
password2 = HASH2-CONTOH

Password untuk crypt dihasilkan dengan rclone config generate, bukan dikarang sendiri. Uji dua arah sebelum melangkah jauh:

rclone mkdir backupcrypt:u24-db
echo uji | rclone rcat backupcrypt:u24-db/uji.txt
rclone cat backupcrypt:u24-db/uji.txt

rclone ls r2:db-backup harus menampilkan nama berkas yang sudah tak terbaca, sementara rclone cat lewat remote crypt mengembalikan isi aslinya. Itu tanda lapisan bekerja dua-duanya.

Dump Tanpa Berkas Perantara

Untuk PostgreSQL, dump format custom diteruskan langsung ke remote lewat rclone rcat, jadi tidak ada dump mentah yang sempat hinggap di disk:

pg_dump -Fc appdb | rclone rcat backupcrypt:u24-db/appdb-$(date +%F).dump

Untuk MySQL/MariaDB, polanya sama dengan mysqldump –single-transaction –routines –triggers appdb | gzip | rclone rcat …. Satu sesi pemipaan ini sekaligus menghindari pola skrip shell yang menulis dump ke /tmp dulu, yang jadi sumber kebocoran data klasik di server berbagi.

Unit Systemd dan Timer

Jalankan backup sebagai unit systemd, bukan cron, supaya log, batas sumber daya, dan status terpusat di satu tempat. Berkas /etc/systemd/system/db-backup.service:

[Service]
Type=oneshot
User=postgres
ExecStart=/bin/sh -c ‘pg_dump -Fc appdb | rclone rcat backupcrypt:u24-db/appdb-$(date +%%F).dump’
Nice=10

Perhatikan %%: systemd membaca % tunggal sebagai specifier sendiri, jadi penulisan tanggal di dalam unit selalu butuh persen ganda. Timer di /etc/systemd/system/db-backup.timer:

[Timer]
OnCalendar=*-*-* 02:30:00
RandomizedDelaySec=15m
Persistent=true

[Install]
WantedBy=timers.target

Aktifkan dengan systemctl enable –now db-backup.timer lalu cek jadwal berikutnya di systemctl list-timers db-backup.timer. Persistent=true menjalankan backup yang terlewat saat server mati pada jadwalnya, misalnya akibat reboot pagi buta.

Rotasi dan Aturan Retensi

Backup tanpa rotasi berubah jadi tagihan storage bulanan yang tumbuh diam-diam. Jalankan pembersihan usia sebagai unit terpisah yang menempel di service yang sama dengan ExecStartPost= atau timer kedua, dengan rclone delete backupcrypt:u24-db –min-age 30d untuk kebijakan 30 hari. Backup mingguan jangka panjang lebih aman dipindahkan ke prefix terpisah dengan retensi bulanan, sehingga kesalahan pembersihan tidak menghapus semuanya sekaligus.

Kejadian yang lebih sering daripada backup gagal adalah backup yang gagal diam-diam: timer mati, remote berubah, atau kuota bucket penuh, dan tidak ada yang beritahu sampai malam saat restore dibutuhkan. Tambahkan pemeriksaan yang tidak bisa dibohongi oleh timer yang sehat, misalnya job harian yang menghitung berkas baru sejak hari sebelumnya lewat rclone lsl backupcrypt:u24-db –max-age 25h | wc -l dan menolak melanjutkan bila hasilnya nol, lalu laporkan hasil pemeriksaan itu ke saluran yang sama dengan monitor server Anda. Restore yang ditunda karena asumsi semuanya jalan adalah kegagalan yang paling mudah dicegah dengan satu baris pengecekan.

Uji yang Benar-Benar Menghitung: Restore

Nilai backup hanya ada di kemampuan memulihkannya. Jadwal verifikasi bulanan yang layak: unduh dump terbaru dengan rclone cat backupcrypt:u24-db/appdb-2026-11-13.dump > /tmp/uji.dump, restore ke database sementara dengan pg_restore -d uji_restore –clean /tmp/uji.dump, lalu bandingkan jumlah baris tabel kunci dan cek pg_restore –list untuk memastikan objek lengkap. Hapus database uji dan berkas lokal setelah selesai. Backup yang lolos tiga bulan tanpa satu kali restore teruji adalah cadangan yang statusnya belum lebih baik dari harapan.

Referensi

  1. Dokumentasi rclone crypt: https://rclone.org/crypt/
  2. Cloudflare R2, kompatibilitas API S3 dan pembuatan token: https://developers.cloudflare.com/r2/
  3. Dokumentasi pg_dump dan pg_restore PostgreSQL: https://www.postgresql.org/docs/current/app-pgdump.html
  4. Dokumentasi systemd.timer termasuk Persistent: https://www.freedesktop.org/software/systemd/man/latest/systemd.timer.html