Bayangkan Anda harus merenovasi ruang IGD, tetapi rumah sakit tidak boleh tutup sehari pun. Pasien tetap datang, dokter tetap bekerja, dan semua rekam medis harus tetap utuh. Begitulah rasanya melakukan upgrade SIMGOS di rumah sakit yang berjalan 24 jam. Artikel ini membagikan alur nyata kami saat menaikkan SIMGOS sebuah RSUD dari versi 2.6.11 ke 2.7.9, sekaligus menaikkan PHP 7.4 ke 8.2. Hasilnya, layanan hanya berhenti sekitar 3,5 menit, tidak ada data yang hilang, dan semua patch lokal rumah sakit tetap berfungsi.
Panduan ini cocok untuk tim IT rumah sakit, vendor lokal, maupun admin server yang baru pertama kali memegang SIMGOS. Kami menulisnya bertahap, lengkap dengan perintah yang benar-benar kami jalankan, screenshot asli yang sudah tersensor, serta jebakan yang hampir merusak data.

Kenapa Upgrade SIMGOS Tidak Bisa Asal dnf update
SIMGOS (SIMRS Generik Open Source dari Kemenkes) datang sebagai paket RPM bernama simgos. Sekilas, cukup menjalankan dnf update. Namun, paket ini membawa skrip instalasi yang melakukan jauh lebih banyak hal daripada menyalin file. Oleh karena itu, kita perlu tahu persis apa yang akan terjadi sebelum menekan Enter.
| Bagian | Yang paket kerjakan | Risiko bagi rumah sakit |
|---|---|---|
| File aplikasi | Menimpa semua file milik paket (tanpa file config) | Patch lokal RS hilang tanpa peringatan |
| Database | Menjalankan ratusan file SQL patch secara berurutan | Prosedur cetak lokal tertimpa, data bisa terpotong |
| PHP | Bila masih 7.4, skrip menghapus PHP lalu mengunduh 8.2 | Server tanpa PHP bila unduhan gagal |
| Web server | Mengganti php.conf dan hak akses folder | Tagihan, report, dan BPJS bisa error 503 |
Selain itu, rollback lewat dnf downgrade tidak membatalkan perubahan database. Dengan demikian, satu-satunya jalan pulang adalah backup yang sudah teruji.
Alur Upgrade SIMGOS yang Aman
Kami membagi pekerjaan menjadi dua fase. Fase persiapan berjalan saat SIMGOS tetap melayani pasien. Kemudian fase eksekusi berlangsung singkat di jam sepi, setelah semua uji lolos.

Prinsipnya sederhana. Pertama, pelajari paket tanpa memasangnya. Kedua, amankan data. Ketiga, uji seluruh proses di salinan. Terakhir, jalankan di server asli dengan skrip yang berhenti sendiri bila ada yang tidak beres.
Langkah 1: Cek Versi dan Bongkar Paket Tanpa Memasang
Mula-mula, lihat versi yang terpasang dan versi yang tersedia di repositori. Perintah berikut menampilkan semua build paket simgos.
dnf list --showduplicates simgos rpm -qi simgos | head -5

