Five flags turn a macOS app into a disk image, and notarization stays with you
Create a good-looking DMG for your macOS app in seconds
At a glance
- What is it?
- create-dmg is a global npm command that wraps a built .app bundle into a styled disk image. It takes an app path and a destination, offers five options, signs the image when a certificate is available, and stops short of notarization.
- Who is it for?
- create-dmg fits a team that ships one .app outside the Mac App Store and already knows how to sign its build. Skip it if you need a password protected image, a Windows build step, or a layout the five options do not describe.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Activity is slowing. The repository last received commits 7 months ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 13, 2026, and from our analysis. They are not legal advice.
Star history
- sindresorhus/create-dmg5,394 ★ · +21
| Repository | 2026-09-23 | 2026-11-13 |
|---|---|---|
| sindresorhus/create-dmg | 5.4k | 5.4k |
21 Stars in the last 51 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Five options and one positional argument
The command line is the whole interface: `$ create-dmg <app> [destination]`. The first positional argument is the path to an app bundle, the second is where the image should land, and the two examples in the help output show the difference between writing to the current directory and writing into a build folder. Nothing else exists except five options. This is the help text as the tool prints it:
Usage
$ create-dmg <app> [destination]
Options
--overwrite Overwrite existing DMG with the same name
--no-version-in-filename Exclude version number from DMG filename
--identity=<value> Manually set code signing identity (automatic by default)
--dmg-title=<value> Manually set DMG title (must be <=27 characters) [default: App name]
--no-code-sign Skip code signing the DMG
Examples
$ create-dmg 'Lungo.app'
$ create-dmg 'Lungo.app' Build/ReleasesRead that list as the project's stated position on configuration. `--overwrite` replaces an existing DMG with the same name. `--no-version-in-filename` takes the version out of the output filename. `--identity=<value>` sets a signing identity by hand, and the help notes that it is automatic by default. `--dmg-title=<value>` sets the title. `--no-code-sign` turns signing off. There is no config file, no argument for a background image, and no way to add a second icon. Anything not on that list is not reachable from the shell.
The install is one npm line and the tarball ships six paths
Install requires Node.js 20 or later, then a global install:
npm install --global create-dmgpackage.json declares `engines.node` as `>=20`, maps the bin name `create-dmg` to `./cli.js`, and marks the package as an ES module. The `files` array decides what actually reaches you: `cli.js`, `compose-icon.js`, `sla.js`, `assets`, `disk-icon.icns`, and `compose-icon`. That is the whole installed footprint, and it means the entry point you run is a file you can open and read. Runtime dependencies are `appdmg`, `execa`, `icns-lib`, `meow`, `ora`, `plist`, and `tempy`. Your first real run is one command with the bundle path, and adding the second argument redirects the output:
$ create-dmg 'Lungo.app'
$ create-dmg 'Lungo.app' Build/ReleasesThe second form is the one to copy into a release script, since the image lands in the same place every time instead of wherever the shell happens to be.
The title stops at 27 characters and the filename keeps the version
The DMG title defaults to the app name, and the help text puts a hard ceiling on it: `must be <=27 characters`. A long product name is therefore a real editing decision rather than a cosmetic one, because nothing in the interface fits, shrinks or truncates the title for you. The filename is a separate concern and follows the pattern `App Name 0.0.0.dmg`, so `Lungo 1.0.0.dmg` is what you get for a bundle called Lungo at version 1.0.0. `--no-version-in-filename` removes the version, which is the right choice when your release tag already carries it and two builds of the same version would otherwise collide. That collision is what `--overwrite` exists to resolve. Note the asymmetry between build machine and reader: the image needs macOS 10.13 or later to open, so the floor constrains whoever downloads your release, not the runner that produced it.
A failed signature still leaves a DMG on disk
Code signing is attempted, not required. The documented behaviour is that the image is still created and fine when signing fails, and the case named is a machine with no developer certificate. That is convenient on a laptop and awkward in CI: the process can report a nonzero exit code while a perfectly usable, unsigned file sits in the build folder, so a pipeline that only inspects the exit status can stop with an artifact already written. `--no-code-sign` exists for exactly that situation, and the README ties it to preventing exit code failures in CI environments. `--identity=<value>` is the opposite lever, forcing a named identity instead of letting automatic detection choose, which matters on a build machine holding several certificates. What the tool does not do is notarize. The README gives that away in one line: `Don't forget to notarize your DMG.` Notarization, and the stapling that follows it, stays outside this command.
A license file in the working directory changes what mounting does
Put a file named `license.txt` or `license.rtf` into the directory you run the command from and it is picked up as a software license agreement shown when the image is opened. The behaviour it triggers is the part worth planning around: the image will not mount until the user indicates agreement. A download becomes a two step interaction, so a user who dismisses the dialog never reaches the app inside. The lookup is against the current working directory, not against the destination argument, so a release script that changes directory first can lose the agreement without any error. The repository ships `sla.js` and lists it among the files the package installs, and the project root carries a `license` entry. No option in the usage output names any of this.
A Swift binary rides along inside the npm package
The project is mostly JavaScript, but the published package also carries a compiled native helper. The `build` script runs `xcrun swift build -c release --arch arm64 --arch x86_64 --product compose-icon` inside the `ComposeIcon` directory and copies the result to `../compose-icon`, and `prepublishOnly` runs that build before publish. The resulting binary is listed in `files`, so it travels inside the tarball you install from npm, already built for Apple Silicon and Intel. That is the upgrade cost to weigh: a change to icon handling touches Swift as well as JavaScript, and the build step needs the macOS toolchain rather than only Node.js. The test setup reflects a similar constraint, with ava configured as `serial: true` and a `30s` timeout, so the suite runs one test at a time under a hard ceiling. The repository keeps `__image_snapshots__`, a `fixtures` directory, and a `jest-image-snapshot` dev dependency, which is how visual output gets compared.
No password, no Windows, no notarization step
The five options are the entire surface, and it ends well short of what a disk image can be. Nothing in the usage output takes a password or requests an encrypted image, so a reader looking for a protected DMG will not find one here. Nothing points to Windows either: the publish path shells out to `xcrun swift build`, and the result is a macOS disk image. There is no option for a custom background, a second icon, a chosen symlink path, or a different volume size. The consequence for a reader is that the layout is fixed and your real decision is whether that fixed layout is the one you want, not how much you want to tune it. The author states the trade plainly: `This tool is intentionally opinionated and simple. I'm not interested in adding lots of options.` The project version in package.json is 8.1.0, and the release tags listed for the repository are v8.1.0 on 2026-03-21, v8.0.0 on 2026-01-23, and v7.1.0 on 2025-09-09, with the last push to the default branch dated 2026-03-21.
Editorial conclusion
create-dmg fits a team that ships one .app outside the Mac App Store and already knows how to sign its build. Skip it if you need a password protected image, a Windows build step, or a layout the five options do not describe. Before adopting, run it once on your own bundle and check three things: the filename it derives from your app name, how your product name sits against the 27 character title limit, and whether the signing identity resolves on the machine that publishes.
Frequently asked questions
How do I create a DMG on Mac with create-dmg?
Install Node.js 20 or later, run npm install --global create-dmg, then pass the path to your app bundle: create-dmg 'Lungo.app'. Add a second argument to send the image somewhere else, as in create-dmg 'Lungo.app' Build/Releases.
Can create-dmg turn a macOS DMG into a bootable USB?
No part of it does that. The command accepts an app bundle and an optional destination directory and writes a .dmg named after the app and its version, and none of --overwrite, --no-version-in-filename, --identity, --dmg-title or --no-code-sign takes a USB device or an image format.
What is a DMG on a Mac, and what does create-dmg put inside one?
It links to an Apple Disk Image and is described as the most common way of distributing an app outside the Mac App Store. The file create-dmg produces carries the app name and version, for example Lungo 1.0.0.dmg, and requires macOS 10.13 or later to open.
Which macOS tool creates .DMG files, and how is create-dmg installed?
create-dmg is an npm package installed globally with npm install --global create-dmg, requiring Node.js 20 or later. It exposes one command, create-dmg <app> [destination], is licensed under MIT, and needs no GUI application to design a layout.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sindresorhus-create-dmg)Star history chart for your README
[](https://hysenlabs.com/projects/sindresorhus-create-dmg)Was this useful?