Important
pkg
has been deprecated with5.8.1
as the last release. There are a number of successful forked versions ofpkg
already with various feature additions. Further, we’re excited about Node.js 21’s support forsingle 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.
- 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 install
to 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
npm install -g pkg
After installing it, runpkg --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
.Pkg
will followbin
property of the specifiedpackage.json
and use it as entry file. - Path to directory.
Pkg
will look forpackage.json
in the specified directory. See above.
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 examplenode18-macos-x64
ornode14-linux-arm64
:
- nodeRange(node8), node10, node12, node14, node16 or latest
- platformalpine, linux, linuxstatic, win, macos, (freebsd)
- archx64, arm64, (armv6, armv7)
(element) is unsupported, but you may try to compile yourself.
You may omit any element (and specify justnode14
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 aliashost
,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 defaultpkg
has to run the executable of the
targetarch to generate bytecodes:
- Linux: configure binfmt withQEMU.
- macOS: possible to build
x64
onarm64
withRosetta 2
but not opposite. - Windows: possible to build
x64
onarm64
withx64 emulation
but not opposite. - or, disable bytecode generation with
--no-bytecode --public-packages "*" --public
.
macos-arm64
is experimental. Be careful about themandatory code signing requirement.
The final executable has to be signed (ad-hoc signature is sufficient) withcodesign
utility of macOS (orldid
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 andldid
installed.
During packaging processpkg
parses your sources, detects
calls torequire
,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 haverequire(variable)
calls (so called non-literal
argument torequire
) 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 bypkg
.So you must specify the
files - scripts and assets - manually inpkg
property of
yourpackage.json
file.
"pkg":{
"scripts":"build/**/*.js",
"assets":"views/**/*",
"targets":["node14-linux-arm64"],
"outputPath":"dist"
}
The above example will include everything inassets/
and
every.js file inbuild/
,build only fornode14-linux-arm64
,
and place the executable insidedist/
.
You may also specify arrays of globs:
"assets": [ "assets/**/*", "images/**/*" ]
Just be sure to callpkg package.json
orpkg.
to make
use ofpackage.json
configuration.
scripts
is aglob
or list of globs. Files specified asscripts
will be compiled
usingv8::ScriptCompiler
and placed into executable without
sources. They must conform to the JS standards of those Node.js versions
you target (seeTargets), i.e. be already transpiled.
assets
is aglob
or list of globs. Files specified asassets
will be packaged
into executable as raw content without modifications. Javascript
files may also be specified asassets
.Their sources will
not be stripped as it improves execution performance of the
files and simplifies debugging.
See also Detecting assets in source codeand Snapshot filesystem.
Node.js application can be called with runtime options
(belonging to Node.js or V8). To list them typenode --help
ornode --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
You may specify--output
if you create only one executable
or--out-path
to place executables for multiple targets.
Pass--debug
topkg
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.
By default, your source code is precompiled to v8 bytecode before being written
to the output file. To disable this feature, pass--no-bytecode
topkg
.
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 (seehereor here) it results in executables with differing hashed values. Disabling bytecode compilation allows a given input to always have the same output.
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.
Specifying--no-bytecode
will fail if there are any packages in your project that aren't explicitly marked
as public by thelicense
in theirpackage.json
.
By default,pkg
will check the license of each package and make sure that stuff that isn't meant for the public will
only be included as bytecode.
If you do require building pkg binaries for other architectures and/or depend on a package with a broken
license
in itspackage.json
,you can override this behaviour by either explicitly whitelisting packages to be public
using--public-packages "packageA,packageB"
or setting all packages to public using--public-packages "*"
pkg
has so called "base binaries" - they are actually same
node
executables but with some patches applied. They are
used as a base for every executablepkg
creates.pkg
downloads precompiled base binaries before packaging your
application. If you prefer to compile base binaries from
source instead of downloading them, you may pass--build
option topkg
.First ensure your computer meets the
requirements to compile original Node.js:
BUILDING.md
Seepkg-fetchfor more info.
Pass--compress Brotli
or--compress GZip
topkg
to compress further the content of the files store in the exectable.
This option can reduce the size of the embedded file system by up to 60%.
The startup time of the application might be reduced slightly.
-C
can be used as a shortcut for--compress
.
Var | Description |
---|---|
PKG_CACHE_PATH | Used to specify a custom path for node binaries cache folder. Default is~/.pkg-cache |
PKG_IGNORE_TAG | Allows to ignore additional folder created onPKG_CACHE_PATH matching pkg-fetch version |
MAKE_JOB_COUNT | Allow configuring number of processes used for compiling |
Examples
#1 - Using export
exportPKG_CACHE_PATH=/my/cache
pkg app.js
#2 - Passing it before the script
PKG_CACHE_PATH=/my/cache pkg app.js
Command line call to packaged app./app a b
is equivalent
tonode app.js a b
During packaging processpkg
collects project files and places
them into executable. It is called a snapshot. At run time the
packaged application has access to snapshot filesystem where all
that files reside.
Packaged files have/snapshot/
prefix in their paths (or
C:\snapshot\
in Windows). If you usedpkg /path/app.js
command line,
then__filename
value will be likely/snapshot/path/app.js
at run time.__dirname
will be/snapshot/path
as well. Here is
the comparison table of path-related values:
value | withnode |
packaged | comments |
---|---|---|---|
__filename | /project/app.js | /snapshot/project/app.js | |
__dirname | /project | /snapshot/project | |
process.cwd() | /project | /deploy | suppose the app is called... |
process.execPath | /usr/bin/nodejs | /deploy/app-x64 | app-x64 and run in/deploy |
process.argv[0] | /usr/bin/nodejs | /deploy/app-x64 | |
process.argv[1] | /project/app.js | /snapshot/project/app.js | |
process.pkg.entrypoint | undefined | /snapshot/project/app.js | |
process.pkg.defaultEntrypoint | undefined | /snapshot/project/app.js | |
require.main.filename | /project/app.js | /snapshot/project/app.js |
Hence, in order to make use of a file collected at packaging
time (require
a javascript file or serve an asset) you should
take__filename
,__dirname
,process.pkg.defaultEntrypoint
orrequire.main.filename
as a base for your path calculations.
For javascript files you can justrequire
orrequire.resolve
because they use current__dirname
by default. For assets use
path.join(__dirname, '../path/to/asset')
.Learn more about
path.join
in
Detecting assets in source code.
On the other hand, in order to access real file system at run time
(pick up a user's external javascript plugin, json configuration or
even get a list of user's directory) you should takeprocess.cwd()
orpath.dirname(process.execPath)
.
Whenpkg
encounterspath.join(__dirname, '../path/to/asset')
,
it automatically packages the file specified as an asset. See
Assets.Pay attention thatpath.join
must have two
arguments and the last one must be a string literal.
This way you may even avoid creatingpkg
config for your project.
Native addons (.node
files) use is supported. Whenpkg
encounters
a.node
file in arequire
call, it will package this like an asset.
In some cases (like with thebindings
package), the module path is generated
dynamicaly andpkg
won't be able to detect it. In this case, you should
add the.node
file directly in theassets
field inpackage.json
.
The way Node.js requires native addon is different from a classic JS
file. It needs to have a file on disk to load it, butpkg
only generates
one file. To circumvent this,pkg
will create a temporary file on the
disk. These files will stay on the disk after the process has exited
and will be used again on the next process launch.
When a package, that contains a native module, is being installed,
the native module is compiled against current system-wide Node.js
version. Then, when you compile your project withpkg
,pay attention
to--target
option. You should specify the same Node.js version
as your system-wide Node.js to make compiled executable compatible
with.node
files.
Note that fully static Node binaries are not capable of loading native
bindings, so you may not use Node bindings withlinuxstatic
.
const { exec } = require('pkg')
exec(args)
takes an array of command line arguments and returns
a promise. For example:
awaitexec(['app.js','--target','host','--output','app.exe']);
// do something with app.exe, run, test, upload, deploy, etc
This error can be caused by deleting the directory the application is
run from. Or, generally, deletingprocess.cwd()
directory when the
application is running.
This error can be caused by usingNODE_OPTIONS
variable to force to
runnode
with the debug mode enabled. Debugging options are disallowed
, aspkgexecutables are usually used for production environments.
If you do need to use inspector, you canbuild a debuggable Node.jsyourself.
This error can be caused by usingNODE_OPTIONS
variable with some
bootstrap ornode
options causing conflicts withpkg.Some
IDEs, such asVS Code,may add this env variable automatically.
You could check onUnix systems(Linux/macOS) inbash
:
$ printenv|grep NODE
When you are using the--debug
flag when building your executable,
pkg
add the ability to display the content of the virtual file system
and the symlink table on the console, when the application starts,
providing that the environement variable DEBUG_PKG is set.
This feature can be useful to inspect if symlinks are correctly handled,
and check that all the required files for your application are properly
incorporated to the final executable.
$ pkg --debug app.js -o output
$ DEBUG_PKG=1 output
or
C:\> pkg --debug app.js -o output.exe
C:\> set DEBUG_PKG=1
C:\> output.exe
Note: make sure not to use --debug flag in production.