Selanjutnya, unduh paket target ke folder kerja lalu bongkar isinya. Cara ini tidak mengubah apa pun di server, jadi Anda aman menjalankannya kapan saja.
mkdir -p /root/simgos-upg/rpm /root/simgos-upg/new cd /root/simgos-upg/rpm && dnf download simgos-2.7.9-26101200 cd /root/simgos-upg/new && rpm2cpio ../rpm/simgos-2.7.9*.rpm | cpio -idm # skrip yang akan berjalan saat install, dan file yang dianggap config rpm -qp --scripts ../rpm/simgos-2.7.9*.rpm rpm -qpc ../rpm/simgos-2.7.9*.rpm
Dari langkah ini kami menemukan tiga fakta penting. Paket tidak menandai satu file pun sebagai config. Skrip pascainstalasi memanggil install-rockylinux.sh, yang menjalankan patch database. Lalu, bila PHP masih 7.4, skrip yang sama menjalankan php_74_to_82.sh.
Langkah 2: Backup Database, Aplikasi, dan Dokumen
Backup adalah satu-satunya tiket pulang. Karena itu, kami mengambil backup lengkap dan menyimpannya di tiga tempat: server, folder backup terkunci, dan laptop IT.
B=/backup/simgos-pre-upgrade-$(date +%Y%m%d-%H%M); mkdir -p $B; chmod 700 /backup # database: konsisten, lengkap dengan routine, trigger, event, posisi binlog mysqldump --defaults-file=/root/.my-root.cnf --all-databases --single-transaction \ --routines --triggers --events --hex-blob --source-data=2 | gzip -1 > $B/db-all.sql.gz zcat $B/db-all.sql.gz | tail -1 # harus berisi "Dump completed" # aplikasi, konfigurasi, dan dokumen tar -C /var/www/html -czf $B/production.tar.gz production tar -czf $B/config.tar.gz /etc/httpd /etc/php.ini /etc/php-fpm.d /etc/my.cnf /etc/cron.d rsync -a /var/data/document-storage/ $B/document-storage/ (cd $B && sha256sum *.gz > SHA256SUMS)
Opsi --single-transaction membuat dump konsisten tanpa mengunci tabel InnoDB. Kemudian --routines --triggers --events memastikan prosedur cetak dan trigger ikut tersimpan. Setelah itu, kami memulihkan dump ke salinan terpisah untuk membuktikan backup benar-benar bisa kita pakai. Saat memulihkan, selalu tambahkan mysql --comments. Tanpa opsi itu, komentar # di dalam prosedur ikut hilang.
Langkah 3: Inventaris Patch Lokal dengan rpm -V
Setiap rumah sakit biasanya punya perbaikan sendiri, misalnya validasi stok obat atau format cetak surat. Untungnya, RPM menyimpan sidik asli setiap file, sehingga perintah rpm -V langsung menunjukkan file yang pernah berubah.
rpm -V simgos > /root/simgos-upg/rpmV.txt # flag 5 = isi file berbeda dari paket asli grep '^..5' /root/simgos-upg/rpmV.txt | grep -vE 'data/cache|reports/output|/logs/'
Di server kami, hasilnya 13 file, antara lain app.js, rekammedis.js, dan beberapa file PHP layanan resep. Selain file, kami juga mencatat objek database yang pernah kami ubah, seperti prosedur cetak dan trigger stok opname. Daftar inilah yang nanti kami pasang ulang di atas versi baru.
Langkah 4: Gladi Resik di Salinan Database
Bagian ini paling sering terlewat, padahal justru paling penting. Kami menjalankan MySQL kedua di port 3307 dengan event scheduler mati, lalu memulihkan dump ke sana. Setelah itu, kami menjalankan patch.sh milik vendor apa adanya, hanya port-nya yang kami ganti.
# instance kedua: datadir sendiri, port 3307, event mati mysqld --defaults-file=/root/simgos-upg/rehearsal.cnf --initialize-insecure --user=mysql nohup mysqld --defaults-file=/root/simgos-upg/rehearsal.cnf --user=mysql & zcat $B/db-all.sql.gz | mysql -S /var/lib/mysql-rehearsal/mysql.sock -uroot --comments # patch.sh vendor dengan port 3307 dan tampilan warning cd /root/simgos-upg/gladi/db/patchs sed -i -e 's/^port=3306$/port=3307/' \ -e 's|--comments < $sql|--comments --show-warnings < $sql|' patch.sh bash patch.sh "2.6.11-25121200" > ../../core.log 2>&1
Opsi --show-warnings ternyata menyelamatkan kami. Akibatnya, kami bisa menangkap satu baris Data truncated yang biasanya lewat tanpa suara.

