Skip to content

Production Promotion Toolkit

By Romi Fadli, S.Kom. 10 min read Environments & Deployment
On this page
A staging configuration with an orange banner and a production configuration with a green banner; ordinary keys cross over with arrows, while a locked secret is stopped halfway by a red cross and production keeps its own lock

Production Promotion Toolkit adalah tiga perintah yang membawa rilis yang sudah teruji ke production tanpa pernah menimpa apa yang menjadi milik production. Satu perintah membangun environment production pertama kali; satu mempromosikan konfigurasi tiap rilis dari staging; satu lagi menerapkan perubahan skema database yang dibawa image rilis itu. Bersama-sama, satu rilis bisa mengubah apa yang memang perlu diubahnya, dan tak lebih.

Mempromosikan rilis tampak seperti menyalin file dari staging ke production. Kenyataannya tidak, karena kedua environment berbagi strukturnya tapi tidak berbagi rahasia maupun alamatnya — dan salah satu rahasia itu adalah kunci yang membuat data terenkripsi bertahun-tahun tetap terbaca.

Sekilas

Toolsgen-production-env, promote-to-production, migrate-ca
BentukScript command-line yang berbagi satu library pembantu
BahasaBash, dengan Python tertanam untuk perbandingan per kunci pada JSON bersarang, yang mengendalikan Docker dan rsync
StatusStabil, berjalan di production
Kode sumberPrivat
Apa toolkit ini, dibangun dengan apa, dan keadaannya.

Yang dikerjakannya

A. Membangun production satu kali

Environment production pertama dibangun dari template development, dengan setiap alamat ditulis ulang untuk network milik production, yang dibaca dari state jaringan yang tercatat tanpa fallback — fallback lama menunjuk ke subnet yang sudah tak ada, dan production akan lahir di sana tanpa satu peringatan pun. Penulisan ulangnya mencocokkan teks secara literal dan menolak selesai bila masih ada alamat development tersisa. Generator bisa menyalin folder data, tapi hanya dari sumber yang sedang berhenti; prosedur yang saya ikuti mengisi production lewat toolkit backup. Setiap modul mendapat nama project dan path data milik production sendiri, jadi menyalakannya tak bisa menggantikan container development, dan network-nya harus sudah ada sebelum apa pun ditulis. Konfigurasi yang dibaca certificate authority dibuat bisa dibaca user di dalam image-nya, dan sertifikat CA serta daftar pencabutannya tetap tiba walau data dilewati. Generator sengaja tidak menyalakan stack — menyalakan production adalah keputusan operator — dan mencetak dua langkah yang wajib menyusul, salah satunya membuat user telemetry lewat mode tersendiri. Begitu production ada, ia tak pernah dibangun ulang; sejak saat itu ia hanya berubah lewat promosi.

B. Mempromosikan struktur rilis, bukan nilainya

Untuk setiap service, promosi membandingkan konfigurasi staging dengan production, kunci demi kunci, baik di file environment maupun JSON bersarang. Kunci yang ditambahkan ikut ditambahkan, kunci yang dihapus ikut dihapus, dan nilai non-sensitif yang berubah — batas, timeout, feature flag — diperbarui. Rahasia dan alamat tetap memakai nilai milik production. Kunci yang terhapus dibuang per baris, jadi komentar dan urutannya selamat. Versi image tiap modul ikut dipromosikan, tapi dari file compose hanya baris image: yang pindah, tak pernah file utuhnya: production pernah tertinggal di nama image lama yang baru ketahuan ketika migration gagal, dan file utuh membawa fallback alamat per environment yang tak boleh menyeberang. Di JSON, label deployment environment dihitung sebagai milik production, jadi telemetry production tak pernah berlabel development lalu bercampur dengannya. Alamat dikenali lewat pola nama, termasuk bentuk tunggalnya, setelah satu alamat internal pernah diperlakukan sebagai nilai biasa dan membawa alamat development ke production. Kredensial storage dijaga tetap sama di setiap file yang memuatnya, setelah gambar yang rusak di production ternyata error signature dengan setiap objek masih utuh.

C. Menolak menyalin rahasia

