Why We're Moving on From Nix (Introducing Railpack)
Bài tóm tắt Railway 2025 3. Quy mô lớn Độ khó: Trung bình
Railway · 2025 · 3. Quy mô lớn · Độ khó: Trung bình — bài gốc
Bài ra mắt sản phẩm; mọi số liệu là theo Railway. Hệ được nói tới là builder biến code của người dùng thành image, tức là "CI" mà Railway chạy cho khách.
Bối cảnh
- Nixpacks (ra mắt khoảng 2022) là builder mặc định của Railway, đã build hơn 14 triệu app. Theo Railway, nó chạy tốt cho 80% người dùng, nhưng 20% còn lại là khoảng 200.000 người gặp giới hạn hằng ngày (2025); họ muốn đi từ 1 triệu lên 100 triệu người dùng.
- Vấn đề với cách họ dùng Nix (theo Railway, không phải lỗi của Nix):
- Phiên bản gắn với commit của nixpkgs: chỉ có bản major mới nhất của mỗi gói; muốn hỗ trợ từng bản patch thì phải giữ bảng "phiên bản → commit SHA". Node và Python rốt cuộc chỉ hỗ trợ bản major mới nhất. Tệ hơn, cập nhật một commit để có bản mới của một gói làm mọi gói khác đổi theo; đổi phiên bản mặc định thì build đang chạy được bỗng hỏng.
- Image lớn: mọi phụ thuộc build lẫn runtime nằm chung một layer
/nix/store, không tách được. - Cache kém: Railway tiêm biến môi trường deployment ID vào mọi build, nên mọi layer sau chỗ biến đó trong Dockerfile luôn bị invalidate.
Thay đổi chính
- Railpack viết lại từ đầu, đổi từ Rust sang Go (để dùng thư viện BuildKit). Ba bước: Analyze (xem code, quyết định gói, lệnh, lệnh start) → Plan (build plan dạng JSON, mỗi bước khai báo rõ đầu vào là bước nào hay image nào) → Generate (dựng đồ thị BuildKit LLB và frontend riêng).
- Dockerfile tuyến tính, còn đồ thị BuildKit song song: mỗi lệnh là một stage riêng, kiểm soát chính xác layer đầu vào và cách ghép filesystem cuối. BuildKit đi ngược từ đích, lấy từ cache nếu được.
- Cache theo biến thật sự dùng: Railpack băm giá trị các biến môi trường mà một bước dùng, mount file chứa hash vào đầu vào của bước đó; code và biến dùng không đổi thì trúng cache. Cache dùng chung được giữa các environment.
- Khoá phụ thuộc của lần build thành công (dùng Mise để phân giải phiên bản), nên đổi mặc định Node 22 → 24 không làm hỏng build cũ; hỗ trợ
major.minor.patch. Secret đi qua BuildKit secrets để không lộ trong log hay image.
Kết quả (theo Railway)
- Image nền nhỏ hơn 38% (Node) và 77% (Python); nhiều lần trúng cache hơn. Bản beta lúc ra mắt, đã build cho railway.com.
Vì sao
- Khoá cache phải chứa đúng và chỉ những đầu vào ảnh hưởng tới kết quả; một biến thay đổi mỗi lần (deployment ID) đặt sai chỗ làm mất cache của mọi bước sau (Cache và invalidation, CI và build ở quy mô lớn).
- Builder của nền tảng phải che độ phức tạp của công cụ bên dưới; theo Railway, cố che những phần làm nên bản chất của Nix là không được (Nền tảng nội bộ cho service (đường ray chung)).
Bài học cho SWE
- Tách phiên bản từng phụ thuộc khỏi nhau; một con trỏ chung cho mọi gói biến mỗi lần nâng cấp thành thay đổi toàn cục.
- Khoá phụ thuộc của lần build thành công để thay đổi mặc định không phá người dùng cũ.
- Mô hình build dạng đồ thị với đầu vào khai báo rõ thì cache và song song hoá được; dạng tuyến tính thì một dòng đổi làm mất mọi thứ phía sau.
Liên quan
- Railway 2023 · Mercari 2026 (cache layer image ở CI) · Uber 2022 (khoá cache theo hash đầu vào)
- CI và build ở quy mô lớn · Cache và invalidation · Nền tảng nội bộ cho service (đường ray chung)
#Builder#Nixpacks#Nix#Railpack#BuildKit LLB#Mise#Layer cache#Dependency locking#Image size#Rust sang Go
Được nhắc tới trong
Chủ đề (3)
Công ty (1)
Bài tóm tắt (4)