Hasilnya, 880 file SQL selesai dalam sekitar satu menit. Namun, gladi resik juga membongkar beberapa masalah yang kami bahas di bagian Contoh Kasus.
Langkah 5: Pasang Ulang Patch Lokal di Atas Kode Baru
Menyalin file lama ke atas versi baru adalah kesalahan klasik, karena kode vendor ikut mundur. Oleh karena itu, kami memasang ulang setiap perubahan di atas kode 2.7.9 dengan tiga cara berbeda.
- File PHP memakai merge tiga arah: versi asli 2.6.11, versi rumah sakit, dan versi baru 2.7.9.
- File JavaScript minified kami ubah dulu ke bentuk kanonik lewat parser (acorn + astring), lalu setiap patch kami terapkan sebagai aturan cari-ganti dengan jumlah temuan wajib.
- Prosedur database kami ambil dari server lama tepat sebelum upgrade, lalu kami pasang lagi sesudah patch vendor.
# merge tiga arah untuk satu file PHP diff3 -m versi-rs.php versi-asli-2.6.11.php versi-baru-2.7.9.php > hasil.php php -l hasil.php
Untuk JavaScript, pendekatan kanonik penting karena nama variabel berubah di setiap build. Misalnya, variabel t di versi lama bisa menjadi u di versi baru. Karena itu, setiap aturan patch harus menemukan pola yang tepat dan unik. Bila jumlahnya tidak cocok, skrip berhenti dan tidak menulis file apa pun.
Persiapan Hari H: Jadwal dan Komunikasi
Upgrade SIMGOS bukan sekadar urusan server. Rumah sakit tetap buka saat Anda bekerja, jadi koordinasi dengan petugas sama pentingnya dengan perintah di terminal. Berikut checklist yang kami pakai sebelum menekan tombol eksekusi.
- Pilih jam paling sepi. Lihat access log beberapa hari terakhir, lalu cari jam dengan request paling sedikit. Di rumah sakit kami, jam sepi jatuh lewat tengah malam.
- Beri tahu unit yang buka 24 jam. IGD, farmasi, kasir, dan rawat inap perlu tahu kapan SIMGOS berhenti, serta cara mencatat manual sementara.
- Minta persetujuan pimpinan. Downtime di rumah sakit adalah keputusan layanan, bukan keputusan teknis semata.
- Siapkan skrip, bukan ingatan. Semua langkah cutover dan rollback harus tertulis dalam skrip yang sudah lolos gladi resik.
- Tentukan batas waktu. Misalnya, bila uji inti belum lolos dalam satu jam, tim langsung menjalankan rollback.
Dengan checklist ini, petugas tidak kaget saat layanan berhenti. Selain itu, tim IT tidak perlu mengambil keputusan besar di bawah tekanan, karena semua pilihan sudah tersedia sejak awal.
Langkah 6: Cutover dan Uji Asap
Setelah gladi resik lolos, barulah kami menyentuh server produksi. Kami menghentikan cron dan httpd, mematikan event scheduler, lalu mengambil backup final. Kemudian, kami menaikkan PHP lebih dulu dengan cara yang aman.
# PHP 7.4 -> 8.2 dalam SATU transaksi: gagal unduh = tidak ada yang berubah dnf -y module switch-to php:remi-8.2 php -v | head -1 # karena PHP sudah 8.2, skrip vendor melewati php_74_to_82.sh dnf -y install /root/simgos-upg/rpm/simgos-2.7.9-26101200.x86_64.rpm dnf -y install /root/simgos-upg/plugins/*.rpm cat /var/log/simgos-test.txt # build patch DB terakhir mysql --defaults-file=/root/.my-root.cnf --comments < pasca-patch.sql cp -a /root/simgos-upg/final/. /var/www/html/production/webapps/ systemctl restart php-fpm && systemctl start httpd crond
Urutan ini sengaja kami pilih. Perintah module switch-to mengganti 20 paket PHP dalam satu transaksi, sehingga server tidak pernah tertinggal tanpa PHP. Selanjutnya, skrip uji asap memeriksa HTTP, jumlah baris tabel utama, dan error log.

Contoh Kasus 1: Konfigurasi SATUSEHAT Terpotong Diam-diam
Salah satu file patch vendor menambahkan kunci webhook ke konfigurasi SATUSEHAT memakai JSON_MERGE_PATCH. Masalahnya, kolom VALUE hanya varchar(1000). Karena sql_mode server tidak strict, MySQL memotong teks JSON di karakter ke-1000 tanpa error.
-- cek setelah patch: panjang dan validitas JSON SELECT ID, LENGTH(VALUE), JSON_VALID(VALUE) FROM aplikasi.properti_config WHERE ID = 113; -- produksi: 763, 1 salinan setelah patch: 1000, 0
Akibatnya, fitur yang membaca konfigurasi ini akan gagal tanpa pesan. Solusinya, kami menyimpan nilai asli dalam bentuk HEX sebelum patch, lalu mengembalikannya lewat pasca-patch.sql. Pelajarannya: selalu cek JSON_VALID dan panjang kolom setelah patch database.
Contoh Kasus 2: Logo Rumah Sakit Berubah Jadi Logo Vendor
Beberapa menit setelah upgrade, petugas melapor bahwa logo di halaman login berubah menjadi logo rumah sakit lain. Lebih aneh lagi, setiap kali petugas mengunggah logo baru, tampilannya kembali ke logo yang sama.

Ternyata file upload tersimpan dengan benar. Masalahnya ada di fungsi baru ImagePath::resolve() yang menolak nama file bukan string. Sejak PHP 8.1, PDO MySQL mengembalikan kolom angka sebagai integer. Akibatnya, ID faskes terbaca sebagai angka, fungsi menolaknya, lalu aplikasi memakai logo cadangan.
public static function resolve($dir, $nama, $ext) {
// PHP 8.1+: ID dari database berupa int, ubah ke string sebelum diperiksa
if (is_int($nama) && $nama >= 0) $nama = (string) $nama;
if (!is_string($nama) || !is_string($ext) || !isset(self::MIME[$ext])) return null;
// ... validasi nama dan path tetap sama ketatnya
}
Perbaikannya cukup satu baris, dan pengamanan path tetap utuh. Setelah itu, endpoint logo kembali mengirim logo asli rumah sakit.

Jebakan Lain dari Skrip Vendor
Selain dua kasus di atas, gladi resik dan cutover memunculkan beberapa jebakan lain. Berikut ringkasannya, lengkap dengan cara kami menanganinya.
| Jebakan | Akibat | Penanganan |
|---|---|---|
| php.conf baru merutekan ke 6 pool FPM yang tidak pernah terbentuk | Tagihan, report, BPJS error 503 | Pertahankan php.conf lama |
| Vendor menimpa prosedur cetak USG dan surat sehat | Bug lama muncul lagi di hasil cetak | Pasang ulang prosedur lokal |
| Skrip memindahkan file kunci rpmdb | dnf install keluar non-zero walau sukses | Cek versi dan log patch, lalu lanjut |
| Hak akses folder berubah ke 755 | Cache, log, dan report gagal tertulis | Kembalikan mode folder semula |
| Plugin membaca password dari file sementara | Patch plugin keluar diam-diam | Pasang inti dulu, baru plugin |
Kesalahan Umum
- Menjalankan
dnf updatelangsung di produksi tanpa membaca skrip instalasi paket. - Membuat backup tanpa pernah menguji restore-nya.
- Menyalin file lama ke atas versi baru, sehingga perbaikan vendor ikut hilang.
- Mengabaikan warning MySQL, padahal di situlah data terpotong terlihat.
- Menaikkan PHP dengan cara hapus lalu pasang ulang dari internet.
- Tidak menyiapkan skrip rollback sebelum cutover.
- Menganggap upgrade selesai tanpa memantau error log dan laporan petugas di jam ramai.
Kesimpulan
Upgrade SIMGOS yang aman tidak bergantung pada keberuntungan. Kuncinya ada pada urutan kerja: pelajari paket tanpa memasang, backup tiga lokasi, gladi resik di salinan, pasang ulang patch lokal di atas kode baru, lalu cutover dengan skrip yang berhenti sendiri. Dengan alur ini, kami melompati enam rilis sekaligus dengan downtime sekitar 3,5 menit dan tanpa kehilangan satu baris data pun.
Terakhir, catat setiap jebakan yang Anda temukan. Upgrade berikutnya pasti datang, dan catatan hari ini akan memangkas pekerjaan Anda menjadi separuhnya.
Baca Juga
- Migrasi Server ke Proxmox: Checklist Cutover VPS ke LXC Tanpa Downtime
- Backup Server Otomatis: Database & File Tiap Jam, Salinan Off-site, dan Watchdog
- Upgrade Proxmox 7 ke 9: Langkah 7.4 → 8.4 → 9.2 dan Solusi Kendalanya
Referensi: MySQL 8.0: mysqldump, RPM: rpm(8) verify, DNF: module command, PHP 8.1: perubahan tidak kompatibel.

