deno compile: build a standalone executable from a TypeScript file
- tool
- Deno
- versions
- Deno 2.x
- os
- any
deno compile -o hello main.ts bundles a script and everything it imports, together with a copy of the Deno runtime, into one executable file. The result runs on machines that do not have Deno installed. Permissions are fixed at compile time: pass the --allow-* flags the program needs to deno compile, and add --target to build for another operating system or CPU.
Compile and run
export NO_COLOR=1
cat > main.ts <<'EOF'
const name = Deno.args[0] ?? "world";
console.log(`hello ${name}, HOME is set: ${Deno.env.get("HOME") !== undefined}`);
EOF
npx --yes deno@2 compile --quiet --allow-env=HOME -o hello main.ts
./hello AdaOutput
hello Ada, HOME is set: true
The program reads an environment variable, so it was compiled with --allow-env=HOME; without that flag the compiled program fails at Deno.env.get with a NotCapable error instead of prompting. Arguments after the executable name arrive in Deno.args. On Windows the output file gets an .exe extension automatically. The example uses npx deno@2 so it needs only Node.js; the first compile also downloads the runtime that is embedded in the file.
Options
| Option | What it does |
|---|---|
| -o, --output PATH | Name of the executable (default: derived from the script name) |
| --allow-read, --allow-net, -A | Permissions baked into the binary; same syntax as deno run |
| --target TRIPLE | Cross-compile, for example x86_64-unknown-linux-gnu, aarch64-apple-darwin, x86_64-pc-windows-msvc |
| --include PATH | Embed extra files or folders the program loads at run time |
| --icon FILE.ico | Set the icon of a Windows executable |
| --no-terminal | Windows only: build a GUI program without a console window |
| --no-check | Skip type-checking |
Arguments placed after the script name are baked in too: deno compile -o srv main.ts --port 8080 makes --port 8080 the first arguments of every run, before the ones the user types.
Pitfalls
- Dynamic imports and file reads. Only modules Deno can see statically are embedded. A file loaded with
Deno.readTextFileor a computedimport()must be added with--include, otherwise it is read from disk at run time and is missing on other machines. - npm packages with native add-ons (
.nodefiles) may not work inside a compiled binary; check them before you rely on it. - Code signing. macOS and Windows may warn about an unsigned executable downloaded from the internet. Sign it with the platform tools if you distribute it.
- Permissions at run time. Users cannot add permissions to a compiled program; recompile with the right flags.
How to confirm it worked
Run the file directly (./hello or hello.exe) on a machine or container without Deno. file hello on Linux and macOS shows the executable format and architecture, which tells you whether --target produced what you expected.
FAQ
Does the compiled program still ask for permissions?
It uses the permissions you passed to deno compile, such as --allow-read or --allow-net, and nothing else. There is no interactive prompt in a compiled binary: an operation without permission fails.
Can I build a Windows .exe on Linux or macOS?
Yes. Pass --target x86_64-pc-windows-msvc. Deno downloads the runtime for that target and writes the executable; supported targets are listed in the deno compile documentation.
Why is the executable so large?
It contains a complete Deno runtime (V8 included) plus your code, so even a one-line program is tens of megabytes. Size grows only a little with your own code.