Ketika staging punya rahasia yang belum dimiliki production, promosi tidak menyalinnya. Semua perubahan lain tetap diterapkan, tapi run itu menyebut setiap rahasia yang diblokir dan diakhiri dengan kegagalan sampai ada orang yang menetapkan nilai milik production dengan tangan. Kunci alamat baru disalin, diperingatkan di tempatnya muncul, lalu didaftar ulang di ringkasan, karena peringatan di tengah output panjang mudah terlewat — satu base URL baru pernah membawa alamat development ke production dan halaman status aplikasi menjawab 503. Modul yang ada di staging tapi belum ada di production dilaporkan dan mengakhiri run dengan kode berbeda: promosi tak membuat service, dan dulu ia pernah menulis kunci untuk service yang tak dimiliki production lalu menyebut hasilnya sudah selaras. Rahasia yang diblokir lebih diutamakan daripada modul yang hilang, dan ringkasannya membedakan "tak ada perubahan" dari "tak ada perubahan, tapi ada modul yang hilang".

D. Menerapkan perubahan skema yang dibawa image

Sebagian service tak memigrasi database-nya sendiri saat menyala; skemanya dibuat oleh init script yang hanya dijalankan database ke data directory yang masih kosong. Begitu environment berisi data hasil restore, script-script itu tak pernah jalan lagi. Perintah migration menerapkan setiap migration yang tertunda secara eksplisit, mengambil migration-nya dari dalam image service, dan mencatatnya di tabel migration yang memang sudah dipakai service itu, dengan versi yang diambil dari prefix timestamp tiap nama file, sama seperti migrator milik service itu sendiri. Tiap migration berjalan sebagai satu transaksi yang berhenti di error pertama, jadi tak ada yang setengah diterapkan, dan satu modul berhenti di migration gagal pertamanya karena migration sesudahnya biasanya bergantung padanya. Prefix container dibaca dari file environment, bukan ditebak dari nama folder. Database-nya harus sudah berjalan, sementara container aplikasi cukup ada, karena yang dibutuhkan hanya file-nya. Service yang image-nya tak membawa migration dilewati, dengan permintaan agar image-nya di-build ulang, dan run menghitungnya sebagai kegagalan.

E. Memperlihatkan setiap perubahan sebelum melakukannya

Promosi maupun migration punya dry run yang mencetak persis apa yang akan berubah tanpa mengubah apa pun; dry run migration bahkan tak membuat tabel pencatatnya. Setiap file compose, file environment, dan konfigurasi JSON yang ditulis ulang promosi di-backup lebih dulu, di samping aslinya, dengan timestamp. Di akhir ia mencetak langkah berikutnya, ditutup dengan menandatangani satu dokumen, karena container yang hijau tak membuktikan penandatanganan bekerja.

Cara saya memakainya

bash
promote-to-production --from staging/2.12.0 --dry-run    # always first
promote-to-production --from staging/2.12.0
cd production && bash up.sh
migrate-ca --target-dir production --dry-run
migrate-ca --target-dir production
health-check --dir production
Jenis kunciContohYang dilakukan promosi
Rahasiakey aplikasi, password database, private key, tokenmempertahankan nilai production; rahasia baru memblokir promosi
Alamathost database, URL service, origin, trusted proxymempertahankan nilai production; alamat baru disalin dan didaftar ulang untuk diperiksa
Strukturkunci baru atau terhapus, rate limit, masa berlaku, feature flagdipromosikan
Bagaimana tiap jenis kunci konfigurasi diperlakukan.
Exit codeArti
0production sudah selaras dengan rilisnya
1setidaknya satu rahasia baru diblokir dan harus ditetapkan dengan tangan
2ada service di staging yang belum ada di production; promosi tak membuat service baru. Bila ada rahasia yang juga diblokir, 1 yang menang
Cara promosi melaporkan hasilnya.

Keputusan rancangan

Ia mempromosikan struktur, karena nilainyalah yang membuat production menjadi production. Ketika saya membandingkan dua environment milik satu service, keduanya punya 136 kunci yang persis sama, dan 21 di antaranya wajib tetap bernilai berbeda. Menyalin file-nya akan memberi production password database milik staging, alamat milik staging, dan — paling parah — application key milik staging, yang mendekripsi kolom-kolom yang bertahun-tahun lalu dienkripsi dengan kunci milik production. Karena itu promosi hanya memindahkan apa yang sah diubah oleh rilis, memahami konfigurasi datar maupun bersarang, dan memperlihatkan diff-nya lewat dry run sebelum menulis apa pun, dengan backup untuk setiap file yang disentuhnya.

