Railway spent years building Nixpacks, open-sourcing it, and watching it become the default zero-config builder behind Coolify, Dokploy, and a chunk of the self-hosted PaaS ecosystem. Then, on March 4, 2026, Railway shipped its replacement. Nixpacks is now in maintenance mode — critical fixes only — and the new builder, Railpack, produces Node.js images that are 38% smaller and Python images that are 77% smaller than the tool it's retiring.
That's not a company switching away from a competitor's product. That's a company throwing out the build tool it invented, rewriting it in a different language, on a different backend, less than three years after shipping it. The interesting part isn't that Railway did this — it's why the rewrite was necessary at all, what it cost to get those numbers, and what the same tradeoff looks like for any self-hosted platform, bex included, picking a build layer today.
Why Nix Had to Go
Nixpacks worked by pulling packages out of the Nix package manager's /nix/store and assembling them into a runnable image. That architecture is also the reason the images were bloated. Railway's own writeup on the rewrite is blunt about the root cause: "the biggest problem with Nix is its commit-based package versioning. Only the latest major version of each package is available, with versions tied to specific commits." A team asking for "Node 22" wasn't getting a clean semver-pinned install — they were getting whatever happened to be attached to a specific, largely opaque Nix channel commit, with no path to "Node 22.14.0 exactly" short of hunting down the archive hash yourself.
That same architecture made per-layer optimization structurally hard. The Nix store isn't organized around "what does this specific app actually need in its final image" — it's organized around Nix's own dependency graph, which pulls in far more than a minimal runtime requires. Independent write-ups on the migration put a real Node.js app at roughly 1.2–1.3GB built on Nixpacks, against roughly 450MB for the same app on Railpack. Read that range as illustrative rather than a precise before/after pair — Railway's own published figure for the average improvement is 38% for Node, not the ~65% those specific numbers would imply — but directionally it's the same story: Nix wasn't a bloat-tuning problem you could fix with better flags, it was a structural ceiling.
Railway's team put it even more plainly about trying to fix Nix in place instead of leaving it: "trying to abstract all the parts of Nix that make Nix… Nix, just fundamentally doesn't work." That's a company concluding the tool couldn't be incrementally patched — it had to be replaced.
What Actually Changed Under the Hood
Railpack isn't Nixpacks with a new name. It's a different builder, built on a different toolchain, for a specific reason:
- Rust → Go. Railway rewrote the builder from Rust to Go specifically to use BuildKit's Go libraries directly, rather than shelling out to or wrapping them.
- Nix → Mise + BuildKit LLB. Railpack drops the Nix package manager entirely. Version resolution now runs through Mise, giving real major.minor.patch semver pinning instead of Nix's commit-pinned approximations. On top of that, Railpack generates a custom BuildKit LLB (Low-Level Build) definition and frontend, which is what actually gives it fine-grained control over how the final image gets assembled layer by layer — the control Nix's package-graph model never exposed.
- Multi-phase builds with shareable caching. BuildKit's cache model lets Railpack share cache layers across environments in a way Nixpacks' Nix-store approach couldn't cleanly support, which is part of where the size reduction comes from: fewer redundant layers surviving into the final image, not just a smaller base.
The net effect Railway is chasing isn't a cosmetic rewrite — it's fixing the two things that actually mattered to the 200,000+ Railway users (out of roughly 1 million total, on a stated path toward 100 million) who were hitting Nixpacks' limitations on a regular basis: bloated images and version pinning you couldn't fully trust.
The Coverage Gap Nobody Should Ignore
Here's the tradeoff the announcement didn't lead with. At its March 2026 beta launch, Railpack supported exactly five things: Node, Python, Go, PHP, and static HTML (with built-in handling for Vite, Astro, Create React App, and Angular). Nixpacks, the tool it was replacing, covered meaningfully more ground.
That's a real regression for anyone who adopted Railpack the week it launched. A team running a Ruby on Rails app, a Rust service, or a JVM workload had no migration path at beta — they were stuck on the maintenance-mode tool or blocked outright. By the time of writing, Railpack's own docs list Rust, Ruby, Java, .NET, Deno, Elixir, and Shell scripts as supported, so the gap has closed substantially in a few months — but "closed substantially in a few months" is itself the risk profile worth naming: a zero-config builder's language coverage is a moving target tied entirely to one company's roadmap and prioritization, not a stable spec you can build against and expect to stay put.
Railpack vs. Cloud Native Buildpacks: The Choice a Self-Hosted PaaS Actually Faces
Railway didn't pick the other obvious option on the table. Cloud Native Buildpacks (CNB) — the CNCF-incubated spec behind Paketo, and the direct descendant of Heroku's original buildpacks — already solved "zero-config source-to-image with pluggable per-language builders," under a vendor-neutral governance model instead of one company's backlog. Railway built a second in-house tool instead.
| Nixpacks (retired) | Railpack (BuildKit) | Cloud Native Buildpacks (Paketo) | |
|---|---|---|---|
| Image size | Baseline (bloated — 1.2–1.3GB typical Node app) | 38% smaller (Node), 77% smaller (Python) than Nixpacks | Comparable-to-smaller than Nixpacks; varies by builder image |
| Version pinning | Nix commit-pinned, approximate | Semver-accurate via Mise (major.minor.patch) | Semver-accurate, builder-declared |
| Caching | Nix-store based, limited cross-env sharing | BuildKit multi-phase, shareable across environments | Layer-based, detect/build/export phase caching |
| Language coverage at launch | Broad (Node, Python, Go, PHP, Ruby, Rust, Java, and more) | Narrow (5 targets), expanded since | Broad — one builder per language, community-maintained (Paketo covers Java, Node, Go, .NET, Python, PHP, Ruby, and more) |
| Governance | Railway-funded, open source | Railway-funded, open source | CNCF-governed spec; multiple independent implementations |
| Adoption signal (mid-2026) | Coolify, Dokploy (legacy default) | Coolify v4.1 (beta, May 2026), Dokku | Heroku, multiple enterprise platforms, longest track record |
The real-world adoption data actually cuts both ways. Coolify shipped Railpack as a beta build-pack option in v4.1.0 (May 18, 2026) — but kept Nixpacks as the default and put Railpack second in the dropdown, a deliberate hedge rather than a full endorsement. Dokku added a Railpack builder too. Both are evidence Railpack is being taken seriously; neither is evidence any major self-hosted platform has made it their default, nine-plus months after launch.
What This Means for bex
The pattern worth sitting with isn't "Railpack is better than Nixpacks" — on the published numbers, it clearly is, for the languages it currently covers. It's that Railway is now on its second in-house, single-vendor-funded builder in under three years, having already walked away from the first one it told the ecosystem to standardize on. Coolify and Dokploy built real product surface on top of Nixpacks; when Railway moved on, those platforms had to add a second builder rather than swap cleanly, because the tool they'd adopted wasn't a spec they could re-implement — it was Railway's codebase.
Cloud Native Buildpacks doesn't have that failure mode by construction. No single company can retire it out from under its adopters, because no single company owns it — Paketo, Heroku's own CNB-based buildpacks, and other implementations all target the same CNCF spec, so a platform building on CNB is building on an API contract, not on one vendor's roadmap decisions. For a Cluster API-based platform like bex, whose build layer needs to be something we can own, patch, and rebase for years without a rug-pull risk, that governance difference matters more than a same-day image-size win.
The honest read: Railpack's numbers are real and its BuildKit-native architecture is a legitimate improvement over Nix's structural bloat — worth tracking, and worth adopting for the specific languages it covers well once its coverage and stability track record matures further. But "track it" and "build the platform's default build path on it" are different bets. bex's build layer should keep leaning on Cloud Native Buildpacks as the default precisely because the risk Railway just demonstrated — a vendor deciding its own zero-config builder needs a from-scratch replacement — is a risk CNB's governance model is specifically designed not to carry.
The Bigger Lesson
Zero-config builders have now gone through three generations in Railway's own history alone: Heroku-style buildpacks, then Nixpacks, then Railpack — each one a response to the last one's limitations, each one built by a single company betting its own roadmap on getting the next iteration right. That's a reasonable bet for Railway to make with its own platform. It's a much less reasonable bet for a different platform to inherit by depending on someone else's in-house tool as a load-bearing piece of its own build pipeline.
The tools that survive a decade in this space — Cloud Native Buildpacks among them — tend to be the ones no single company can retire on a Tuesday.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, built on Cluster API and Cloud Native Buildpacks instead of a single vendor's in-house builder. Star the repo on GitHub or deploy your first app today.



