How to Use Nixpacks: A Beginner's Guide
CodexFuse Team · 2026-11-01T13:20:00.000Z · 12 min read
What Is Nixpacks, and How Does It Work?
Learning how to use Nixpacks starts with one mental model: Nixpacks looks at your source code, figures out which language and framework you used, and generates a Dockerfile for you — so you never have to write one by hand. It is an open-source tool built by Railway, and it is the same build engine that powers one-click app deploys on self-hosted PaaS platforms like Coolify (see our Coolify beginner guide for the platform side of the story).
The process always runs in two steps. This nixpacks tutorial follows the same arc the official docs use: first the plan step analyzes your source directory and produces a reproducible build plan in JSON, describing which packages to install, which commands to run, and what starts the app. Then the build step turns that plan into an OCI-compliant image using Docker BuildKit — literally by generating a Dockerfile and running docker build on it.
Inside the plan, work is organized into phases. Each phase is a named group of steps that runs in topological order, and phases can declare dependencies on each other. This is the whole Nixpacks worldview in one sentence: detect, plan, generate, build.
| Phase | What it does | Example for a Node app |
|---|---|---|
| setup | Installs system-level packages from Nix or Apt | Installs nodejs and any extra nix packages you request |
| install | Downloads the language or framework dependencies | Runs npm install (or yarn/pnpm/bun install) |
| build | Generates everything needed to run the app | Runs npm run build, then a default start command is configured |
How Do You Use Nixpacks? The 6-Step Quick Start
Here is the complete beginner arc — everything below is just detail on these six steps.
- Install Nixpacks with one command (Homebrew, the install script, Scoop, or cargo).
- Move into your app folder — a folder with a recognizable manifest file like
package.json,requirements.txt, orgo.mod. - Run
nixpacks planto see exactly which provider was detected and which phases will run. - Run
nixpacks buildto generate the Dockerfile and build the image with Docker. - Override what you don't like with a
nixpacks.tomlfile orNIXPACKS_environment variables. - Ship it — run the image locally with
docker run, or let a platform like Coolify run the same Nixpacks pipeline for you.
One Honest Note: Nixpacks Is in Maintenance Mode
Say this once, up front, so there are no surprises: the Nixpacks README and docs now carry a banner stating that the project is in maintenance mode and is not under active development, and Railway recommends Railpack as its replacement. The most recent release, v1.41.0, added Node 24 support and these maintenance notices. None of this makes Nixpacks stop working — it is a stable, finished tool — but it does mean new features are unlikely, and if you are choosing a build system for a brand-new project in 2026, you should know the official replacement exists.
Step 1: Install Nixpacks
Installation takes under a minute on any platform. The official install page lists one-liners for each: brew install nixpacks on macOS, curl -sSL https://nixpacks.com/install.sh | bash on Linux, irm https://nixpacks.com/install.ps1 | iex in PowerShell, scoop install nixpacks on Windows via Scoop, plus per-release .deb and .msi files from GitHub. If you already have Rust installed (1.60 or newer), cargo install nixpacks builds it from source.
Verify it with nixpacks --version. The only real prerequisite for building is Docker itself, which must be installed and running — nixpacks build hands the generated Dockerfile to docker build via BuildKit.
Step 2: Run Your First Build
A first build is one command: nixpacks build ./my-app --name my-app. Nixpacks scans the directory, picks a provider, generates the Dockerfile, builds the image, and tags it my-app. Then docker run -it my-app starts it, exactly like any hand-built image — if you are new to images and containers, our Docker beginner guide covers the basics.
The build accepts inline overrides for almost everything: --install-cmd, --build-cmd, and --start-cmd replace the detected commands, --pkgs adds Nix packages, --apt adds Debian packages, and --env sets environment variables. There is also --out (or -o), which skips Docker entirely and saves the generated output directory — handy for inspecting what Nixpacks would have built without needing Docker running.
The Plan Lesson: Always Read the Plan Before You Build
Run nixpacks plan on your app folder before you build — this is the habit that separates beginners from people who fight the tool. The command prints the full build plan as JSON (add --format toml if you prefer TOML) to stdout, with errors on stderr, so you can see the detected provider, the phases, the packages, and the start command before a single layer is built.
When a build surprises you — wrong Node version, missing system package, weird start command — the plan is where the surprise is hiding. Read it first, fix the config, then build. Most Nixpacks debugging is plan reading, not Dockerfile archaeology.
How Nixpacks Detects Your Stack (Providers)
Nixpacks ships with 24 nixpacks providers — node, python, go, rust, ruby, php, java, elixir, deno, bun-era tooling, and more — each of which is matched against the files in your source directory. The provider then suggests Nix packages plus install, build, and start commands, and every suggestion is overridable by you.
Detection is pure convention: the Node provider looks for package.json, the Python provider fires on main.py, requirements.txt, pyproject.toml, or Pipfile, and the Go provider triggers when it finds a main.go file. This convention lesson is the whole mental model — Nixpacks does not understand your code, it recognizes your project layout and applies the matching recipe.
| Language | Detection signal | Version source |
|---|---|---|
| Node.js | package.json | NIXPACKS_NODE_VERSION, then engines.node, then .nvmrc |
| Python | main.py, requirements.txt, pyproject.toml, Pipfile | NIXPACKS_PYTHON_VERSION, .python-version, runtime.txt, .tool-versions |
| Go | main.go | Version parsed from go.mod (1.18–1.23, default 1.22) |
If two recipes could match your project — a Python backend next to a Node frontend, for example — set providers = ['...', 'python'] in your config to control which providers run and in what order, instead of hoping auto-detection guesses right.
Overriding Everything with nixpacks.toml
The nixpacks.toml file (or nixpacks.json if you prefer JSON) lives in your app root and is picked up automatically; you can point at a different file with --config or the NIXPACKS_CONFIG_FILE variable. Merge priority runs provider < file < environment < CLI — a value set on the command line beats the same value in the file, which beats the provider default, and writing "..." inside an array extends it instead of replacing it.
Each phase ([phases.setup], [phases.install], [phases.build], or a custom phase you name yourself) accepts cmds, nixPkgs, aptPkgs, dependsOn, cacheDirectories, and more. The [start] section sets the container's start command, and top-level keys like [variables] inject environment variables into the build.
[phases.setup]
nixPkgs = ["...", "ffmpeg"]
[phases.install]
cmds = ["npm ci --omit=dev"]
[variables]
NODE_ENV = "production"
Pinning Versions with NIXPACKS_ Environment Variables
Version detection is the reproducibility gotcha — say it once and pin it. For Node, Nixpacks resolves the version from NIXPACKS_NODE_VERSION first, then the engines.node field in package.json, then .nvmrc (which can hold a plain version or an lts/* alias), and it only understands major versions like 18.x or 20. The same explicit pinning exists for Python (NIXPACKS_PYTHON_VERSION) and Go (NIXPACKS_GO_BIN to pick the binary).
The fix is one line: export NIXPACKS_NODE_VERSION=20 before building, or set it wherever your platform stores build-time variables — this nixpacks node version pin is the single most reliable way to keep builds reproducible. Node builds also get sensible defaults baked in — CI=true, NODE_ENV=production, NPM_CONFIG_PRODUCTION=false — and the package manager comes from the packageManager field or your lockfile, with corepack enabled on Node 16 and up.
Other useful overrides follow the same pattern: NIXPACKS_INSTALL_CMD, NIXPACKS_BUILD_CMD, and NIXPACKS_START_CMD replace the three lifecycle commands, NIXPACKS_PKGS and NIXPACKS_APT_PKGS add packages, and NIXPACKS_NO_CACHE forces a clean build.
The Coolify Lane: Using Nixpacks in Production
Coolify is where most people meet Nixpacks without realizing it — this nixpacks coolify combination is the default build path on the platform: the platform detects your app's language from its codebase, generates the Dockerfile with Nixpacks, and builds the image from those generated instructions. Draw the line once: Coolify is the platform, Nixpacks is its build pack; they are not rivals, they are two layers of the same deploy.
In Coolify, the detected install, build, and start commands show up under General — override only what you need there. A nixpacks.toml or nixpacks.json committed to the repo is read for anything deeper (extra packages, custom phases, start behavior), and NIXPACKS_ variables go under Environment Variables with "Available during build" enabled. Static apps get a small twist: Coolify copies your chosen Publish Directory into an Nginx image instead.
If Coolify picks the wrong provider, the debugging order is fixed: check that your Base Directory actually contains the right manifest and lockfile, inspect the generated plan in the deployment log, and remove conflicting lockfiles or manifests — or just commit a config file that pins the provider. Since deploys start from a git push, keeping your repo tidy matters; our GitHub beginner guide covers the push side of that workflow.
| Your situation | Use Nixpacks | Use a Dockerfile |
|---|---|---|
| Standard app, manifest + lockfile in place | Yes — auto-detection is enough | Unnecessary complexity |
| One or two small overrides (a package, a command) | Yes — toml or env vars handle it | Not needed yet |
| Exact base image, OS config, users, build stages | No — fighting the tool | Yes — full control |
| Multi-stage builds, custom copy instructions | No | Yes |
When to Graduate to a Hand-Written Dockerfile
The control-vs-convenience line is simple and worth saying once: Nixpacks is the right choice until the moment your build needs something the generated Dockerfile cannot express cleanly — a specific base image, multi-stage builds with custom copy instructions, OS-level users, or unusual system configuration. At that point a hand-written Dockerfile is not a failure of the tool — it is the tool having done its job of getting you 90% of the way there. That is the whole nixpacks vs dockerfile decision in one rule.
Most projects graduate gradually: start with pure Nixpacks, add a nixpacks.toml for overrides, reach for NIXPACKS_ variables for per-environment tweaks, and only switch to a Dockerfile when the overrides start outnumbering the defaults. If you self-host that final setup on a VPS, our what is VPS hosting explainer covers the server side.
7 Beginner Mistakes
The same seven mistakes show up in every beginner Nixpacks thread — this table is the checklist to run through before you ask for help.
| Mistake | Why it breaks | The fix |
|---|---|---|
Skipping nixpacks plan | You build blind and debug symptoms | Read the plan first, every time |
| Not pinning the Node version | Detection picks a surprise major version | Set NIXPACKS_NODE_VERSION explicitly |
| Conflicting lockfiles in the repo | Provider picks the wrong package manager | Keep one lockfile, delete the rest |
Putting NIXPACKS_ vars in runtime env only | They never reach the build step | Enable "available during build" (Coolify) or export before building |
| Assuming Nixpacks replaces Docker | Confusion when builds need BuildKit | Docker must be installed — Nixpacks only generates the Dockerfile |
| Treating maintenance mode as a bug | Expecting new features that won't come | Know Railpack is the official successor for new projects |
| Writing a Dockerfile on day one | You pay full complexity cost for zero benefit | Start with Nixpacks, graduate only when overrides outnumber defaults |
Sources
Every fact in this guide was verified from pages fetched on November 1, 2026: the Nixpacks repository at github.com/railwayapp/nixpacks (3,556 stars, 319 forks, MIT license, maintenance-mode banner); the v1.41.0 release notes (Node 24 support, maintenance notices); the official CLI reference at nixpacks.com/docs/cli; the install page at nixpacks.com/docs/install; the architecture explainer at nixpacks.com/docs/how-it-works; the configuration file reference at nixpacks.com/docs/configuration/file; the environment-variables reference at nixpacks.com/docs/configuration/environment; the Node provider page at nixpacks.com/docs/providers/node; the Python provider page at nixpacks.com/docs/providers/python; the Go provider page at nixpacks.com/docs/providers/go; the full provider list on the nixpacks GitHub docs tree; and the Coolify Nixpacks integration docs at coolify.io/docs/applications/builds/nixpacks/overview.
Frequently Asked Questions
What is Nixpacks?
Nixpacks is an open-source build tool from Railway that detects your app's language and framework from its source code and generates a Dockerfile for it, which is then built with Docker BuildKit into an OCI image. It organizes the build into phases — setup, install, and build — and every detection is overridable through a config file, environment variables, or CLI flags.
Is Nixpacks still maintained?
Nixpacks is in maintenance mode: the README and docs state it is not under active development, and Railway recommends Railpack as its replacement. The latest release (v1.41.0) added Node 24 support and the maintenance notices. It remains stable and usable, but new features are unlikely.
How do I see what Nixpacks will build before building?
Run nixpacks plan ./path/to/app. It prints the full build plan as JSON (or TOML with --format toml) — the detected provider, phases, packages, and start command — so you can inspect and fix the configuration before spending time on a build.
How do I pin the Node.js version in Nixpacks?
Set the NIXPACKS_NODE_VERSION environment variable to a major version like 20 — it takes priority over the engines.node field in package.json and the .nvmrc file. Only major versions (for example 18.x or 20) are supported.
Does Nixpacks replace Dockerfiles?
Not exactly — Nixpacks generates a Dockerfile and then runs docker build on it, so Docker with BuildKit is still required. Use Nixpacks while your app follows standard conventions and graduate to a hand-written Dockerfile when you need exact base images, custom build stages, or OS-level control.