Build and publish an iOS app on Windows, no Mac (2026)
What you need
- An Apple Developer Program membership ($99 a year). Apple only signs apps for members, and only members can publish.
- Your app in a GitHub repo: a Capacitor app (including Lovable, Bolt, v0 and Replit web apps), a Flutter app, or a React Native app. Expo works after
npx expo prebuildwith theios/folder committed. - OpenSSL on Windows, to make the certificate. Git for Windows includes it: run the commands below in Git Bash.
- A Mac in the cloud for the build itself. GitHub Actions has macOS runners, and that is where Corenna runs iOS builds, in your own GitHub account.
Step 1: Create the signing certificate on Windows
On a Mac this is done in Keychain Access. On Windows, OpenSSL does the same two jobs: it makes the private key and the certificate signing request (CSR) Apple asks for, and afterwards it packs Apple's certificate and your key into the .p12 file builds are signed with.
openssl genrsa -out ios_distribution.key 2048
openssl req -new -key ios_distribution.key -out ios_distribution.csr \
-subj "/emailAddress=you@example.com/CN=Your Name/C=US"In the Apple Developer site, open Certificates, Identifiers & Profiles, add a certificate of type Apple Distribution, upload ios_distribution.csr and download the .cer Apple gives back. Then:
openssl x509 -inform DER -in distribution.cer -out distribution.pem
openssl pkcs12 -export -legacy -inkey ios_distribution.key \
-in distribution.pem -out ios_distribution.p12OpenSSL asks for an export password: that is the .p12 password you will give the build. -legacy matters on OpenSSL 3: without it the file is encrypted in a newer format that the Mac keychain on the build machine can refuse with "MAC verification failed". On OpenSSL 1.1 (check with openssl version) leave it out; that version already uses the older format.
Keep ios_distribution.key private and backed up. It is the other half of the certificate; without it, the certificate cannot sign anything.
Step 2: Register the app ID and make a provisioning profile
Still in Certificates, Identifiers & Profiles, all in the browser:
- Under Identifiers, register an App ID with your app's bundle ID, for example
com.yourname.app. It must match the ID in your project exactly. - Under Profiles, create an App Store distribution profile for that App ID and the certificate from step 1, and download the
.mobileprovisionfile. - In App Store Connect, create the app record with the same bundle ID. An upload is refused until the app exists there.
The profile expires after a year; the distribution certificate lasts longer. When either runs out, make a new one the same way. For the full picture, see iOS code signing explained.
Step 3: Build and sign on a Mac you don't own
Connect the repo to Corenna and upload the .p12 (with its password) and the .mobileprovision once, on the project's Signing tab. Before a build starts, Corenna checks that the profile can be read, matches your bundle ID and team, and has not expired, so those mistakes stop you up front instead of twenty minutes into a build.
Start the iOS build from Windows, or from your phone. Corenna writes a GitHub Actions workflow into your repo, runs it on a macOS runner in your GitHub account, installs the certificate and profile there, and signs the .ipa. You watch the logs live and download the signed .ipa when it finishes. A sandbox build, which is free, runs the same signing without keeping the file, so you can prove your setup first.
Step 4: Upload the .ipa to App Store Connect from Windows
This is the step people expect to need a Mac for. Apple's Transporter app is Mac-only, and Corenna does not upload to App Store Connect for you. From Windows you have two of Apple's own routes:
- Transporter's command-line tool (iTMSTransporter), which Apple ships with installers for Windows and Linux as well as macOS. Install it from Apple's Transporter User Guide, create an App Store Connect API key, and upload the
.ipawith it. - The App Store Connect API, which Apple documents as a way to upload a build. It suits you if you script your releases.
Once the build has processed, everything else happens in App Store Connect in the browser: send it to TestFlight testers, fill in the listing, and submit it for review.
What it costs
- Apple Developer Program: $99 a year. Unavoidable on any route.
- GitHub Actions: free for public repos on GitHub's standard runners. A private repo uses your plan's included minutes first (2,000 a month on GitHub Free), then macOS minutes cost $0.062 each (GitHub's 2026 price).
- Corenna: iOS builds need the plan, $2.99/month. Sandbox builds are free.
- A Mac: nothing. You never buy or rent one by the hour.
When it goes wrong
- "MAC verification failed" importing the .p12: it was made with OpenSSL 3 without
-legacy. Run the last command again with it. - "No profile matches" or a signing identity not found: the profile was made for a different certificate. Edit the profile, tick the certificate from step 1, download it again.
- Bundle ID mismatch: the ID in the project, the App ID and the App Store Connect record must be the same string.
- Wrong password: it is the export password you typed for the
.p12, not your Apple ID password.