惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

G
Google Developers Blog
宝玉的分享
宝玉的分享
月光博客
月光博客
B
Blog
云风的 BLOG
云风的 BLOG
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
aimingoo的专栏
aimingoo的专栏
N
Netflix TechBlog - Medium
博客园_首页
GbyAI
GbyAI
人人都是产品经理
人人都是产品经理
A
About on SuperTechFans
Y
Y Combinator Blog
L
LangChain Blog
有赞技术团队
有赞技术团队
D
Docker
爱范儿
爱范儿
博客园 - 司徒正美
H
Hackread – Cybersecurity News, Data Breaches, AI and More
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
酷 壳 – CoolShell
酷 壳 – CoolShell
Microsoft Security Blog
Microsoft Security Blog
D
DataBreaches.Net

Railway Blog

Where Railway is, and where it's going (Summer 2026) PaaS vs IaaS vs SaaS: What Each Means and Who Should Pick What in 2026 The Best Continuous Deployment Tools in 2026 The Best PaaS for Multi-Region Deployments in 2026 The Best Platforms for Monorepo Deployments in 2026 Compliance Isn't a Feature, It's a Posture What is BYOC (Bring Your Own Cloud)? A Developer's Guide for 2026 The Best Managed Kubernetes Hosting in 2026 The Best Container Registries in 2026 The Vanilla Cloud Tax: What Rolling Your Own on AWS Actually Costs What is a PaaS? A Developer's Guide for 2026 The Best Cloud Observability and Logging Tools in 2026 The Best PostgreSQL Hosting for Developers in 2026 The Best Multi-Region Hosting Platforms in 2026 The Best Platforms to Deploy AI Apps in 2026 (Not the Models, the Apps Around Them) Incident Report: May 19, 2026- GCP Account Suspension Counting to 3 with a new builder processing 50M+ monthly builds Railway iOS preview now available via TestFlight Kill your onboarding: selling to 10,000+ new users a day Your AI wants to nuke your database. Guardrails fix that. Better Rails for Agents: A New Remote MCP and Railway Agent in the CLI Moving Railway's Frontend Off Next.js One command deploys, there's a Stripe APP for that From registrar to deployed: buying a domain inside Railway A letter to open source builders who deserve more Networking is a black box, we used eBPF to open it Heroku Walked So Railway Can Run Security Features Your Security Team Will Love Railway Runs Open Source, Now We're Funding It Railway raises $100M Series B to unburden the builders
Why We’re Moving on From Nix
Jake Runzer · 2025-03-04 · via Railway Blog

Today we’re excited to release Railpack — the next iteration of the Railway builder, developed from the ground up and based on everything we’ve learned from building over 14 million apps with Nixpacks.

We first announced Nixpacks nearly 3 years ago and it quickly became the default way to build images from user code on Railway. While Nixpacks works great for 80% of users, that still left us with 200k Railway users who might encounter limitations daily.

It became clear we needed a major builder upgrade to scale our user base from 1M to 100M.

Cumulative builds with Nixpacks over time
Cumulative builds with Nixpacks over time

Here are the highlights of Railpack:

  • Granular Versioning: Support for major.minor.patch versions of packages (instead of Nix’s approximate versions)
  • Smaller Builds: We’ve been able to reduce image sizes between 38% (Node) and 77% (Python), enabling faster deploys on Railway
  • Better caching: Railpack interfaces directly with BuildKit to control the layers and filesystem, resulting in more cache hits (with sharable caches across environments)

You can opt-in to using Railpack for your builds today. It is already powering builds for railway.com and central station.

Our problems with Nix

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 in the nixpkgs repo. We tried to support every patch version, but it looked like this:

const AVAILABLE_SWIFT_VERSIONS: &[(&str, &str)] = &[
    // ...
    ("5.4", "c82b46413401efa740a0b994f52e9903a4f6dcd5"),
    ("5.4.2", "c82b46413401efa740a0b994f52e9903a4f6dcd5"),
    ("5.5.2", "7592790b9e02f7f99ddcb1bd33fd44ff8df6a9a7"),
    ("5.5.3", "7cf5ccf1cdb2ba5f08f0ac29fc3d04b0b59a07e4"),
    ("5.6.2", "3c3b3ab88a34ff8026fc69cb78febb9ec9aedb16"),
    ("5.7.3", "8cad3dbe48029cb9def5cdb2409a6c80d3acfe2e"),
    ("5.8", "9957cd48326fe8dbd52fdc50dd2502307f188b0d"),
];

