Skip to main content

Coolify v4.1's Railpack Bet: Should Your Self-Hosted PaaS Follow?

10 min readDora NodaDora Noda
Share
On this page

Coolify 4.1.0 landed on May 18 with a build-pack lineup that reads like a changing of the guard: Dockerfile, Nixpacks, Railpack (beta), Static, Docker Compose, Docker Image. Railway's BuildKit-native successor to Nixpacks now sits one slot away from the tool it was built to replace — in Coolify's build-pack list, with Dokploy already shipping its own Railpack builder beside it.

So here is the verdict up front. If you operate a git-push platform, make Railpack the default for new auto-detected apps, keep Nixpacks working, keep the Dockerfile escape hatch open, and do not rewrite your build layer around beta APIs yet. The convergence is real — Nixpacks is in maintenance mode and its own README points at Railpack — but Railpack is still beta, still single-vendor, and the boring OCI-standard alternative (Paketo / Cloud Native Buildpacks) just got a fresh vote of confidence from Heroku. Track the fast horse, bet the platform on the standard. The rest of this post is the evidence behind that sentence: why Railway walked away from Nix, a three-way comparison with real numbers, what adopting Railpack operationally entails, and the decision tree for each case.

(The same 4.1.0 release also shipped structured audit logging for API mutations and webhook events, plus an instance-level MCP server exposing a set of read-only tools for Coolify resources, behind API/UI enablement flags. Agent-readable infrastructure state is squarely this blog's beat, but it is a one-clause mention here: this post is about the build layer, and the build decision is big enough to fill it.)

Why Railway walked away from Nix

Railway explained the move in June 2025 in a post titled "Why We're Moving on From Nix", and the technical complaint is specific enough to take seriously. Nixpacks pulls dependencies through Nix, and the way it used Nix produced images with a single /nix/store layer containing the packages needed for both the build and the runtime. Build-time compilers and runtime libraries shipped in the same blob, with no way to split them into separate layers — so there was not much left to do about final image size. Railway is careful to say this is not a problem with Nix per se but with how Nixpacks used it. The HN thread on the announcement (44208968) pushed back in exactly that spirit: Nix people argued the bloat was solvable with proper store-path splitting, and that Railway was blaming the tool for its own packaging choices.

Both things can be true, and for a platform operator the distinction barely matters. What matters is the revealed preference: the team that ran Nixpacks in production for years, at Railway's scale, concluded the fix was a new builder rather than better Nix hygiene. Railpack's headline numbers are Railway's own, so treat them as vendor-reported rather than independent: 38% smaller images for Node, up to 77% for Python, plus granular major.minor.patch version resolution instead of Nix's approximate versions, and caching that interfaces directly with BuildKit — finer-grained layers, more cache hits, caches shareable across environments. This blog covered the launch numbers when Railpack replaced Nixpacks; what is new in 2026 is that the decision is no longer Railway's alone. Coolify's release notes credit the Railpack integration with build-time environment support, config merging, examples, and deployment smoke coverage, and Railpack-related fixes kept landing through the 4.2.0 cycle — the classic shape of a beta the maintainer intends to graduate, not abandon.

The three-way table: Nixpacks vs Railpack vs Paketo/CNB

This is the core deliverable — the whole decision on one page. Four columns because the Dockerfile escape hatch is not a buildpack but it is always an option, and every serious platform keeps it.

DimensionNixpacksRailpack (beta)Paketo / Cloud Native BuildpacksPlain Dockerfile
Image sizeBloated by design legacy: build + runtime deps in one /nix/store layer38% (Node) – 77% (Python) smaller than Nixpacks, per Railway's figuresCompetitive: proper build/run layer separation is the CNB spec's whole pointWhatever you write; optimal or terrible, your choice
Caching modelNix store paths; coarse invalidationBuildKit-native: fine layers, more hits, caches shareable across environmentsOCI layer reuse + buildpack layer caching; mature but BuildKit-agnosticBuildKit layer cache, fully manual
Config surfacenixpacks.toml + NIXPACKS_* env varsrailpack.json + RAILPACK_* env vars; config merging supportedproject.toml, builders, stacks; the most specified — and most verboseThe Dockerfile itself
Language providersBroadest after years of accumulationGrowing fast (Node, Python, Go, Rust, more) but narrower; check your stackBroad via Paketo + Heroku + community buildpacksInfinite, by definition
Maturity / statusMaintenance mode; README recommends RailpackBeta in Coolify v4.1+; fixes still landing post-releaseStable standard; Paketo under the Cloud Foundry Foundation, lifecycle actively maintained into 2026Ancient and unkillable
Vendor riskCommunity-owned, low bus factor risk, zero momentumSingle-vendor (Railway); roadmap follows one company's needsMulti-vendor standard (Heroku, Salesforce, Paketo contributors); Heroku shipped a cnb stack option in September 2026None — it is just Docker
Standards storyNixpacks-specificRailpack-specific (BuildKit is standard; the plan format is not)OCI images via an open spec; portable across any CNB-compatible platformOCI by construction

Three honest caveats before anyone screenshots this table. First, the image-size row compares Railway's numbers against Nixpacks' worst-known behavior; your mileage varies by language, and a hand-tuned Dockerfile still beats every auto-detector. Second, "broader provider coverage" is qualitative — count your own stacks against each tool's provider list rather than trusting anyone's "supports everything" claim. Third, beta status cuts both ways: Railpack's velocity (a v0.31 registry-cache release, v0.38 mise/pnpm resolution improvements, Coolify fixes through 4.2.0) is exactly what you want to see, and exactly what can break your pinned behavior next month.

What adopting Railpack actually entails

Suppose the table sold you and you want Railpack on your platform. Concretely, what changes?

Configuration migrates, it does not translate. A Nixpacks app carries nixpacks.toml and NIXPACKS_* variables; its Railpack equivalent carries railpack.json (or railpack.toml in Dokploy's builder) and RAILPACK_* overrides. Coolify's integration added config merging, so platform defaults and app-level config compose instead of fighting — but there is no automatic converter, and detection can misfire on polyglot monorepos. Budget for per-app verification, not a flag flip.

The build path needs a BuildKit daemon. Railpack is Go driving BuildKit: docker run --privileged -d --name buildkit moby/buildkit, then BUILDKIT_HOST=docker-container://buildkit. For a single-box PaaS that is one more privileged container next to the ones already there. For a multi-tenant fleet it is a real architectural question — where build daemons live, how their cache is shared across tenants without leaking layers between them, and who pays for the warm cache disk. Railway's "shareable caches across environments" is a feature and a tenancy boundary you now own.

Beta means pinning and watching. Coolify's docs still label Railpack Beta, and fixes arrived through 4.2.0. Pin your Railpack version, run the deployment smoke coverage the integration ships with, and keep Nixpacks as the fallback build path until a full release cycle passes without a Railpack-shaped incident. Dokploy's parallel Railpack support means bug reports and fixes now come from two communities — helpful, but also a sign the integration surface is still being discovered.

The audit-log and MCP side of 4.1.0 is the sleeper. Structured audit logging for API mutations plus a read-only, instance-level MCP endpoint is precisely the machine-readable-operations direction this blog argues every platform needs: an agent that can inspect fleet state without write access. It deserves its own post; here it is just further evidence that Coolify's 4.x line is building for agent operators, and your build layer should be legible to those same agents — declarative config in git, not click-ops in a dashboard.

The decision tree

With the table and the operational picture in place, the recommendation compresses to five branches:

  • New auto-detected app, common stack (Node, Python, Go)? Default to Railpack. You get the smaller images and the better cache behavior, and you align with where Coolify, Dokploy, and Railway are all heading. Keep the Nixpacks path one toggle away.
  • Existing Nixpacks app that builds fine? Do not migrate it. Nixpacks is in maintenance mode, not end-of-life; a working build has no reason to absorb beta churn. Migrate when you touch the build config anyway.
  • Regulated, multi-vendor, or "boring by policy" shop? Pick Paketo/CNB. The OCI-standard story, the multi-vendor governance, and Heroku's fresh cnb stack commitment are worth more than 38% smaller Node images when auditability outranks velocity. Paketo survived even a GCP sponsorship exit, and Heroku's September 2026 CNB support says the standard is not standing still either.
  • Exotic stack, monorepo, or compliance-pinned base image? Dockerfile, as always. No auto-detector survives contact with a genuinely weird build; the escape hatch is the feature.
  • Revisit trigger: Railpack graduating from beta in Coolify's docs, or a second independent implementation of the plan format. Convergence of consumers (Coolify, Dokploy) on one vendor's tool is encouraging; convergence of producers would be proof. Until then, "track closely, default new apps, don't rewrite the platform" is the whole strategy — which is exactly the verdict this post opened with.

The strongest counter-argument remains the HN one: if Nix's store model could have been used properly all along, then Railpack's advantage is execution quality, not architecture — and execution leads can evaporate. Fair. But execution quality compounded over a year, shipped by the vendor with the most production build data, adopted by the two biggest self-hosted PaaSes, is not a fluke to wait out. It is a trend to ride with a fallback configured.

What this means for the self-hosted PaaS builder

Step back and the pattern is bigger than one beta flag. Buildpacks have now cycled through three philosophies in a decade: Heroku's bespoke scripts, the Nix reproducibility detour, and now a return to layered OCI images with the layering done right — via BuildKit natively (Railpack) or via the CNB lifecycle (Paketo). Both winners agree on the destination: small, cacheable, standards-shaped images from zero-config detection, with a Dockerfile hatch for the rest. They disagree only on who writes the plan.

For a Cluster-API-based platform assembling its own build layer, that agreement is the actionable part. Build the seam — detect, plan, build with BuildKit, cache, push — so the planner is swappable, and you can ride Railpack's velocity today and the CNB standard's gravity tomorrow without re-architecting. The platforms that hard-code one planner will re-live this migration with every cycle. The ones that treat "Nixpacks vs Railpack vs Paketo" as a config value will shrug through the next one.

Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex