Self-hosted service
railwayapp/nixpacks avatar
railwayapp/nixpacks

Nixpacks: generating an OCI image from app source with Nix packages

App source + Nix packages + Docker = Image

3,554 stars317 forksRustMIT

At a glance

What is it?
Nixpacks turns a source directory into a Docker image by detecting the language and pulling system and language dependencies from Nix. It is in maintenance mode, and the README points users to Railpack as a replacement.
Who is it for?
Nixpacks is for teams that already have Railway or a similar platform generating images from source and want the build logic readable and configurable through nixpacks.toml. It is not the right pick for a new project, because the README states the project is in maintenance mode and recommends Railpack instead.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 16 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What Nixpacks solves, and for whom

The README states the project started at Railway as an alternative to Buildpacks, and that it tries to address problems seen while deploying thousands of user apps to the Railway platform. The stated change is that system and language dependencies come from the Nix ecosystem rather than from a curated buildpack stack. The tagline in the README is "App source + Nix packages + Docker = Image".

The audience is narrower than the tagline suggests. Nixpacks is for teams that want a build step which inspects a source directory, decides what runtime and system libraries it needs, and emits an OCI compliant image without hand-writing a Dockerfile for every service. The examples directory shows the range that implies: clojure, cobol, crystal, csharp-api, csharp-cli, dart, deno, elixir-ecto, gleam, and more. That breadth is the point. A single tool with per-language providers beats a folder of near-identical Dockerfiles.

The trade-off is that the build is generated, not written. When the generated plan is wrong, you debug a plan file rather than a Dockerfile you wrote yourself.

How a source directory becomes a build plan

The mechanism visible in the repository is a two-stage pipeline. First Nixpacks scans the directory and selects a provider for the detected language. Then it assembles a plan: a list of Nix packages, a list of build and start commands, and any environment variables the provider needs. The plan is rendered into a shell script that runs inside the image build.

The Makefile confirms this shape. The debug-single target runs cargo run -- build $(TEST_TARGET) --out $(TEST_TARGET), then reads the generated script at $(TEST_TARGET)/.nixpacks/build.sh and rewrites docker build into a buildx debug invocation so you can drop into a shell mid-build. That tells you the generated artifacts are ordinary files on disk: .nixpacks/build.sh is a script, and the plan is inspectable before anything is built.

Because dependencies come from Nix, the image gets a shared source of packages rather than per-language base images. The Makefile also carries a latest-nixpkgs-revision target that queries the NixOS/nixpkgs commit API, which is how the pinned nixpkgs archive constants get updated. That pin is what makes builds reproducible across time, and it is also what makes them stale until someone bumps it.

Installing Nixpacks and building a first image

The repository ships install.sh and install.ps1 at the top level, plus an uninstall.sh, so installation is a script rather than a package manager step. The Cargo.toml declares a nixpacks binary and a nixpacks library, and the crate is published on crates.io, so cargo install is also a route.

Once installed, the entry point is the build subcommand. The Makefile shows the exact form used for local testing:

bash
cargo run -- build $(TEST_TARGET) --name node

That command takes a source directory and an image name. To keep the generated artifacts instead of only producing an image, the Makefile passes an output directory:

bash
cargo run -- build $(TEST_TARGET) --out $(TEST_TARGET)

After that run, the directory contains a .nixpacks folder. Inside it, plan.json holds the provider's decisions and build.sh holds the shell script that the image build executes. Read plan.json first: it lists the Nix packages and the commands the provider chose, which is the fastest way to see whether the detection matched your project.

Configuration lives in nixpacks.toml or nixpacks.json at the root of the source directory. The examples directory contains config-toml-file, config-json-file, and config-from-environment-variables, which is the clearest signal that both file formats and environment overrides are supported. The README itself does not document the config schema; the docs site at nixpacks.com is where the README sends you.

Where the generated build breaks down

The most concrete limitation is stated by the project itself. The README carries a maintenance mode notice saying the project is not under active development and recommending Railpack as a replacement. That is unusual honesty, and it should shape any adoption decision more than any feature list.

The second limitation is structural. Detection is a guess based on files present in the directory. A monorepo with several services, or a project whose build depends on a tool the provider does not know about, will produce a plan that compiles but does not match what you intended. The escape hatch is nixpacks.toml, but once you have overridden the packages and the build commands, you have written most of a Dockerfile in TOML and lost the convenience that motivated the tool.

