Prerequisites
Before starting the release process, ensure you have completed all the steps in Build Instructions.Release Workflow
1. Update CHANGELOG
EnsureCHANGELOG.md is up to date with all changes for this release:
- Review all changes since the last release
- Change the
[Unreleased]header to[<VERSION>] - <DATE>format - Add a new
[Unreleased]header at the top - Push changes, get them reviewed, and merge
The CHANGELOG follows Keep a Changelog format. Use imperative form for entries (e.g., “add”, “fix”, “increase”).
2. Prepare Release
Run theprepare-release.sh script to prepare the release:
- Checks repository state and version format
- Updates
package.jsonwith the new version - Creates a signed git tag with the release version
- Commits the version changes
3. Configure Code Signing
Code signing is required for production releases.Windows and macOS
Set these environment variables:CSC_LINK - Path to the signing certificate:
- Windows:
.pfxcertificate file - macOS:
.p12certificate file containing both:- Developer ID Application certificate
- Developer ID Installer certificate
CSC_KEY_PASSWORD - Certificate password
macOS Only: Notarization
For macOS releases, configure notarization:NOTARIZE_KEYCHAIN - Keychain where the notarytool profile is stored
NOTARIZE_KEYCHAIN_PROFILE - Name of the notarytool profile
Setup notarytool profile:
- Generate an app-specific password on Apple’s AppleID management portal
- Run:
- Leave the first prompt empty, then fill in:
- Apple ID
- App-specific password (not your real Apple ID password)
- Team ID
- Set the environment variables to match step 2
4. Build Release
Runbuild.sh on each platform where you want to create a release artifact:
- Updates
relays.jsonwith the latest relay list - Compiles and packages the app into a distributable artifact
- Signs the binaries (if configured)
dist/ directory.
Pay attention to the output at the end of the script and verify the version matches what you want to release.
5. Verify Build
Before distribution:- Check the version number in the built artifact
- Test the installer on a clean system
- Verify signatures on signed builds
- Test core functionality after installation
Release Types
Production Release
A production release meets these criteria:- Built with
--signflag (signatures enabled) - Built with
--optimizeflag (optimizations enabled) - Built on a release git tag (no
-dev-suffix in version)
Development Build
Any build that doesn’t meet all production release criteria is a development build:- Has
-dev-{commit hash}appended to version - Not for general distribution
- Allows runtime API server override
Build Options
Optimization
Enable compiler optimizations (slower build, smaller binaries):Signing
Sign the build artifacts (requires certificates):Notarization (macOS)
Notarize the macOS build with Apple:Universal Builds
Create installers supporting both x86_64 and ARM64:Combined Example
Platform-Specific Notes
Windows
Requirements:- Visual Studio 2022 Build Tools or Community Edition
- Windows 10/11 SDK
msbuild.exein PATHzig0.14+ in PATH
CERT_HASH environment variable with certificate thumbprint.
macOS
Requirements:- Recent bash version (not the default 3.2.57)
- Xcode command line tools
clangfor CGo
--universal for both Intel and Apple Silicon.
Linux
Cross-compilation for ARM64:Clean Working Directory
The script enforces this to prevent:- Release builds from potentially modified code
- Accidentally distributing untested changes
- Supply chain security issues
Version Numbering
Mullvad VPN uses this versioning scheme:- Stable releases:
YYYY.N(e.g.,2026.1) - Beta releases:
YYYY.N-betaN(e.g.,2026.1-beta1) - Dev builds:
YYYY.N-dev-HASH(e.g.,2026.1-dev-abc123)
YYYYis the yearNis the release number for that yearHASHis the git commit hash
Post-Release
After building:- Upload artifacts to distribution channels
- Create GitHub release with changelog
- Update download pages on mullvad.net
- Announce the release via blog and social media
- Monitor for issues in the first 24-48 hours
Troubleshooting
”Dirty working directory” Error
The build script refuses to sign on dirty working directories. Solution:- Commit all changes before building with
--sign - Or remove
--signfor development builds
Certificate/Signing Errors
Windows:- Verify
CERT_HASHmatches the certificate thumbprint - Check certificate is installed in the certificate store
- Verify
CSC_LINKpoints to valid.p12file - Check
CSC_KEY_PASSWORDis correct - Ensure certificate includes both Application and Installer certs
Notarization Fails
- Verify notarytool profile is configured correctly
- Check Apple ID credentials are valid
- Ensure app-specific password is used (not regular password)
- Verify Team ID is correct
Next Steps
Build Instructions
Set up your development environment
Troubleshooting
Fix common development issues
Known Issues
Platform-specific limitations
Contributing
How to contribute to the project