Migrasi MediaCMS v3 ke v8: 6 Jebakan Upgrade Lintas 2,5 Tahun
Ada satu server video kami yang jalan 984 hari tanpa pernah di-upgrade. Waktu dibongkar, isinya MediaCMS rilis November 2023, PostgreSQL 14, dan tiga patch keamanan yang sudah ketinggalan belasan versi. Proyek minggu ini: pindahkan semuanya ke Docker dengan MediaCMS v8.4.0 + PostgreSQL 17.2, tanpa kehilangan satu file pun. Yang menarik bukan MediaCMS-nya โ tapi kenapa cutover bisa terlihat sukses padahal servernya belum benar-benar pindah.
Kenapa Baru Sekarang Dipindah
Alasan bertahan selama ini masuk akal: server lama bisa snapshot manual kapan saja, sedangkan server baru backup-nya otomatis cuma sekali sehari. Setelah ditimbang, ternyata backup mandiri (dump database + media ke object storage) lebih penting daripada snapshot VM. Ditambah tiga hal ini:
- 1 vCPU / 1 GB RAM seharga $32 per bulan โ mahal untuk ukuran 2026.
- Versi app ketinggalan ยฑ2,5 tahun โ v3.1.0 vs upstream v8.4.0.
- Patch keamanan yang kelewat: path traversal di nginx (v8.3.2), akses media/playlist privat (v8.3.3), dan subtitle (v8.3.5).
Hasil Akhir (angka nyata, bukan proyeksi)
| Item | Server lama | Server baru |
|---|---|---|
| Versi MediaCMS | v3.1.0 (2023) | v8.4.0 |
| Database | PostgreSQL 14 | PostgreSQL 17.2 |
| Migrations | 35 | 72 |
| Tabel | 38 | 60 |
| Video / User | 17 / 7 | 17 / 7 |
| Media | 5,4 GB | 5,4 GB (4.175 file) |
| Container | systemd (5 service) | 6 Docker container |
| Uptime server | 984 hari | 31 hari |
Domain tetap sama, SSL tetap di belakang Cloudflare, dan jumlah baris datanya identik โ itu bukti yang kami pakai untuk bilang migrasi ini bersih.
Alur 7 Fase
- Inventaris read-only โ versi, ukuran media, jumlah user/video, daftar service.
- Backup di server lama โ
pg_dump -Fc+ SQL gzip +media_files.tar.gz+ checksum SHA256. - Transfer ditarik dari sisi tujuan (
rsync -avP --partial --inplace), bukan didorong dari server lama โ lalu diverifikasisha256sum -c. - Deploy Docker fresh SEBELUM import โ port app digeser, data dir database dikeluarkan dari folder repo, env DB/Redis dipasang ke semua service.
- Import data lama โ
pg_restore --no-owner --no-privileges, lanjutmanage.py migratesebagai jembatan skema v3 โ v8. - Cutover domain โ hostname ditambah ke tunnel, DNS A diganti CNAME.
- Watchdog 24 jam โ cek tiap 4 jam, lapor otomatis.
6 Jebakan yang Bikin Migrasi Kelihatan Sukses Padahal Belum
1. DNS masih menunjuk server lama
Ini yang paling menipu. Web balas HTTP 200, halaman tampil normal, semua orang senang โ padahal request-nya masih dilayani server lama. Cutover belum terjadi. Bukti yang sah cuma satu: log container di server baru menunjukkan request masuk, bukan angka 200 dari luar.
2. migrate exit 0 walau semua query gagal
Container selesai dengan status sukses, docker compose ps hijau, tapi database-nya kosong karena query-nya gagal semua. Exit code bukan bukti. Verifikasinya harus query nyata ke database: hitung barisnya, bandingkan dengan sumber.
3. Healthcheck PostgreSQL false positive
pg_isready cuma mengecek socket, bukan apakah klaster benar-benar melayani. Kalau kepemilikan folder data salah, Postgres bisa mati sementara statusnya tetap healthy. Healthcheck yang tidak menyentuh data = hiasan.
4. Image mendeklarasikan VOLUME โ chown ditimpa
Image aplikasi mendeklarasikan VOLUME di Dockerfile. Akibatnya setiap kali container start, Docker membuat ulang direktori mount dengan uid 33 dan menimpa chown yang sudah kita set. Solusinya: folder data database wajib di luar folder repo. Detail perilaku ini memang dokumentasi resmi Docker, tapi efeknya baru terasa saat produksi.
5. role "postgres" does not exist โ ini normal
Muncul di log, kelihatan seram, padahal cuma artinya klaster memakai user yang kita set di environment sebagai superuser. Jangan panik dan jangan “perbaiki” dengan membuat role baru.
6. URL media yang benar bukan yang kelihatan logis
Path media yang benar punya segmen original di tengahnya. Menebak URL dari struktur folder = 404 yang bikin kita mengira data media tidak ikut tercopy.
Yang Bisa Dipakai Siapa Saja
Naik lintas major version (Postgres 14 โ 17) itu selalu lewat dump + restore ke folder data baru, bukan upgrade di tempat pada folder lama. Dan yang lebih penting: jangan pernah percaya satu sinyal saja. Exit code boleh bohong, status container boleh hijau, healthcheck boleh salah โ pola ini kami bahas juga di artikel Cron Job Bilang OK Tapi Bohong. Verifikasi harus dari sisi yang benar-benar melayani request.
Kalau setup-nya melibatkan tunnel dan domain, langkah cutover-nya mirip dengan panduan Cloudflare Tunnel. Intinya sama: yang menentukan berhasil atau tidak adalah apa yang melayani request, bukan apa yang tertulis di dashboard.
Kami masih menunggu watchdog 24 jam dan baru menghapus server lama setelah semua cek hijau. Ada pengalaman serupa soal migrasi lintas versi? Ceritakan di komentar.
Sumber: MediaCMS ยท Dokumentasi VOLUME Docker
โ Chokdi ๐ท ยท Content Studio ยท 2026