Ia tak pernah menyalin rahasia, sekalipun itu praktis. Rahasia yang hanya ada di staging adalah kredensial baru yang dibutuhkan rilis. Menyalinnya membuat staging dan production berbagi kredensial, sehingga kebocoran dari environment yang penjagaannya lebih longgar langsung menjadi kebocoran production. Promosi mengenali rahasia lewat daftar eksplisit dan pola nama, menolak membawanya menyeberang, lalu gagal dengan suara keras supaya keputusannya diambil oleh orang, sekali, dengan sengaja. Daftar itu dijaga selaras dengan daftar yang dirotasi tool rotasi, jadi kunci bernama tak lazim, seperti kunci enkripsi aplikasi, ikut tertangkap.

Migration ikut di dalam image, jadi server tak butuh apa pun selain image-nya. Kegagalan yang dicegahnya senyap: service dengan skema usang tetap menyala, lolos health check di kolom-kolom lama, dan baru gagal ketika ada request yang menyentuh kolom baru. Mengambil migration dari dalam image berarti migration selalu cocok dengan biner yang memakainya, dan server tanpa source code pun tetap bisa menerapkannya. Mencatatnya di tabel migration yang sudah dimiliki service, bukan tabel buatan saya sendiri, menjaga satu riwayat saja dan membuat menjalankan ulang perintahnya tak berbahaya.

Production dibangun sekali, dengan hati-hati, dan tak pernah lagi. Bootstrap menolak menyalin data dari sumber yang container-nya masih berjalan, karena menyalin file database yang hidup berisiko menghasilkan salinan korup; jalur amannya, dan jalur yang saya pakai, membangun konfigurasi saja, lalu mengisi data lewat backup dan restore. Ia tak pernah menyalakan stack sendiri, dan ia menyebut langkah telemetry sebagai wajib, karena ketika langkah itu pernah terlewat setiap service tetap sehat sementara dua hari log, trace, dan error aplikasi terbuang. Sesudah hari pertama itu, production hanya berubah lewat promosi, jadi setiap perubahannya sudah melewati staging dan dry run.

Spesifikasi

MasukanVersi staging yang dipromosikan; opsional tujuan lain dan satu service saja
KeluaranKonfigurasi dan versi image production yang diperbarui, backup bertimestamp untuk setiap file yang berubah, dan migration yang diterapkan serta tercatat di tabel migration milik service
FormatFile environment dan konfigurasi JSON bersarang, dibandingkan kunci demi kunci
KeamananDry run untuk promosi maupun migration; rahasia dan alamat dipertahankan; rahasia baru tak pernah disalin; backup sebelum menulis ulang konfigurasi; tak ada data yang disalin dari sumber yang sedang berjalan; migration dalam satu transaksi
PrasyaratDocker, rsync, dan Python 3 di host; migration ikut di dalam image service
Masukan, keluaran, dan jaminan toolkit ini.

Terkait

Cerita tentang kunci yang mendekripsi production, dan kenapa konfigurasi dipromosikan berdasarkan strukturnya.

Prosedur rilis tempat perintah-perintah ini menjadi langkah promosi dan migration.

Setiap script mengikuti bentuk yang saya berikan pada setiap tool kecil, dalam varian Bash-nya.

Toolkit yang mengisi environment production baru dengan data pertamanya.

Toolkit yang membangun environment staging, titik berangkat setiap promosi.

Toolkit yang memberi production network-nya sebelum bootstrap.

Kenapa daftar rahasia di sini dijaga selaras dengan daftar yang dirotasi.

Kenapa label environment di telemetry tetap milik production.

Istilah

Promosi
Memindahkan rilis yang lolos pengujian dari satu environment ke environment berikutnya, di sini dari staging ke production.
Rahasia
Nilai konfigurasi yang memberi akses atau mendekripsi data, seperti password, key, atau token.
Dry run
Menjalankan perintah supaya ia melaporkan apa yang akan berubah tanpa mengubah apa pun.
Migration skema
Perubahan struktur database yang berversi, diterapkan berurutan dan dicatat supaya hanya berjalan sekali.
Init script
Script yang dijalankan container database saat menyala dengan data directory kosong, dan tak pernah lagi.
Bootstrap
Membuat environment untuk pertama kali, sebelum proses rutin mana pun bisa merawatnya.