The third is the nixpkgs pin. Because system packages come from a pinned Nix archive, a language version or library that landed in nixpkgs after that pin is not available until the constants are updated, which the Makefile does through the latest-nixpkgs-revision target. If you need a runtime released after the pin, you are either overriding the package or waiting.

Finally, this is the wrong tool when the build is already well expressed as a Dockerfile. Nixpacks adds a detection layer and a Nix dependency layer on top of Docker. For a single service with a stable, hand-tuned multi-stage Dockerfile, that layer is overhead.

Nixpacks compared with Buildpacks and Dockerfiles

Buildpacks is the comparison the README makes directly. Both take source and produce an image without a Dockerfile. The difference the README names is the dependency source: Buildpacks use their own curated buildpack stacks, while Nixpacks pulls system and language dependencies from Nix. In practice that means the set of available packages is the nixpkgs set, and the version pinning story is the nixpkgs revision rather than a buildpack release.

Against a Dockerfile the difference is who writes the build. A Dockerfile is explicit and portable; every step is a line you can read, and docker build works anywhere Docker does. Nixpacks generates that content. You gain not maintaining per-language base image logic; you lose the ability to read the whole build in one file you authored. The .nixpacks/build.sh output narrows that gap, since the generated script is inspectable, but it is still generated.

The comparison with Railpack is not really a comparison, because the README presents Railpack as the successor rather than a peer. If you are choosing today, that sentence in the README is the deciding fact. Nixpacks remains relevant for existing deployments that already depend on its plan format and nixpacks.toml behaviour.

Maintenance status, release cadence and licence

The last push to the repository was on 2026-09-16, and the repository is not archived. The most recent release listed is v1.41.0 from 2025-10-24, preceded by v1.40.0 on 2025-09-08 and v1.39.0 on 2025-05-12. Those dates sit alongside the README's maintenance mode notice, and the notice is the more meaningful signal: commits can continue while the project is explicitly not under active development.

For upgrade cost, the Cargo.toml declares rust-version = "1.60" while the README badge states Rust 1.70+. That mismatch is worth noting if you build from source, since the manifest is the stricter authority for cargo. The package version in Cargo.toml is 1.41.0, matching the newest release.

The licence is MIT, declared in both the README badge set and the Cargo.toml license field. MIT is permissive: it allows use, modification and redistribution with the licence text retained. That is the extent of what the repository states, and it is not legal advice. If you redistribute a built image or embed the binary, check the notice requirements against your own distribution model.

The dependency list is large, including actix-web and tokio, which suggests the binary carries more than a CLI's worth of surface. That affects binary size and the audit surface, not correctness.

Editorial conclusion

Nixpacks is for teams that already have Railway or a similar platform generating images from source and want the build logic readable and configurable through nixpacks.toml. It is not the right pick for a new project, because the README states the project is in maintenance mode and recommends Railpack instead. Before adopting it, run nixpacks build against one real repository and read the generated .nixpacks/plan.json and .nixpacks/build.sh, since those two files show exactly which packages and build commands the provider selected.

Frequently asked questions

What is Nixpacks?

Nixpacks takes a source directory and produces an OCI compliant image that can be deployed anywhere. The README summarises it as app source plus Nix packages plus Docker equalling an image.

What is nixpacks.toml?

It is the TOML configuration file placed in the source directory to override what the provider detected. The repository also contains examples for a JSON config file and for configuration from environment variables.

How does Nixpacks differ from Buildpacks?

The README states Nixpacks was started as an alternative to Buildpacks, and that the biggest change is pulling system and language dependencies from the Nix ecosystem instead of a curated buildpack stack.

What are the differences between Nixpacks and Railpack?

The README does not compare them feature by feature. It states that Nixpacks is in maintenance mode and is not under active development, and recommends Railpack as a replacement.

Is there a Nixpacks alternative?

The README names Railpack as the recommended replacement. Buildpacks is the other alternative it discusses, with the difference being that Buildpacks draw dependencies from their own stacks rather than from Nix.

Official sources

  1. License: MIT
  2. Project website
  3. railwayapp/nixpacks on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/railwayapp-nixpacks.svg)](https://hysenlabs.com/projects/railwayapp-nixpacks)