Pkg
Package your Node.js project into an executable
Install / Use
/learn @vercel/PkgREADME
pkg
[!IMPORTANT]
pkghas been deprecated with5.8.1as the last release. There are a number of successful forked versions ofpkgalready with various feature additions. Further, we’re excited about Node.js 21’s support for single executable applications. Thank you for the support and contributions over the years. The repository will remain open and archived.
This command line interface enables you to package your Node.js project into an executable that can be run even on devices without Node.js installed.
Use Cases
- Make a commercial version of your application without sources
- Make a demo/evaluation/trial version of your app without sources
- Instantly make executables for other platforms (cross-compilation)
- Make some kind of self-extracting archive or installer
- No need to install Node.js and npm to run the packaged application
- No need to download hundreds of files via
npm installto deploy your application. Deploy it as a single file - Put your assets inside the executable to make it even more portable
- Test your app against new Node.js version without installing it
Usage
npm install -g pkg
After installing it, run pkg --help without arguments to see list of options:
pkg [options] <input>
Options:
-h, --help output usage information
-v, --version output pkg version
-t, --targets comma-separated list of targets (see examples)
-c, --config package.json or any json file with top-level config
--options bake v8 options into executable to run with them on
-o, --output output file name or template for several files
--out-path path to save output one or more executables
-d, --debug show more information during packaging process [off]
-b, --build don't download prebuilt base binaries, build them
--public speed up and disclose the sources of top-level project
--public-packages force specified packages to be considered public
--no-bytecode skip bytecode generation and include source files as plain js
--no-native-build skip native addons build
--no-signature skip signature of the final executable on macos
--no-dict comma-separated list of packages names to ignore dictionaries. Use --no-dict * to disable all dictionaries
-C, --compress [default=None] compression algorithm = Brotli or GZip
Examples:
– Makes executables for Linux, macOS and Windows
$ pkg index.js
– Takes package.json from cwd and follows 'bin' entry
$ pkg .
– Makes executable for particular target machine
$ pkg -t node16-win-arm64 index.js
– Makes executables for target machines of your choice
$ pkg -t node16-linux,node18-linux,node16-win index.js
– Bakes '--expose-gc' and '--max-heap-size=34' into executable
$ pkg --options "expose-gc,max-heap-size=34" index.js
– Consider packageA and packageB to be public
$ pkg --public-packages "packageA,packageB" index.js
– Consider all packages to be public
$ pkg --public-packages "*" index.js
– Bakes '--expose-gc' into executable
$ pkg --options expose-gc index.js
– reduce size of the data packed inside the executable with GZip
$ pkg --compress GZip index.js
The entrypoint of your project is a mandatory CLI argument. It may be:
- Path to entry file. Suppose it is
/path/app.js, then packaged app will work the same way asnode /path/app.js - Path to
package.json.Pkgwill followbinproperty of the specifiedpackage.jsonand use it as entry file. - Path to directory.
Pkgwill look forpackage.jsonin the specified directory. See above.
Targets
pkg can generate executables for several target machines at a
time. You can specify a comma-separated list of targets via --targets
option. A canonical target consists of 3 elements, separated by
dashes, for example node18-macos-x64 or node14-linux-arm64:
- nodeRange (node8), node10, node12, node14, node16 or latest
- platform alpine, linux, linuxstatic, win, macos, (freebsd)
- arch x64, arm64, (armv6, armv7)
(element) is unsupported, but you may try to compile yourself.
You may omit any element (and specify just node14 for example).
The omitted elements will be taken from current platform or
system-wide Node.js installation (its version and arch).
There is also an alias host, that means that all 3 elements
are taken from current platform/Node.js. By default targets are
linux,macos,win for current Node.js version and arch.
If you want to generate executable for different architectures,
note that by default pkg has to run the executable of the
target arch to generate bytecodes:
- Linux: configure binfmt with QEMU.
- macOS: possible to build
x64onarm64withRosetta 2but not opposite. - Windows: possible to build
x64onarm64withx64 emulationbut not opposite. - or, disable bytecode generation with
--no-bytecode --public-packages "*" --public.
macos-arm64 is experimental. Be careful about the mandatory code signing requirement.
The final executable has to be signed (ad-hoc signature is sufficient) with codesign
utility of macOS (or ldid utility on Linux). Otherwise, the executable will be killed
by kernel and the end-user has no way to permit it to run at all. pkg tries to ad-hoc
sign the final executable. If necessary, you can replace this signature with your own
trusted Apple Developer ID.
To be able to generate executables for all supported architectures and platforms, run
pkg on a Linux host with binfmt (QEMU emulation) configured and ldid installed.
Config
During packaging process pkg parses your sources, detects
calls to require, traverses the dependencies of your project
and includes them into executable. In most cases you
don't need to specify anything manually.
However your code may have require(variable) calls (so called non-literal
argument to require) or use non-javascript files (for
example views, css, images etc).
require('./build/' + cmd + '.js');
path.join(__dirname, 'views/' + viewName);
Such cases are not handled by pkg. So you must specify the
files - scripts and assets - manually in pkg property of
your package.json file.
"pkg": {
"scripts": "build/**/*.js",
"assets": "views/**/*",
"targets": [ "node14-linux-arm64" ],
"outputPath": "dist"
}
The above example will include everything in assets/ and
every .js file in build/, build only for node14-linux-arm64,
and place the executable inside dist/.
You may also specify arrays of globs:
"assets": [ "assets/**/*", "images/**/*" ]
Just be sure to call pkg package.json or pkg . to make
use of package.json configuration.
Scripts
scripts is a glob
or list of globs. Files specified as scripts will be compiled
using v8::ScriptCompiler and placed into executable without
sources. They must conform to the JS standards of those Node.js versions
you target (see Targets), i.e. be already transpiled.
Assets
assets is a glob
or list of globs. Files specified as assets will be packaged
into executable as raw content without modifications. Javascript
files may also be specified as assets. Their sources will
not be stripped as it improves execution performance of the
files and simplifies debugging.
See also Detecting assets in source code and Snapshot filesystem.
Options
Node.js application can be called with runtime options
(belonging to Node.js or V8). To list them type node --help or node --v8-options.
You can "bake" these runtime options into packaged application. The app will always run with the options
turned on. Just remove -- from option name.
You can specify multiple options by joining them in a single string, comma (,) separated:
pkg app.js --options expose-gc
pkg app.js --options max_old_space_size=4096
pkg app.js --options max-old-space-size=1024,tls-min-v1.0,expose-gc
Output
You may specify --output if you create only one executable
or --out-path to place executables for multiple targets.
Debug
Pass --debug to pkg to get a log of packaging process.
If you have issues with some particular file (seems not packaged
into executable), it may be useful to look through the log.
Bytecode (reproducibility)
By default, your source code is precompiled to v8 bytecode before being written
to the output file. To disable this feature, pass --no-bytecode to pkg.
Why would you want to do this?
If you need a reproducible build process where your executable hashes (e.g. md5, sha1, sha256, etc.) are the same value between builds. Because compiling bytecode is not deterministic (see here or here) it results in executables with differing hashed values. Disabling bytecode compilation allows a given input to always have the same output.
Why would you NOT want to do this?
While compiling to bytecode does not make your source code 100% secure, it does
add a small layer of security/privacy/obscurity to your source code. Turning
off bytecode compilation causes the raw source code to be written directly to
the executable file. If you're on *nix machine and would like an example, run
pkg with the --no-bytecode flag, and use the GNU strings tool on the
output. You then should be able to grep your source code.
Other considerations
S