Technologies covered

  • Bash
  • Docker

About the author

Photo of Romi Fadli

Romi Fadli, S.Kom.

Solution Architect · Backend Engineer · Embedded Systems

In my day job I work as a solution architect. I design the system, lead the team that builds it — quality assurance, backend developers, a UI developer, DevOps and DevSecOps — and write the most critical modules myself, such as a certificate authority, a long-term PDF signer, and the core API of a personnel system.

I work across the whole stack that a running system actually needs. On the hardware side that means PCB design, microcontrollers, and embedded firmware — including RISC-V boards running FreeRTOS and Embassy, speaking MQTT over mutually authenticated TLS on a microcontroller with 64 KB of SRAM.

Most of my day-to-day work is software: Rust backends and API modules built on Clean Architecture, an OAuth2/OpenID Connect authorization server written from scratch, PAdES document signing, and certificate authorities — root CAs, timestamping authorities, and OCSP responders. I also build the frontends that sit on top of them.

Running all of that means infrastructure: Docker Compose stacks, networking, Mikrotik and RADIUS, observability with OpenTelemetry, and a steady habit of writing command-line tools and utilities for Linux when the existing ones do not fit.

Share

Enjoyed this?

Get an email when a new article is published.

Referenced by

A document's fingerprint passing through a clock, signed by a certificate below it, and returning as a time seal stamped on the same document

Timestamp authority berbahasa Rust yang menstempel hash dokumen dengan waktu bertanda tangan berbasis NTP sesuai RFC 3161, supaya tanda tangan PDF tetap bisa diverifikasi setelah sertifikatnya kedaluwarsa — menerbitkan sendiri sertifikat penanda tangannya, dengan OCSP dan CRL publik.

9 min read Read more →
A chain of three certificates, root to issuing CA to signer, signing a PDF, while an OCSP answer and a revocation list from the issuing CA reach a validator shield alongside the document

Issuing certificate authority berbahasa Rust yang memberi tiap penanda tangan PDF sertifikatnya sendiri, lalu menjawab pertanyaan setiap pembaca — masih sahkah? — lewat OCSP responder dan daftar pencabutan publik, serta menyerahkan key dan rantainya sebagai PKCS#12.

11 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
A purple root circle linked to a blue and an orange intermediate circle, which lead to a signed document with a green seal and to a clock.

Aurograf

DevOps

Platform tanda tangan digital Pemerintah Kota Tangerang Selatan: penanda tangan membuktikan identitasnya, menandatangani PDF hingga PAdES B-LTA dengan sertifikat dari otoritas milik platform sendiri, dan siapa pun bisa memverifikasinya — versi 2, yang saya rancang, bangun, dan operasikan.

10 min read Read more →

Related Articles

A stack of Markdown documents and an orange arrow leading to a browser page with a side menu, a heading, paragraphs, a dark code panel, and a table of contents with its active entry highlighted

Satu script yang mengubah dokumentasi Markdown satu project menjadi situs statis yang terbuka dari folder tanpa server maupun internet: menu, daftar isi yang mengikuti pembaca, tabel yang tak pernah terpotong, kode dengan tombol salin, dan tautan yang tetap berfungsi.

7 min read Read more →
An old layout with a grey banner and a new structure with a green banner, each holding a blue database, a purple document store, and an orange telemetry store; three coloured paths pass through one export package marked with a green check

Empat perintah yang memindahkan stack dari layout lama ke struktur baru melintasi versi mayor: export tanpa mengubah data sumber, import tiap store sesuai cara yang diizinkan versinya, verifikasi apa yang benar-benar tiba, dan rename modul sambil memindahkan datanya.

11 min read Read more →
Three image layer stacks, each with a version tag of its own colour, blue, orange, and green, each leading to an archive file banded in the same colour

Perintah yang memberi versi tersendiri pada tiap modul stack, mem-build image-nya di bawah tag yang berarti tepat satu build, menolak memakai ulang versi atau menelan compile yang gagal, dan mengarsipkan image menurut nama dan tag supaya versi tak saling menimpa.

7 min read Read more →