Skip to content
macOSDeep Dive Published Updated 9 min readViews unavailable

macOS Disk Image Distribution: Build, Sign, Notarize, and Test DMGs

Create macOS disk-image releases with a deliberate filesystem layout, Developer ID signing, notarization, stapling, and clean-machine tests.

A disk image is a distribution container, not an installer. A user opens a DMG in Finder and accesses the app or files it contains. For a single app bundle, the format can provide a familiar drag-to-Applications experience. It does not copy the app automatically, install privileged components, or make an unsigned payload trustworthy.

Apple recommends choosing a container based on what the product needs. A disk image can be signed, which protects included files from modification after signing. An installer package is a better fit when a product needs to place multiple components, install a launch item, or run installer actions. This article concentrates on the format-specific engineering of a DMG: staging contents, building a stable image, signing and notarizing the outer container, and validating the exact file users download.

Separate product contents from image presentation

Build the app bundle first, with its code signature and embedded resources complete. Create a clean staging directory containing only the user-facing items that belong in the image. Do not build the release DMG from a developer’s Desktop or a workspace directory that may contain debug builds, credentials, source code, temporary logs, or stale artifacts.

Use deterministic names and a versioned output path. If the image includes a README or license, generate it from reviewed release content and confirm that it matches the shipped version. A Finder alias to Applications is a user-interface convenience, not part of the app’s trust chain. Avoid extra executables or shell scripts in the image unless they are a deliberate, signed, reviewed component of the product.

For a compressed read-only UDIF image, hdiutil can create an image from the staging folder. The example below uses environment variables to make the release inputs explicit. Run it in a clean build job after verifying that the application bundle and staging directory contain exactly the approved release files.

set -eu

test -n "$APP_NAME"
test -n "$STAGING_DIR"
test -n "$DMG_PATH"
test -d "$STAGING_DIR"
test -d "$STAGING_DIR/$APP_NAME.app"
mkdir -p "$(dirname "$DMG_PATH")"

hdiutil create \
  -volname "$APP_NAME" \
  -srcfolder "$STAGING_DIR" \
  -ov \
  -format UDZO \
  "$DMG_PATH"

The source-folder option places the staging tree in the image. The volume name is what Finder shows after mounting. UDZO creates a compressed, read-only image. Validate the resulting image with hdiutil’s verification command and inspect its mounted contents in a clean test account; do not assume a zero exit status from image creation proves that the contents or Finder presentation are correct.

Sign the code before signing the outer image

The app bundle must already have the appropriate Developer ID signature and hardened-runtime settings for direct distribution. Verify nested code before creating the disk image. Apple describes nested distribution as a lowest-level-first process: sign code, create and sign inner containers, then create and sign the outer disk image when used.

Signing the DMG protects the files and folders inside it from modification after the image is signed. It does not repair an invalid app signature, grant permissions, or substitute for notarization. Build and sign in a controlled release environment, use a dedicated Developer ID identity, and keep signing credentials out of the source repository. Restrict access to notarization credentials and use a CI keychain or supported credential profile with limited scope.

After creating the image, sign that exact DMG using the distribution identity and a secure timestamp according to Apple’s current distribution guidance. Verify the signature with codesign and retain the verification output in the release record. Do not modify the image after signing. Any change to the volume contents or image file means the previous signature and notarization result no longer describes the distributed artifact.

Notarize the artifact users will receive

For direct distribution outside the Mac App Store, submit the signed distribution container to Apple’s notary service. Apple supports notarization for disk images and explains that a ticket can be stapled to the artifact so Gatekeeper can validate it when the device is offline. Use notarytool rather than the retired altool upload workflow; Apple states that its service no longer accepts uploads from altool or Xcode 13 and earlier beginning November 1, 2023.

A typical command-line sequence submits the built image, waits for a terminal result, staples the returned ticket, and validates the staple:

xcrun notarytool submit "$DMG_PATH" \
  --keychain-profile "$NOTARY_PROFILE" \
  --wait

xcrun stapler staple "$DMG_PATH"
xcrun stapler validate -v "$DMG_PATH"
codesign --verify --verbose=2 "$DMG_PATH"
hdiutil verify "$DMG_PATH"

