On this page
Distribution
deno desktop ships in Deno v2.9.0 and is not in a stable release yet. To try
it now, run deno upgrade canary to install the
canary build. The command, configuration
keys, and TypeScript APIs may still change before the feature is stable.
deno desktop cross-compiles from any host. The same machine builds for macOS
Intel, macOS arm64, Windows x86_64, Linux arm64, and Linux x86_64. Backend
binaries (CEF, WebView, etc.) are downloaded as needed.
Per-platform output Jump to heading
# Build for the host platform.
deno desktop main.ts
# Build for a specific target.
deno desktop --target aarch64-apple-darwin main.ts
# Build for every supported target in one go.
deno desktop --all-targets main.ts
Supported triples:
| Triple | OS | Architecture |
|---|---|---|
aarch64-apple-darwin |
macOS | arm64 |
x86_64-apple-darwin |
macOS | Intel |
x86_64-pc-windows-msvc |
Windows | x86_64 |
aarch64-unknown-linux-gnu |
Linux | arm64 |
x86_64-unknown-linux-gnu |
Linux | x86_64 |
The CLI fetches the matching prebuilt denort and the matching prebuilt backend
archive automatically. No platform-specific toolchain is needed on the host.
Output formats Jump to heading
The output extension determines the format:
macOS Jump to heading
| Output | Produced by |
|---|---|
MyApp.app/ |
Default; .app bundle. |
MyApp.dmg |
hdiutil; drag-to-Applications disk image. |
The .app bundle has the standard layout:
MyApp.app/
Contents/
Info.plist
MacOS/
MyApp # the launcher
Resources/
icon.icns
Frameworks/
Chromium Embedded Framework.framework/ # CEF backend
…
Self-extracting mode is enabled by default: the embedded virtual filesystem (your code, framework build outputs, static assets) extracts to disk on first run so frameworks like Next.js find their build output relative to CWD.
Windows Jump to heading
| Output | Produced by |
|---|---|
MyApp/ |
Default; directory with a launcher and support files. |
The MyApp/ directory contains:
MyApp/
MyApp.bat # launcher
denort.dll # Deno runtime + your code
*.dll # rendering backend and CEF libraries
resources.pak, locales/ # CEF support files
AppIcon.ico # icon (optional)
Zip the directory or feed it into an installer toolchain. Windows MSI output is not yet implemented; for now, use a third-party installer generator such as Inno Setup, NSIS, or WiX with the directory as input.
Linux Jump to heading
| Output | Produced by |
|---|---|
my-app/ |
Default; app directory with launcher script. |
my-app.AppImage |
Single-file portable bundle. |
The app directory layout:
my-app/
my-app # launcher shell script
libdenort.so # Deno runtime + your code
*.so # rendering backend and CEF libraries
resources.pak, locales/ # CEF support files
AppIcon.png # icon (optional)
AppImage is the most portable Linux format: one file, no install step, runs on
any modern distro. deno desktop builds it directly: it packs the app directory
into a SquashFS image and prepends the AppImage Type-2 runtime, adding the
required AppRun, .desktop, and icon entries. There is no external tool to
install (no appimagetool), and it works from any build host, so you can
produce a Linux .AppImage while cross-compiling from macOS or Windows.
.deb / .rpm packaging is not yet implemented. For now, use fpm or
dpkg-deb against the app directory.
Choosing the output path Jump to heading
The output path can be set in three places, in priority order:
- The
--outputCLI flag. - The
desktop.outputfield indeno.json(per-platform). - The default: the project name, with the platform-appropriate extension.
# Override per build:
deno desktop --output ./builds/MyApp-1.4.0.dmg main.ts
{
"desktop": {
"output": {
"macos": "./dist/macos/MyApp.app",
"windows": "./dist/windows/MyApp",
"linux": "./dist/linux/my-app.AppImage"
}
}
}
Cross-compilation details Jump to heading
Cross-compiling from one OS to another requires:
- The right
denortbinary for the target. Downloaded automatically fromgithub.com/denoland/deno/releases, matching your local Deno version. - The right backend archive for the target. Downloaded automatically, pinned to your Deno version.
Both downloads are SHA-256 verified and cached under <deno_dir>/.
There is no Rust toolchain involved in cross-compiling a desktop app. You
are not compiling Rust on the host; you are downloading prebuilt artifacts for
the target and packaging them with your code. This is the same model as
deno compile --target.
Icon assembly (.icns, .ico) and the Linux .AppImage are produced on any
host. The one exception is the macOS .dmg, which shells out to hdiutil and
therefore must be built on a macOS host. To produce a .dmg from another
platform, build it on a macOS CI machine.
CI Jump to heading
A typical GitHub Actions matrix builds platform-native installers in parallel:
jobs:
build:
strategy:
matrix:
include:
- { os: macos-14, target: aarch64-apple-darwin }
- { os: macos-15-intel, target: x86_64-apple-darwin }
- { os: windows-latest, target: x86_64-pc-windows-msvc }
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- uses: denoland/setup-deno@v2
- run: deno desktop --target ${{ matrix.target }} main.ts
- uses: actions/upload-artifact@v4
with:
name: my-app-${{ matrix.target }}
path: dist/
Cross-compiling from a single host (e.g. only running on ubuntu-latest with
--all-targets) works for the bundles and the Linux .AppImage. Only the macOS
.dmg needs a macOS host.
Code signing Jump to heading
On macOS, deno desktop code-signs the bundle for you. By default it applies an
ad-hoc signature (-), which gives the app a stable code identity, enough
for the OS to grant notification permission,
but not enough to distribute without Gatekeeper warnings. The CEF helper
processes' bundle identifiers are harmonized with the main bundle id
automatically.
To produce a distributable, notarizable bundle, set a real signing identity in
deno.json (signing must run on a macOS host, since it shells out to
codesign(1)):
{
"desktop": {
"app": { "identifier": "com.example.myapp" },
"macos": {
"codesignIdentity": "Developer ID Application: Acme, Inc. (TEAMID)"
}
}
}
With a real identity, the bundle is signed with Hardened Runtime and a secure
timestamp. Notarization is still a separate step. Submit the signed bundle
with xcrun notarytool submit and staple the ticket.
On Windows, sign the produced executables (the backend .exe and denort.dll
in the output directory) externally for now, e.g.
signtool sign /f cert.pfx /tr <timestamp> <file>.
Distributing updates after release Jump to heading
Once your binary is in users' hands, ship updates via
Deno.autoUpdate(): bsdiff patches shipped
from your own server, no app store required.