This approach isn’t clear or maintainable, especially for contributors unfamiliar with Nix’s version management.

For languages like Node and Python, we ended up only supporting their latest major version.

But even this was problematic because versions are tied to a single commit SHA. Updating the commit hash to support the latest version of a package meant all other package versions would also update. If a default version changed, there was a high likelihood that a user's build would suddenly fail with unexpected errors.

We feel bad when users can't access the latest packages, but feel worse when previously functional builds suddenly fail.

Image sizes and caching

The way Nixpacks uses Nix to pull in dependencies often results in massive image sizes with a single /nix/store layer ... all Nix and related packages and libraries needed for both the build and runtime are here.

With no way of splitting up the Nix dependencies into separate layers, there was not much we could do to reduce the final image sizes. Not a problem with Nix per se but certainly a problem with how we were using it.

Caching was also problematic as we had little control over when layer caches were invalidated.

Railway injects a deployment ID environment variable into all builds. This means that any layers that run after these variables are added to the Dockerfile are always invalidated and can never be cached.

Result of running dive on a simple python uv app
Result of running dive on a simple python uv app

I want to be clear, we don’t have any problem with Nix itself. But there is a problem with how we were using it. Trying to abstract all the parts of Nix that make Nix… Nix, just fundamentally doesn't work.

We don’t want our users to have to understand what a derivation is or why Node 22.14.0 is available on archive version 757d2836919966eef06ed5c4af0647a6f2c297f4 of the unstable channel.

Introducing Railpack

To fix the issues we’ve had with Nixpacks, we built Railpack.

Since we transitioned away from Nix, we also transitioned away from the name Nixpacks in favor of Railpack. We also changed the codebase from Rust to Go because of the Buildkit libraries.

Here are some architectural highlights:

  • We generate a custom BuildKit LLB + Frontend to give us much more control over how the final image is constructed — resulting in 38% smaller base Node and 77% smaller base Python images compared to building with Nixpacks
  • We use Mise for version resolution and most package installation, though it leaves room to support other executable sources in the future
  • We're now able to lock the dependencies used when a successful build happens. This means that builds won’t break when we update the default Node version from 22 to 24
  • We improved secret environment variable management. Railpack leverages BuildKit secrets to prevent variables from appearing in build logs or the final image

How it works

The Railpack process is split into three parts:

  • Analyze: Look at the code and determine what packages should be installed, what commands should be run, and what the start command should be
  • Plan: Create a build plan in a JSON-serializable format that contains several steps, each with inputs derived from other steps or entire images.
  • Generates: Construct a BuildKit build graph based on the inputs and outputs from the plan.
screenshot-2025-03-04-11.04.20.png

While Dockerfiles are very linear in nature, BuildKit graphs are extremely parallel. Each command runs in its own stage of a multi-stage build and provide precise control over the input layers and how the final file system is assembled.

Railpack analyzes the code and generates a build plan of all the necessary steps needed to build.

Each step specifically defines what previous step or image is required — a format that is much lower-level than what was used in Nixpacks. This plan is then turned into a graph in LLB format and solved.

screenshot-2025-03-04-11.01.32.png

BuildKit starts at the end and works backwards, pulling from the cache if possible or running the commands to resolve each requested layer.

To invalidate layers when specific environment variables change, Railpack will hash the used variable values and mount a file with the hash to an input filesystem. If the code and used variables don’t change, the layer cache will be hit.

Railpack can therefore fully define how an image is made.

Deploy inputs for a static Vite build
Deploy inputs for a static Vite build

What else does Railpack unlock? We're glad you asked:

  • Support for building and deploying Vite, Astro, CRA, and Angular static sites with zero config
  • Tight integration between your builds and the Railway UI
  • Support for the latest versions of languages with no Railpack release necessary
  • Optimized layer caching for a project across environments

How you can use it today

Railpack is available in Beta today. Just enable it in your service settings.

screenshot-2025-03-04-13.44.35.png

It currently supports Node, Python, Go, Php, and Static HTML deployments, including out-of-the-box support for Vite, Astro, CRA, and Angular static sites, making Railway the easiest place to deploy both your frontend and backend.

We are adding more framework and language support actively, so let us know in Help Station what you want to see first. We are prioritizing depth on the more commonly used languages rather than breadth, at least until the core API and abstraction are nailed.

Railpack is also open source with documentation available at railpack.com.