The keychain profile name is configuration supplied by the release environment; it is not a password to place in the script. Create and access that profile through the supported notarytool credential setup. A successful submission requires a notarization result that is accepted, not just an upload receipt. If the result is rejected, inspect the notary log, repair the underlying signing or packaging problem, rebuild the image, and submit the new artifact.

The ticket belongs to the exact distribution object. If the application is packaged inside the DMG, validate the nested app signature and the outer image signature independently. A stapled ticket on the app does not prove that the disk image is notarized, and an accepted outer image does not excuse a broken nested signature. Keep the app, image, submission ID, accepted result, staple validation, and cryptographic checksums linked in the release record.

Inspect the mounted filesystem, not just the archive metadata

Mount the image in a clean test environment and check that the volume opens, the expected app name and version are present, the app can be copied to Applications, and no build-only files are exposed. Confirm that the app launches from the copied destination and that the first-launch Gatekeeper experience matches the intended distribution channel. Test a newly downloaded copy so quarantine behavior is represented; a local build directory may not have the same attributes.

Avoid testing only from the mounted image. Apple describes a disk image as a container from which users can run the app or move it to another location. If the product expects copying, test the copied bundle. If the product supports running from the mounted image, verify that the app does not depend on adjacent files that disappear when the DMG is ejected.

Check case sensitivity and path assumptions. A developer’s APFS workspace may behave differently from the filesystem created for the image. Keep filenames stable, avoid relying on undeclared hidden files, and test any symlinks deliberately. If the app updates itself, verify that the updater does not attempt to mutate a read-only mounted copy.

Make the build reproducible and safe

Use a fresh staging directory for each release. Record the app bundle hash, DMG hash, signing identity, build number, notary submission ID, and notarization result. Keep secrets in the CI credential store and fail the build if a signing, staple, or verification step fails. Do not let a script continue after a failed notarization upload and publish the unaccepted image under a production URL.

Publish atomically. Upload to a versioned temporary location first, fetch it back, verify its checksum, test the fetched file, then update the release pointer. A CDN or object store can serve a stale or truncated file even when the local artifact passed verification. Include the DMG’s size and checksum in release metadata, but never publish private signing logs or credential material.

If you create a decorative Finder layout, treat it as release content with visual QA. A background image, alias, or window arrangement should not obscure the app, imply a false security guarantee, or block keyboard navigation. Test at more than one display scale and with larger text or accessibility settings where appropriate. Keep the functional install path obvious even if Finder does not retain a custom window layout.

Diagnose common release failures

If the DMG fails signature verification, first determine whether it changed after signing and whether the expected identity signed the container. If the app inside fails, validate the app bundle and nested code separately. If notarization rejects the upload, inspect the notary log for the affected executable or validation rule rather than repeatedly resubmitting an unchanged image.

If stapling fails, confirm that the notary submission completed with an accepted result and that the path is the exact artifact submitted. If Gatekeeper cannot find a ticket while offline, verify the staple on the image and the nested app. If the download is corrupted, compare the served checksum with the release record before rebuilding the app.

A successful hdiutil verify checks the image’s internal integrity, not the app’s signature or security assessment. A successful codesign verify checks signature integrity, not whether the artifact passed notarization. A successful stapler validate checks the staple, not whether the UI is usable. Keep these checks as distinct release gates.

When a DMG is the wrong container

Use an installer package when installation must place multiple components in fixed locations, migrate supported state, or execute a narrowly scoped installer action. Use an archive when the user needs the files as a portable directory tree and the signing and distribution contract is appropriate. A DMG adds user-interface value for a simple bundle, but it adds a container-signing and notarization step that must be maintained.

Do not wrap every artifact in a DMG just because it looks familiar. A package inside a disk image creates nested distribution objects and requires the release pipeline to sign and validate each supported layer in the correct order. Pick the least complex format that satisfies the actual product workflow.

Release checklist

Build a clean staging folder, inspect its contents, create and verify the image, sign the app and outer container, notarize the exact artifact users download, staple and validate the ticket, check cryptographic integrity, and test the downloaded file on a clean Mac. Record all release evidence and ensure publication fails closed when any gate fails.

A well-engineered DMG is a small part of the release surface, but it is the file users trust first. Treat the exact disk image as a versioned, signed software deliverable. A polished Finder window is useful only after the contents, signatures, notarization, and download path have all been verified.

Related:

Sources:

Comments