nFPM (deb/rpm/apk)
Generate Linux packages using nFPM
Anodizer integrates with nFPM to generate native Linux packages.
Classification
Packager — generates deb/rpm/apk/archlinux/termux.deb/ipk packages from Linux binaries, and msix app packages from Windows binaries. Required: not a publisher; always runs unless disabled.
Minimal config
crates:
- name: myapp
nfpm:
- package_name: myapp
formats: [deb, rpm]
vendor: "My Company"
homepage: "https://example.com"
maintainer: "maintainer@example.com"
description: "My application"
license: MITFull config reference
crates:
- name: myapp
nfpm:
- package_name: myapp # optional; defaults to crate name
formats: [deb, rpm] # REQUIRED (>= 1 entry): deb | rpm | apk | archlinux | termux.deb | ipk | msix
# with no formats entry, this config emits nothing
vendor: "" # optional
homepage: "" # optional
maintainer: "" # optional; maintainer email
description: "" # optional
license: "" # optional; SPDX identifier
bindir: /usr/bin # optional; binary installation directory
bin_alias: "" # optional; rename the installed binary inside the package only
file_name_template: "" # optional; custom filename template
contents: [] # optional; additional files to include
dependencies: {} # optional; per-format package dependencies
recommends: [] # optional; soft dependencies
suggests: [] # optional; weaker-than-recommends dependencies
conflicts: [] # optional; packages this conflicts with
replaces: [] # optional; packages this replaces (rename upgrades)
provides: [] # optional; virtual packages provided
scripts: # optional; install scripts
preinstall: ""
postinstall: ""
preremove: ""
postremove: ""
overrides: {} # optional; per-format field overrides
rpm: {} # optional; RPM-specific block (see Per-format blocks)
deb: {} # optional; deb-specific block
apk: {} # optional; apk-specific block
archlinux: {} # optional; archlinux-specific block
ipk: {} # optional; ipk (OpenWrt) block
msix: {} # optional; msix (Windows) blockAuthentication
Not applicable — nFPM generates package files locally. Uploading them to a package repository is handled by the cloudsmith, gemfury, or artifactory publishers.
Common gotchas
formats: the list must match the package types your downstream publishers expect. A mismatch (e.g., configuring cloudsmith fordebbut nFPM only producingrpm) silently skips upload.dependencies: per-format dependency maps allow different deps for deb vs rpm — use theoverridesmap for format-specific fields.- Platform routing: Linux (plus Android/iOS/AIX where supported) binaries feed the Linux formats; Windows binaries feed only
msix. Darwin targets are ignored. A[deb, msix]config packages each target with its matching format and silently skips the rest. msixneeds nfpm >= 2.46.0: older nfpm binaries don't know the msix packager; anodizer surfaces the version floor in the error when packaging fails.
Republish / update behavior
Not applicable — this is a local packaging stage, not a publisher.
nFPM config fields
| Field | Type | Default | Description |
|---|---|---|---|
package_name | string | crate name | Package name |
formats | list | required | Package formats: deb, rpm, apk, archlinux, termux.deb, ipk, msix. At least one entry is required — a config with no formats produces no package. |
vendor | string | Cargo first author | Distributing entity recorded in the rpm/deb Vendor field. Auto-derives from the crate's first Cargo.toml author (with any <email> suffix stripped); set to override. See Vendor. |
homepage | string | none | Homepage URL |
maintainer | string | none | Maintainer email |
description | string | none | Package description |
license | string | none | License identifier |
bindir | string | /usr/bin | Binary installation directory |
bin_alias | string | none | Rename the installed binary inside the package only (e.g. fd → fdfind for the Debian package); the build output and archive are untouched. Templated. |
file_name_template | string | auto | Custom filename template |
contents | list | none | Additional files to include |
dependencies | map | none | Package dependencies keyed by format (e.g., deb: [git]) |
recommends | list | none | Soft (recommended) dependencies |
suggests | list | none | Suggested dependencies (weaker than recommends) |
conflicts | list | none | Packages this package conflicts with |
replaces | list | none | Packages this package replaces (upgrade paths from renamed packages) |
provides | list | none | Virtual packages this package provides |
scripts | object | none | Pre/post install/remove scripts (preinstall, postinstall, preremove, postremove) |
overrides | map | none | Per-format field overrides |
rpm | object | none | RPM-specific block (see Per-format blocks) |
deb | object | none | Deb-specific block |
apk | object | none | APK-specific block |
archlinux | object | none | Archlinux-specific block |
ipk | object | none | IPK (OpenWrt) block |
msix | object | none | MSIX (Windows) block (see MSIX packages) |
Vendor
The Vendor field of a deb/rpm package names the distributing entity. anodizer
auto-derives it from the crate's first Cargo.toml author, stripping any
<email> suffix — so a crate authored by TJ Smith <tj@jarvispro.io> produces
Vendor: TJ Smith without any nfpm config. Set vendor: only to override:
# Cargo.toml
[package]
authors = ["TJ Smith <tj@jarvispro.io>"]nfpm:
- package_name: myapp
formats: [deb, rpm]
# vendor omitted -> derived as "TJ Smith"
vendor: "Some Other Org" overrides the derived value.
File contents
Include additional files in the package:
nfpm:
- package_name: myapp
formats: [deb, rpm]
contents:
- src: config.example.yaml
dst: /etc/myapp/config.yaml
type: config
- src: myapp.service
dst: /usr/lib/systemd/system/myapp.serviceInstall scripts
nfpm:
- package_name: myapp
formats: [deb]
scripts:
preinstall: scripts/preinstall.sh
postinstall: scripts/postinstall.sh
preremove: scripts/preremove.sh
postremove: scripts/postremove.shPer-format blocks
Each package format has an optional dedicated block carrying settings that only
apply to that format. Set the block alongside formats; anodizer emits the
matching nfpm section only for the formats you list.
nfpm:
- package_name: myapp
formats: [rpm, deb, apk, archlinux, ipk]
rpm:
summary: "A fast CLI tool" # RPM Summary tag
compression: zstd # lzma | gzip | xz | zstd
group: "System/Tools"
packager: "Build Team <build@example.com>"
prefixes: ["/usr"] # relocatable RPM prefixes
build_host: "reproducible" # override RPM BuildHost tag
signature:
key_file: signing.gpg
key_passphrase: "" # falls back to NFPM_PASSPHRASE
scripts:
pretrans: scripts/pretrans.sh # %pretrans scriptlet
posttrans: scripts/posttrans.sh # %posttrans scriptlet
deb:
compression: xz # gzip | xz | zstd | none
predepends: ["dpkg (>= 1.17)"] # stronger than Depends
breaks: ["oldpkg (<< 2.0)"] # Breaks relationship
lintian_overrides: ["binary-without-manpage"]
fields: # extra control fields
Built-Using: "rustc"
signature:
key_file: signing.gpg
type: origin # origin | maint | archive
triggers:
activate_noawait: ["ldconfig"]
scripts:
rules: debian/rules
templates: debian/templates # debconf templates
config: debian/config # debconf config script
apk:
signature:
key_file: signing.rsa
key_name: "build@example.com.rsa.pub"
scripts:
preupgrade: scripts/preupgrade.sh
postupgrade: scripts/postupgrade.sh
archlinux:
pkgbase: myapp # base name for split packages
packager: "Build Team <build@example.com>"
scripts:
preupgrade: scripts/preupgrade.sh
postupgrade: scripts/postupgrade.sh
ipk:
abi_version: "1"
auto_installed: false
essential: false
predepends: ["libc"]
tags: ["net"]
fields:
Maintainer: "team@example.com"
msix: # only consumed by formats: [msix]
arch: x64 # optional; x64 | x86 | arm64 | arm | neutral (derived from the target)
publisher: "CN=My Company, O=My Company, C=US" # REQUIRED; must match the signing cert subject
identity:
resource_id: en-us # optional
properties:
display_name: "My App" # optional; defaults to the package name
publisher_display_name: "My Company" # optional; defaults to the package name
logo: assets/logo.png # REQUIRED; package logo image
applications: # REQUIRED (>= 1 entry)
- id: MyApp # REQUIRED
executable: myapp.exe # REQUIRED; path inside the package
entry_point: Windows.FullTrustApplication # optional; this is the default
visual_elements:
display_name: "My App" # optional; defaults to the package name
description: "A fast CLI" # optional; defaults to the package description
background_color: transparent # optional; this is the default
square150x150_logo: assets/150.png # optional; defaults to properties.logo
square44x44_logo: assets/44.png # optional; defaults to properties.logo
dependencies:
target_device_families: # optional; this is the default family
- name: Windows.Desktop
min_version: 10.0.17763.0
max_version_tested: 10.0.22621.0
capabilities:
capabilities: [internetClient] # optional
device_capabilities: [] # optional
restricted: [] # optional; runFullTrust is auto-added for full-trust apps
signature:
pfx_file: cert.pfx # optional; PFX certificate for signingTermux packages
termux.deb builds a .deb for Termux's apt
repository from Android binaries. Termux uses its own architecture
nomenclature — anodizer stamps it into both the conventional filename and
the control-file Architecture, and rewrites install paths under the Termux
filesystem prefix (/data/data/com.termux/files):
| Build arch | deb name | termux.deb name |
|---|---|---|
amd64 | amd64 | x86_64 |
arm64 | arm64 | aarch64 |
386 | i386 | i686 |
armv6 | armhf | arm |
nfpm:
- package_name: myapp
formats: [termux.deb]
maintainer: "team@example.com"dist/linux/myapp_1.0.0_aarch64.deb # aarch64-linux-android, Termux arch nameMSIX packages
msix builds a Windows app package (.msix) from Windows binaries — the one
format that consumes Windows targets (all other formats skip them, and msix
skips non-Windows targets). Requires nfpm >= 2.46.0. The binary is placed at
the package root (an MSIX is a virtual filesystem rooted at the install
location, so bindir does not apply), and the artifact is registered as a
Windows installer (checksummed, signed, released) under dist/windows/.
The conventional filename is {name}_{version}_{arch}.msix with a 4-part
numeric version (1.2.3 → 1.2.3.0) and MSIX arch names (x64, x86,
arm64, arm, neutral):
dist/windows/myapp_1.2.3.0_x64.msix # x86_64-pc-windows-msvc
Signing uses a PFX certificate; the passphrase is never written to config —
anodizer resolves it from NFPM_<ID>_MSIX_PASSPHRASE, then
NFPM_<ID>_PASSPHRASE, then NFPM_PASSPHRASE, and forwards it to nfpm's
environment.
Full example
crates:
- name: myapp
nfpm:
- package_name: myapp
formats: [deb, rpm, apk]
vendor: "My Company"
homepage: "https://github.com/myorg/myapp"
maintainer: "team@example.com"
description: "A fast CLI tool"
license: MIT
dependencies:
deb:
- git
rpm:
- git
contents:
- src: config.example.yaml
dst: /etc/myapp/config.yaml
type: config
overrides:
deb:
dependencies:
- git
- ca-certificates