- Railway meluncurkan Railpack, sistem build baru yang menggantikan Nixpacks yang ada
- Railpack menawarkan kemampuan yang lebih unggul dibanding Nixpacks dalam hal granularitas versioning, ukuran image yang lebih kecil, dan caching yang lebih baik
- Metode versioning berbasis commit pada Nixpacks menunjukkan keterbatasan dalam memenuhi berbagai kebutuhan pengguna dan skalabilitas
- Railpack meningkatkan stabilitas dan fleksibilitas lingkungan build melalui integrasi BuildKit, perlindungan secret environment variable, serta dukungan untuk berbagai bahasa dan framework
- Saat ini mendukung Node, Python, Go, PHP, HTML statis, dan terus memperluas dukungan framework serta bahasa
Ikhtisar dan latar belakang
- Railway memperkenalkan Railpack sebagai sistem build generasi berikutnya
- Railpack adalah alat baru yang dikembangkan berdasarkan pengalaman membangun lebih dari 14 juta aplikasi di platform Railway menggunakan Nixpacks
- Nixpacks cocok untuk 80% pengguna, tetapi lebih dari 200 ribu pengguna mengalami ketidaknyamanan karena terbentur keterbatasan
- Railway menilai bahwa peningkatan besar diperlukan untuk memperluas basis pengguna dan menciptakan lingkungan build yang berkelanjutan
Peningkatan utama Railpack
- Granularitas versioning: Mendukung penentuan versi yang detail hingga tingkat
major.minor.patch untuk tiap paket, mengatasi keterbatasan pendekatan versi Nix yang kurang jelas
- Ukuran image lebih kecil: Mengurangi ukuran image build dasar hingga 38% untuk Node dan 77% untuk Python, sehingga memberikan pengalaman deployment yang lebih cepat
- Caching yang ditingkatkan: Terintegrasi langsung dengan BuildKit untuk mengontrol layer dan file system, meningkatkan cache hit rate dan memungkinkan berbagi cache antar environment
- Build Railpack sudah diterapkan di railway.com dan layanan pusat
Masalah dalam penggunaan Nixpacks
- Metode manajemen versi paket di Nix menggunakan struktur berbasis commit, hanya menyediakan major version terbaru, dan tiap versi dipetakan ke commit tertentu di repositori nixpkgs
- Ada inefisiensi karena semua versi patch kecil harus dikelola secara manual, dan versioning juga tidak intuitif bagi kontributor sehingga menurunkan aksesibilitas
- Untuk bahasa seperti Node atau Python pun, pada akhirnya hanya major version terbaru yang didukung
- Saat versi diperbarui, perubahan hash commit dapat mempengaruhi versi paket lain sekaligus, sehingga menurunkan keandalan bagi pengguna dan bisa memicu kegagalan build yang tak terduga
- Pada Nixpacks, semua dependensi dimasukkan ke dalam satu layer
/nix/store, sehingga image sulit dipecah secara efektif atau diperkecil ukurannya
- Caching juga tidak dapat dimanfaatkan dengan baik karena layer selalu ter-invalidate setiap kali environment variable disuntikkan
Bukan masalah pada Nix itu sendiri, melainkan keterbatasan cara penggunaannya
- Ini bukan masalah desain Nix itu sendiri, melainkan masalah pada cara Railway menggunakan dan mengabstraksikannya
- Railway berusaha merancang agar pengguna tidak perlu memahami konsep derivation di Nix atau struktur versi internalnya, tetapi akhirnya menilai hal itu tidak realistis
- Untuk menyelesaikan masalah-masalah tersebut, Railway mengembangkan Railpack
Arsitektur teknis Railpack
- Perubahan codebase dari Rust ke Go: Beralih ke Go untuk memanfaatkan BuildKit dan memperkuat kemampuan dalam merespons ekosistem
- BuildKit LLB dan frontend: Dengan langsung menghasilkan custom BuildKit LLB dan frontend, struktur image build dapat dikendalikan secara presisi → image dasar Node dan Python menjadi jauh lebih ringan dibanding Nixpacks
- Manajemen versi dengan Mise: Menggunakan Mise untuk instalasi paket dan resolusi versi, sehingga ke depan lebih mudah mendukung sumber executable lain
- Jika build berhasil, lock-in dependensi pada titik waktu tersebut diterapkan → meskipun versi dasar Node berubah dari 22 ke 24, build yang sudah ada tidak akan rusak
- Memanfaatkan fitur secret di BuildKit untuk meningkatkan keamanan/pengelolaan environment variable
Tahapan build Railpack
- Analyze: Menganalisis kode untuk menentukan paket yang diperlukan, command eksekusi, dan perintah start
- Plan: Membuat rencana build dalam bentuk yang dapat diserialisasi ke JSON (terdiri dari beberapa tahap, dan tiap tahap bergantung pada hasil tahap sebelumnya atau pada keseluruhan image)
- Generates: Menghasilkan build graph BuildKit (berdasarkan input/output)
Strategi build dengan memanfaatkan BuildKit
- Sementara Dockerfile bekerja secara serial, BuildKit memproses banyak perintah secara paralel dan memberi kontrol rinci atas input/output di setiap tahap
- Railpack mendefinisikan semua tahap build berdasarkan hasil analisis kode, lalu menentukan dependensi tiap tahap secara detail pada level rendah
- Rencana ini kemudian diubah menjadi graf BuildKit LLB dan di-resolve
- Saat environment variable dan sebagainya berubah, nilai hash-nya di-mount sebagai file; jika tidak ada perubahan pada kode dan variabel, cache hit dijamin
- Hasilnya, Railpack dapat sepenuhnya mengontrol cara image dibuat
Fitur baru yang dimungkinkan oleh adopsi Railpack
- Mendukung build/deploy situs statis tanpa konfigurasi untuk Vite, Astro, CRA, dan Angular
- Integrasi erat proses build dengan Railway UI
- Dukungan untuk versi bahasa terbaru dimungkinkan tanpa perlu rilis Railpack itu sendiri
- Menyediakan optimasi caching antar environment untuk tiap proyek
- Saat ini mendukung Node, Python, Go, PHP, HTML statis, dan terus memperluas dukungan framework serta bahasa
Open source dan rencana ke depan
- Railpack saat ini dirilis dalam status Beta, dan dapat langsung digunakan hanya dengan mengaktifkannya
- Dokumentasi resmi, kode nyata, dan kanal dukungan publik tersedia di railpack.com
- Ke depan, prioritasnya adalah dukungan mendalam untuk bahasa yang banyak digunakan, lalu memperluas cakupan setelah API inti dan tingkat abstraksinya mapan
1 komentar
Komentar Hacker News
Saya penggemar Nix, tetapi saya harap dipercaya bahwa saya tidak terikat secara emosional pada keputusan untuk tidak memakainya. Namun, saya sulit memahami beberapa keluhan di tulisan ini dan merasa perlu penjelasan lebih lanjut. Misalnya ada pernyataan bahwa “masalah terbesar Nix adalah manajemen versi paket berbasis commit”. Nixpkgs memang sumber daya yang luar biasa, tetapi Nix dan Nixpkgs bukan hal yang sama. Jika ingin mengambil versi arbitrer dari toolchain, Nixpkgs memang sangat tidak cocok, tetapi ada cara lain dengan Nix. Misalnya, ada tool Nix yang sangat bagus untuk mengambil versi arbitrer Rust. Lalu ada juga pernyataan bahwa “dependensi Nix tidak bisa dipisah ke layer terpisah”, dan menurut saya itu sama sekali tidak masuk akal. Itu bisa dipisah dengan cara apa pun yang diinginkan. Tool Docker dari Nixpkgs juga mendukung ini. Bagian tentang memindahkan codebase dari Rust ke Go memang tidak terkait langsung dengan Nix, tetapi menurut saya menarik. Biasanya orang tidak memutuskan ganti bahasa pemrograman dengan enteng, kecuali memang sejak awal berencana membangun ulang. Saya curiga Railpacks dan Nixpacks dikerjakan oleh orang yang berbeda. Saya juga pernah melihat apa yang terjadi ketika orang yang tidak terlalu paham Nix harus menangani solusi Nix yang belum matang di sebuah organisasi. Hasilnya tidak bagus, dan kebanyakan orang memang tidak mau belajar Nix. Karena itu di tempat kerja lama saya, kami hampir tidak memakai Nix untuk menghindari situasi seperti ini
Saya suka memanfaatkan Nix, tetapi saya frustrasi karena setiap kali membahas masalah penggunaan dasar Nix, jawaban yang saya terima selalu “ada workaround” (tetapi butuh puluhan hingga ratusan baris kode tambahan dengan dokumentasi minim, bahasa yang aneh, pesan error yang buruk, dan pengetahuan tak terdokumentasi yang hanya diketahui orang berpengalaman). Sebagian besar masalah Nix bukan berasal dari Turing-completeness, melainkan dari tidak adanya fitur bawaan dasar seperti API yang intuitif. Jika di setiap proyek penggunaan Nix makin berubah menjadi keterlibatan penuh untuk menyelesaikan masalah Nix itu sendiri, maka tidak ada alasan kuat untuk memilih Nix ketika ada tool arus utama yang terdokumentasi dengan baik. Pada praktiknya pun kebanyakan orang memilih Docker. Sangat mengecewakan bahwa Nix begitu keras kepala pada kemurnian ideal dan tidak menyelesaikan masalah nyata pengalaman developer dalam waktu yang realistis. Tentu semua orang berkontribusi secara sukarela, tetapi tetap sangat disayangkan melihat upaya teknis sebesar ini akhirnya praktis tidak bisa digunakan karena UX yang dirancang buruk
Saya tidak memakai Nix, tetapi klaim “Nix ≠ Nixpkgs” terasa jauh dari realitas. Bagi mayoritas pengguna, kalau alternatifnya menuntut riset dan usaha tambahan, maka pada akhirnya Nixpkgs itulah Nix. Untuk klaim “bisa dipisah ke layer terpisah”, saya juga penasaran apakah itu benar-benar intuitif, sederhana, dan jadi perilaku bawaan
Yang penting adalah pengguna Railway adalah developer yang ingin menentukan versi paket sesuai keinginan mereka. Dalam struktur Nix dan Nixpkgs, mengunci versi suatu paket berarti mengunci commit dari seluruh tree nixpkgs. Karena build paket node/python/ruby banyak bergantung pada hal di luar tree, akhirnya perlu pemetaan antara versi dan commit. Abstraksi ini tidak sempurna, sehingga bahkan ketika pengguna hanya ingin melakukan “yarn add paket”, mereka mungkin tetap harus mencocokkan keadaan tree. Menggunakan Nix tanpa Nixpkgs mungkin masih oke untuk penggunaan terbatas, tetapi untuk platform seperti Railway itu pilihan yang berat
Saya kurang paham kontroversi soal versioning. Saya baru mulai memakai Nix, tetapi saya jelas punya paket yang diambil dari commit tertentu
Menurut saya penjelasannya tepat sasaran. Nixpkgs dan Nix memang berbeda, tetapi pada praktiknya Nixpkgs adalah keunggulan utamanya. Dengan memakai NixOS, untuk pertama kalinya saya bisa menggunakan versi terbaru kernel Linux tepat di hari rilisnya. Debian Stable juga bagus, tetapi selalu terasa seperti mundur beberapa tahun ke masa lalu. Meski begitu, bahasa Nix memang pantas banyak dikritik. Itu bahasa tua, hasil terbaik yang bisa dicapai pada masanya, tetapi saya rasa tidak perlu dipaksakan berubah. Build system Nix terasa klasik dan menyebabkan terlalu banyak rebuild yang tidak perlu. Misalnya, hanya karena mengubah satu command line yang diteruskan ke kernel di ISO instalasi NixOS (contohnya kecepatan port console), bisa terjadi build aneh yang memakan waktu sekitar 3 menit. Lucu sih, tetapi tidak sampai membuat saya meninggalkan Nix. Hanya saja, itu hal yang tak akan pernah saya terima di build system saya sendiri. Memakai Nix untuk membuat image Docker secara pribadi menurut saya adalah yang terburuk. Dulu saya hanya ingin menambahkan binary
pg_dumpdari Postgres ke binary buatan Go, lalu tim infrastruktur menyarankan Nix. Binary Go yang sudah dikompresi tadinya 50MB, tetapi hasilnya jadi image monster 1.5GB. Padahalpg_dumpcuma 464KB. Akhirnya saya beralih ke kombinasi Bazel, rules_debian, dan distroless, dan hasilnya jauh lebih rapi. Sebagian besar sistem Nix terasa seperti 1.4GB itu nilai default. Untuk build proyek C++ besar pun Nix tidak terasa luar biasa hebat. Bahkan sistem build yang dibuat sendiri untuk software masing-masing sering kali lebih sesuai dengan kebutuhan. Saya suka Bazel, dan untuk proyek Go saya hanya ingin memakaigo build. Dalam 99% kasus saya akan memilih tool seperti itu daripada Nix, meski untuk pembaruan atau deployment saya mungkin tetap menulis flake agar bisa dipakai di home-managerPemilihan versinya terasa aneh. Versi nixpkgs jelas masuk akal saat menjalankan atau membangun sistem. Jika ini platform yang menyediakan runtime/compiler, seharusnya versi diberikan secara langsung seperti devenv. Misalnya nixpkgs-python menyediakan “semua versi Python, diperbarui tiap jam dengan Nix”. Fakta bahwa Railway menyuntikkan environment variable deployment ID ke semua build juga sebenarnya bisa dilakukan pada layer setelah instalasi. Paket pun bisa dipisah ke beberapa layer, dan pengaturan jumlah layer juga bisa diotomatisasi
Sebagai orang dengan pengalaman DevOps/SRE, saya sering melihat ketika seseorang mencoba membuat sistem manajemen dependensi, biasanya ujungnya jatuh ke salah satu dari dua arah (misalnya dalam konteks Python). Opsi 1: “monorepo + environment bersama”, kelebihannya mudah dikelola, patch keamanan lebih mudah, terpusat. Kekurangannya, selalu ada orang yang ingin versi khusus, rollout bertahap sulit, dan ada masalah dalam membangun image ramping. Opsi 2: “masing-masing conda/venv”, kelebihannya bisa disesuaikan per kasus, paket tak perlu bisa dibuang, upgrade bertahap memungkinkan. Kekurangannya, environment jadi terlalu banyak, kompatibilitas silang tidak teruji, dan manajemen keamanan jadi mimpi buruk. Pada akhirnya, semakin lama berkarier, semakin terasa benarnya ungkapan “tidak ada solusi, yang ada hanya trade-off”
Menurut saya pernyataan “Nix itu sendiri tidak bermasalah. Yang bermasalah adalah cara memakainya” adalah contoh bagus dari prinsip “gunakan tool yang tepat untuk tempat yang tepat”. Nix luar biasa di beberapa konteks, tetapi di konteks lain bisa jadi yang terburuk. Masalahnya, waktu belajar Nix itu panjang, sehingga saat seseorang sudah cukup paham untuk membuat keputusan, ia sudah telanjur banyak berinvestasi waktu dan jadi sulit berbalik arah. Akhirnya Nix dipaksakan terus untuk tujuan awalnya meski kurang cocok
shell.nixatauconfiguration.nixsesuai spesifikasi justru karena struktur seperti ini. Saya juga kadang membuat environment per repositori yang sepenuhnya terbungkus, dan jika memakai flakes mungkin bisa membuat environment yang lebih reproducible. (flake.nixmiripshell.nixtetapi juga mendukung penguncian versi…)Rasanya seperti memaksakan konsep versi ke tempat yang sebenarnya tidak punya versi. Dependensi rusak karena “versi default”? Itu mirip memakai tag
:latestdi Docker lalu server rusak setiap kali ada perubahan. Saya kurang paham isi blog ini. Saya juga tidak setuju dengan pernyataan “dependensi Nix tidak bisa dipisah ke layer terpisah”./nix/storebisa dipisah sebanyak yang diinginkan, dan rasanya mereka juga tidak benar-benar paham cara memakai container dan Nix bersama. Kalau kemampuannya seperti ini, alternatif yang mereka usulkan kemungkinan besar hanya akan mengulang masalah yang sama. Ini contoh klasik sindrom NIH (membuat tool sendiri)Tentu wajar untuk tidak memakai Nix di tempat yang memang tidak cocok, tetapi tetap terasa aneh kalau sistem yang sudah berjalan dibangun ulang dari awal sampai akhir, padahal banyak masalah itu sebenarnya sudah diselesaikan orang lain dan cukup dicari sedikit saja. Sepertinya nix2container atau flakes bisa menyelesaikan hampir semua masalah itu. Soal versioning juga, flakes yang saya tulis tiga tahun lalu masih bisa dibangun sekarang dengan hasil yang sama persis. Rasanya ada aroma pivot platform demi masuk pasar atau menarik investasi. Sebagai catatan, saya melihat GitHub nixpacks dan ternyata hanya memakai rustPlatform, dan kalau masalahnya di Rust maka rust-overlay pada praktiknya adalah jawaban yang benar
Kalau dipikir-pikir dari sudut pandang mana yang lebih mudah menarik VC, judul “platform deployment” memang lebih menguntungkan daripada pembungkus Nix
Berlawanan dengan klaim “dependensi Nix tidak bisa dipisah ke layer terpisah”, nix2container justru bisa melakukan pemisahan itu secara tepat. Misalnya jika ada image yang membutuhkan bash, Anda bisa membuat layer khusus yang hanya berisi bash, dan layer ini hanya perlu dibangun ulang/didorong lagi saat bash berubah. Klaim bahwa “karena dependensi, image raksasa muncul sebagai satu layer
/nix/storetunggal” memang berlaku untuk fungsinixpkgs.dockerTools.buildImage, tetapi tidak berlaku untuknix2containerataunixpkgs.dockerTools.streamLayeredImage. Tool ini pada praktiknya menghasilkan skrip yang kemudian dipakai untuk mendorong image.nix2containermembuat JSON dari path semua layer, lalu memakai Skopeo untuk mendorong image ke Docker, registry, podman, dan sebagainya. (Sebagai catatan, saya penulisnix2container)Saya benar-benar ingin berterima kasih untuk
nix2container. Saya memakainya untuk deployment AWS (ECR), dan waktu perpindahan antar-build turun menjadi hitungan detik satu digitKami juga berencana menguji
nix2containerkarena masalah ukuran image Docker. Terima kasih sudah membuat tool yang bagusMenurut saya masalah intinya di sini adalah sikap yang ingin mempertahankan “sup versi kustom” yang didorong package manager bahasa pemrograman (dan pendekatan ini tidak berkelanjutan). Alternatif seperti Mise tidak memahami batasan versi antar-paket, dan sama sekali tidak menguji setiap paket. Mustahil mengharapkan tingkat keandalan yang sama
Memang benar “sup versi kustom” itu tidak berkelanjutan, tetapi orang terus memakainya karena itu bekerja cukup baik. Library level OS dikelola sangat konservatif sehingga tidak mudah rusak, dan tool seperti mise atau asdf yang menumpuk kombinasi versi kustom di atasnya biasanya tetap baik-baik saja. Kalaupun rusak, cukup utak-atik versi atau konfigurasi dan beres. Memang menyebalkan saat rusak, tetapi tidak terlalu penting. Sistem yang membutuhkan pembelajaran atau usaha tambahan dianggap buang-buang waktu. Di sisi lain, orang yang lebih mementingkan kondisi “tidak rusak” justru cenderung memilih Nix meski ada learning curve dan ketidaknyamanan. Untuk layanan yang menargetkan banyak pengguna seperti Railway, pada akhirnya mereka akan lebih memerhatikan kelompok pertama: kemudahan dan inersia
Saya penasaran apa maksud dari “sup versi kustom” dan apa alternatifnya
Keduanya sama-sama mungkin dilakukan. Misalnya, paket Rust bisa dengan mudah dibangun dengan Nix berdasarkan informasi
Cargo.lock. Nixpkgs memang bertentangan dengan kombinasi versi kustom, tetapi Nix sendiri sangat mampu melakukannyaNix memberikan jaminan pada level commit, bukan versi arbitrer. Anda tetap bisa kesulitan di edge case seperti perubahan glibc atau konflik shared library. Mungkin sekarang sudah terlambat, tetapi saya juga bisa memberi konsultasi tentang cara memakai Nix dengan lebih elegan. Menurut saya produk mereka sendiri keren
Nix sangat kuat dalam mencegah konflik shared library. Namun, perubahan kecil sekalipun (komentar, dokumentasi, dan sebagainya) bisa memicu rebuild total sampai ke semua dependensi turunannya. Akibatnya, kebutuhan rebuild bisa menjadi sangat besar dan membuat pengembangan terasa menyakitkan. Ini bisa dilihat dari proses staging di nixpkgs
Saya sangat memahami nilai Nix. Hanya saja, menurut saya mengatakan “akan hancur” agak berlebihan. Memang ada beberapa jaminan besar yang hilang dibanding Nix, tetapi tetap saja kemungkinannya masih jauh lebih baik berjalan dengan benar dibanding kebanyakan software lain
Saya tidak paham kenapa mereka bergantung pada hash nixpkgs alih-alih membuat derivation mereka sendiri
Menarik melihat banyak komentar bernuansa “sebenarnya semua ini bisa diselesaikan dengan Nix, asal Anda ahli seperti saya”
Kalau ada sebuah perusahaan yang melakukan semua teknologi dan bisnisnya dengan JavaScript, lalu karena tidak memahami konsep inti yang sudah ada (fungsi, array, dan sebagainya) mereka malah membuat NIH (bahasa baru dengan spesifikasi sendiri), maka itu lebih mencerminkan kekurangan internal mereka sendiri
Ini memang suasana yang selalu berulang setiap kali topik Nix muncul
Dan itulah aura Nix. Selalu ada narasi khas “saya akan menyelamatkan dunia” dan setiap kali ada tanggapan “fitur yang saya butuhkan tidak tersedia”, jawabannya selalu “itu karena Anda memakainya tidak benar”