Skip to main content
On this page

Distribution

Coming in Deno 2.9

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:

  1. The --output CLI flag.
  2. The desktop.output field in deno.json (per-platform).
  3. The default: the project name, with the platform-appropriate extension.
>_
# Override per build:
deno desktop --output ./builds/MyApp-1.4.0.dmg main.ts
deno.json
{
  "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 denort binary for the target. Downloaded automatically from github.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:

.github/workflows/release.yml
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)):

deno.json
{
  "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.

Last updated on

Did you find what you needed?

Edit this page
Privacy policy