Introduction to macOS Notarization
macOS Notarization is a security process introduced by Apple that verifies applications distributed outside the Mac App Store. When you notarize your app, Apple's automated system scans the software for malicious content and confirms that it is properly signed and free from known security issues. Once approved, Apple issues a notarization ticket that the operating system can verify at runtime, allowing Gatekeeper to let users run your app without scary warning dialogs.
Since macOS Catalina (10.15), notarization has been a mandatory requirement for all software distributed outside the Mac App Store. Without it, users encounter blocking dialogs that prevent them from launching your application by default, which can severely impact adoption and trust.
Why Notarization Matters
Notarization serves several critical purposes in the macOS ecosystem:
- User Trust: A notarized app displays a clean, verified badge in Gatekeeper dialogs, reassuring users that the software has been scanned by Apple.
- Security: Apple's automated scanning detects malware, insecure entitlements, and embedded malicious code before it reaches end users.
- Gatekeeper Compliance: Without notarization, Gatekeeper blocks the app by default, requiring users to right-click and bypass warnings manually.
- Developer Reputation: Notarization ties your app to your Developer ID, creating accountability and traceability.
- Future-Proofing: Apple continues to tighten notarization requirements with each macOS release, so adopting the process early avoids last-minute scrambling.
Prerequisites
Before you can notarize an application, you need the following:
- An Apple Developer account (paid membership).
- A Developer ID Application certificate installed in your Keychain.
- Xcode command-line tools installed.
- An app bundle, DMG, or ZIP archive ready for distribution.
- An App-Specific Password for your Apple ID (required for the notarization API).
Creating an App-Specific Password
Because notarization uses your Apple ID credentials, Apple requires an app-specific password instead of your account password. Generate one at appleid.apple.com under the Security section, then store it securely. You will reference it when submitting your app for notarization.
Step 1: Code Signing Your Application
Notarization requires that your app be signed with a valid Developer ID Application certificate. The signature must be "deep," meaning all embedded frameworks, helpers, and binaries must also be signed. Here is a basic example of signing an app bundle:
# Sign embedded frameworks first
codesign --force --deep --options runtime \
--sign "Developer ID Application: Your Name (TEAMID)" \
--entitlements entitlements.plist \
/path/to/YourApp.app/Contents/Frameworks/SomeFramework.framework
# Sign the main app bundle
codesign --force --deep --options runtime \
--sign "Developer ID Application: Your Name (TEAMID)" \
--entitlements entitlements.plist \
/path/to/YourApp.app
The --options runtime flag enables the Hardened Runtime, which is mandatory for notarization. It restricts dangerous capabilities like unsigned memory execution and dynamic code injection unless explicitly allowed via entitlements.
Verifying the Signature
After signing, verify that the signature is valid and that all components are properly sealed:
codesign --verify --deep --strict --verbose=2 /path/to/YourApp.app
You should see output confirming that the app satisfies the designated requirement and that no unsigned components exist. If any embedded binary lacks a signature, notarization will fail.
Step 2: Creating a Distributable Archive
Notarization accepts several archive formats, but the most common are ZIP and DMG. For apps with many files, a DMG is often preferred because it preserves the directory structure and is the final artifact users download. Use ditto to create a ZIP that preserves macOS metadata:
ditto -c -k --keepParent /path/to/YourApp.app /path/to/YourApp.zip
For a DMG, use hdiutil:
hdiutil create -volname "YourApp" -srcfolder /path/to/build_output \
-ov -format UDZO YourApp.dmg
Regardless of format, the archive must contain only signed binaries. Any unsigned helper tool or framework inside the archive will cause notarization to fail.
Step 3: Submitting for Notarization
Submit your archive to Apple using the notarytool command, which replaced the older altool workflow. notarytool is faster, more reliable, and provides better error reporting. You will need your Apple ID, the team ID, and the app-specific password created earlier:
xcrun notarytool submit /path/to/YourApp.zip \
--apple-id "you@example.com" \
--team-id "TEAMID12345" \
--password "xxxx-xxxx-xxxx-xxxx" \
--wait
The --wait flag tells the tool to block until Apple finishes processing the submission. Processing typically takes a few minutes, though complex apps can take longer. The output includes a submission ID that you can use to check status later if needed.
Storing Credentials Securely
Typing credentials on the command line exposes them in shell history. notarytool supports storing credentials in the macOS Keychain so you never have to type them again:
xcrun notarytool store-credentials "AC_PASSWORD" \
--apple-id "you@example.com" \
--team-id "TEAMID12345" \
--password "xxxx-xxxx-xxxx-xxxx"
After storing, subsequent submissions reference the keychain profile by name:
xcrun notarytool submit /path/to/YourApp.zip \
--keychain-profile "AC_PASSWORD" \
--wait
Step 4: Checking Notarization Status
If you did not use --wait, or if you want to inspect a previous submission, query the status using the submission ID:
xcrun notarytool info <submission-id> \
--keychain-profile "AC_PASSWORD"
To retrieve the full JSON log, which contains detailed reasons for any failures, use:
xcrun notarytool log <submission-id> \
--keychain-profile "AC_PASSWORD" \
notarization_log.json
The log lists every file scanned and any issues found. Common failures include unsigned binaries, incorrect entitlements, or the use of deprecated APIs. Address each issue, re-sign, and resubmit.
Step 5: Stapling the Notarization Ticket
Once Apple approves your submission, the notarization ticket is hosted on Apple's servers. However, if a user downloads your app without internet access, Gatekeeper cannot fetch the ticket. To solve this, staple the ticket directly to your app or DMG:
xcrun stapler staple /path/to/YourApp.app
xcrun stapler staple /path/to/YourApp.dmg
Verify that stapling succeeded:
xcrun stapler validate /path/to/YourApp.app
For DMG files, also verify the notarization status of the enclosed app:
spctl --assess --type open --context context:primary-signature -v /path/to/YourApp.dmg
If validation passes, your app is ready for distribution.
Automating the Notarization Workflow
Manual notarization is error-prone, especially when shipping frequent updates. Most teams automate the entire pipeline in a shell script or CI/CD system. Below is a complete example script that signs, archives, submits, staples, and validates an app:
#!/bin/bash
set -euo pipefail
APP_PATH="build/YourApp.app"
DMG_PATH="build/YourApp.dmg"
SIGN_ID="Developer ID Application: Your Name (TEAMID)"
KEYCHAIN_PROFILE="AC_PASSWORD"
echo "Signing app..."
codesign --force --deep --options runtime \
--sign "$SIGN_ID" \
--entitlements entitlements.plist \
"$APP_PATH"
echo "Verifying signature..."
codesign --verify --deep --strict --verbose=2 "$APP_PATH"
echo "Creating DMG..."
hdiutil create -volname "YourApp" \
-srcfolder "build/" \
-ov -format UDZO "$DMG_PATH"
echo "Submitting for notarization..."
xcrun notarytool submit "$DMG_PATH" \
--keychain-profile "$KEYCHAIN_PROFILE" \
--wait
echo "Stapling ticket..."
xcrun stapler staple "$DMG_PATH"
echo "Validating..."
xcrun stapler validate "$DMG_PATH"
echo "Done. $DMG_PATH is ready for distribution."
Integrate this script into your CI pipeline, triggering it on tagged releases. Store the keychain profile as a secret in your CI system, and ensure the build runner has the Developer ID certificate installed.
Common Notarization Failures and Fixes
Even experienced developers encounter notarization rejections. Understanding the most frequent issues saves debugging time:
- Unsigned embedded binaries: Every executable, framework, and helper inside the bundle must be signed. Use
codesign --verify --deepto catch these before submission. - Missing Hardened Runtime: Forgetting
--options runtimeis a common mistake. Without it, notarization will reject the build. - Invalid entitlements: Certain entitlements, like
com.apple.security.cs.disable-library-validation, are allowed but flagged. Review each entitlement to ensure it is necessary. - Bundle structure issues: Mach-O binaries placed outside the expected bundle locations can trigger failures. Follow standard bundle layouts.
- Expired certificates: A Developer ID certificate that has expired will cause signing to fail. Renew it annually through the Apple Developer portal.
Best Practices
To keep your notarization workflow smooth and reliable, follow these best practices:
- Notarize the final artifact: If you distribute a DMG, notarize and staple the DMG itself, not just the app inside it. This ensures the ticket covers the entire download.
- Minimize entitlements: Only request the entitlements your app truly needs. Excessive entitlements increase scrutiny and may trigger warnings.
- Automate everything: Manual steps introduce human error. Script the full pipeline and run it in CI for every release.
- Keep certificates current: Monitor certificate expiration dates and renew well in advance to avoid release-day surprises.
- Test on a clean machine: After stapling, download the artifact on a fresh macOS installation and confirm Gatekeeper allows it to open without warnings.
- Read the notarization log: Even successful submissions can include warnings. Review the JSON log periodically to catch issues before they become failures.
- Version your builds: Include version numbers in archive names so you can correlate notarization submissions with specific releases.
Conclusion
macOS Notarization is an essential step in distributing software outside the Mac App Store, providing users with confidence that your app has been scanned and signed by a verified developer. By understanding the full pipeline—from code signing with the Hardened Runtime, through submission with notarytool, to stapling the final ticket—you can ship apps that open cleanly on every Mac. Automating the workflow and following best practices will keep your releases reliable, secure, and ready for whatever new requirements Apple introduces in future macOS versions.