Railway 在 2026 年 3 月 4 日開源了 Railpack Beta,公開表示要用它取代自家從 2022 年就開始跑的 Nixpacks。這件事對自架 PaaS 圈的意義不小——Nixpacks 過去三年幾乎是 Coolify、Dokploy 這類「push 一份 repo、自動包成 image」的預設 build 引擎,Railway 官方轉向等於直接影響下游好幾個社群。Coolify 在 5 月的 v4.1 就把 Railpack 加成內建的 buildpack 選項,Dokploy 也同步支援。整條 pipeline 從 Nix 換成 BuildKit LLB 加 Mise,重點不是換個工具而已,而是把 zero-config build 這個抽象從 Nix 生態脫鉤出來。
Railpack 目前掛在 railwayapp/railpack repo 底下、MIT 授權、用 Go 寫成。CLI 只有幾個指令:railpack build 直接把當前目錄打成 OCI image,railpack prepare 產出中繼設定給 CI 用。設計上它是 BuildKit 的一個 custom frontend,因此本地跑一顆 buildkitd container 就能獨立使用,不需要 Railway 帳號或任何雲端服務。
Nix 撐了三年之後撞到的三面牆
Nixpacks 當初選 Nix 有兩個理由:套件版本可重現、社群套件量夠大。但撐到 2025 年下半,實務上出現幾個難處理的問題。
第一個是版本綁定。Nixpkgs 每個 package 只有一個「當下最新」版本,要指定舊版就得綁 nixpkgs 的某個 commit hash。Railway 為了讓使用者能選 Node.js 20.11 或 Python 3.11.7 這種精確版本,被迫維護一張「patch 版本對應到 nixpkgs commit」的巨大對照表,維運上難以持續。更糟的是換 commit hash 會連帶影響其他 package——為了升 Ruby 換了 commit,結果同一份 config 隔天 build 出來的 OpenSSL 版本變了,先前跑得好好的專案突然爆掉。
第二個是 image size。Nix 為了保持整個 dependency graph 可回溯,會把每個套件連同它的依賴完整放進 image。Node.js 專案跑完 Nixpacks 打出來的 image 大約 1.3 GB,其中一大半是 Nix store 本身跟大量根本沒被 runtime 用到的 build-time 依賴。Python 更誇張,1.5 GB 上下是常態。這個大小對 Railway 這種按 image pull 計費的平台是硬成本,對自架用戶則是每次部署都要吃掉頻寬跟磁碟。
第三個是 layer cache 失效。Nixpacks 產生的 Dockerfile 是線性的,任何一個上游 layer 有變動就會讓下游全部重跑。Railway 內部把 deployment ID 當環境變數注入以後,發現這個變數的 hash 每次都不同,等於每次部署 cache 全滅。這件事在 Railway 環境裡特別明顯,但邏輯本身在任何 CI 系統都一樣——Nixpacks 的線性 Dockerfile 模型跟現代 build cache 的期望對不上。
BuildKit LLB frontend 才是這次重寫的重點
Railpack 沒有沿用 Nixpacks 的「產生 Dockerfile 再交給 Docker build」路線,而是直接寫成 BuildKit 的 custom frontend,輸出 LLB(Low-Level Build)指令。這個決定看似技術細節,實務差別很大。
BuildKit 內部的 LLB 是一張 DAG,不是一條線。傳統 Dockerfile 每個 RUN 都是一個 layer、只能依序執行;LLB 允許不同 stage 平行跑,最後再合併結果。舉例來說,Node.js 專案要同時裝 system dependencies、跑 npm ci、下載 tailwind CLI,三件事沒有先後關係,Railpack 就把它們安排在三個獨立 stage 平行跑;Nixpacks 走傳統 Dockerfile 得排成序列。以 monorepo 或多語言專案來說,這個平行度直接影響 build 時間。
Cache 這一層改動也很大。Railpack 對每個 stage 標註輸入的 hash,只要輸入沒變 stage 就從 cache 拿;而且它把環境變數的值 hash 之後掛成檔案,同一份 code 遇到不同 deployment ID 不會讓 cache 失效。BuildKit 原生支援跨環境共享的 remote cache backend(S3、GHA cache、Registry cache),因此在 CI/CD 跟本地開發之間共用 cache 也變成順路的事。
railpack build 的執行流程大致是:偵測專案語言 → 生成 LLB → 提交給 buildkitd → 輸出 OCI image。開發者要本地跑就是 docker run --privileged -d --name buildkit moby/buildkit 起一顆 buildkitd,再 export BUILDKIT_HOST='docker-container://buildkit',接著 railpack build . 就會直接把當下目錄打成可執行的 image。全程沒有 Dockerfile。
Mise 接手做語言版本管理
Nixpacks 用 Nix 一次解決套件跟語言版本兩件事,Railpack 把這兩件事切開——BuildKit 負責 image build,Mise 負責語言 runtime 的安裝跟切換。
Mise(前身是 rtx)在 Ruby、Node.js、Python 社群已經有相當使用量,取代 asdf 的位置。它的 plugin 系統本來就對「同時裝多個語言、每個各自釘版本」這件事最擅長,Node.js 20.11.1、Python 3.11.7、Go 1.22.3 這種細節都能鎖住,也沒有 Nixpkgs 那種「換 commit 連帶影響一堆套件」的耦合問題。
Railpack 內部把每個支援語言對應到一個 provider,provider 決定要跟 Mise 要哪個版本、裝哪些系統依賴、build 完要留下哪些檔案。Node 專案 provider 會偵測有沒有 pnpm-lock.yaml 或 yarn.lock,自動裝對應 package manager;Python 專案會分辨 Poetry、uv、pip 的鎖檔格式。這些邏輯過去也散在 Nixpacks 各個 provider 裡,但因為底層是 Nix,換 package manager 常常要重跑整個依賴解算;改用 Mise 之後只影響那一個 stage。
實測縮到什麼程度
Railway 官方公布的數字是 Node 專案 image size 縮 38%、Python 專案縮 77%。這個 Python 的數字看起來誇張,但拆開來看合理——Nixpacks 的 Python image 塞了大量 Nix 帶進來的 C 編譯工具鏈跟系統函式庫,一旦切成 slim 基底加 Mise 裝的 Python runtime,Python 3.11 本體加上專案依賴大概就是 300 MB 出頭。
實測還有一個沒公布但明顯的差異是冷啟動時間。Nixpacks 每次 build 要先解 Nix expression、拉 Nix store,光是初始化就要 20 秒起跳;Railpack 的 LLB 直接送給 buildkitd 執行,第一個 stage 幾秒內就會開始跑。對頻繁部署的專案(每天推十幾次那種),累積下來差很多。
Coolify 跟 Dokploy 端的實務現況
Coolify v4.1 在 2026 年 5 月加入 Railpack 選項,跟原本的 Nixpacks、Dockerfile、Docker Compose 並列在 build pack 下拉選單。切換基本上就是把 build type 從 Nixpacks 改成 Railpack、重新部署一次;已經 build 好的 Nixpacks image 不會受影響。Dokploy 則從幾乎同期就把 Railpack、Heroku Buildpacks、Paketo Buildpacks 一併整合,UI 上跟 Nixpacks 平行陳列。
實務上要注意的是 Railpack 目前對某些相對冷門的 stack 支援還不完整。Elixir、Rust、.NET 這幾種 Nixpacks 有處理但 Railpack 還在 provider 開發中;PHP 專案雖然支援,但對 Laravel Nova 這類需要私有 Composer registry 的情境還在補功能。已經跑在 Nixpacks 上、build 過程穩定的專案沒有理由趕著切;反過來,長期被 image size 拖累(Docker registry 費用、部署頻寬、pull 時間)的 Node.js 跟 Python 專案,切過去回本很快。
另外一個要留意的點是 buildkitd 需要 privileged container 或 rootless BuildKit 的完整權限。自架環境若跑在受限 Docker daemon 上(例如某些共享 VPS),Railpack 這條路可能還不能走;這種情境目前還是要走 Nixpacks 或直接寫 Dockerfile。
Nixpacks 沒死,只是換位置
Railway 官方沒有停掉 Nixpacks 的維護——目前主分支還在收 PR、修 bug——但明確定位為「Railpack 支援不到的邊角語言」的 fallback。實務上這代表 Nixpacks 未來不會再有大功能,但已知的專案短期內不會壞掉。
對自架 PaaS 使用者的選擇邏輯其實不複雜:新專案優先試 Railpack,遇到不支援的 stack 才回退 Nixpacks 或 Dockerfile。既有專案除非有明確痛點(image 太大、cache 太常失效),否則不用主動遷。長期來看,「zero-config build」這個抽象從 Nix 脫鉤是好事——BuildKit 是 OCI 標準生態的一部分,未來就算 Railway 有天不做了,社群還是能接手。
臺灣的自架社群過去這幾年慢慢從「一台 VPS 手動裝 Docker」進化到「用 Coolify 管十幾個服務」的規模,build 引擎的效能直接對應到部署節奏跟主機成本。把 image 從 1.3 GB 縮到 800 MB,看似只是數字差異,但推到十幾個環境、每天部署好幾次的專案,累積起來就是主機空間、頻寬、部署等待時間三邊同時省下來。
NCSE Network 的 VPS 服務跑在臺灣是方電訊機房、Intel Gold CPU 加 NVMe SSD,非常適合搭配 Coolify、Dokploy 這類自架 PaaS 打造個人或小團隊的部署平台。想在自家主機上把 Railpack 這套 build 流程完整跑起來,可以到 ncse.tw 看看方案細節。