Code Signing Overview
Code Signing Modes
Section titled “Code Signing Modes”RunnerHub supports two code signing modes for iOS and macOS apps:
- Automatic (recommended) — RunnerHub manages certificates and provisioning profiles using your Apple API key
- Manual — You upload your own P12 certificate and provisioning profiles
Both modes handle certificate installation, keychain management, and cleanup automatically. Choose the mode that works best for your workflow.
Automatic Mode - How It Works
Section titled “Automatic Mode - How It Works”RunnerHub uses a two-level configuration model:
- Workspace Level: Apple App Store Connect API key + certificate setup
- App Level: Bundle ID and signing type configuration
When your job runs, RunnerHub automatically:
- Generates a certificate signing request (CSR)
- Creates or reuses Apple Distribution certificates
- Fetches provisioning profiles from Apple
- Installs everything in an isolated, ephemeral keychain
- Injects signing environment variables into your build
- Cleans up all signing artifacts after the job completes
Manual Mode - How It Works
Section titled “Manual Mode - How It Works”In manual mode, you upload your own P12 certificate(s) and provisioning profiles to RunnerHub:
- App Level: Upload one or more P12 certificates and provisioning profiles
- During Build: RunnerHub installs all your uploaded certificates and profiles into an ephemeral keychain
- Automatic Selection: xcodebuild automatically picks the correct certificate based on the provisioning profile
- Environment: Signing environment variables are injected (same as automatic mode)
- Cleanup: All artifacts are cleaned up after the job
Manual mode supports multiple certificates per app — for example, you can upload both a development certificate and a distribution certificate. All certificates are installed into the build keychain, and xcodebuild automatically selects the correct one based on your provisioning profile.
Manual mode is useful if you want to:
- Use existing certificates you already manage
- Upload multiple certificates for different signing purposes (development, distribution, etc.)
- Have specific signing requirements your workflow depends on
- Avoid sharing Apple API credentials with RunnerHub
Ephemeral Keychain
Section titled “Ephemeral Keychain”All signing operations happen in an ephemeral keychain—a temporary keychain that exists only for the duration of your job. This keychain:
- Is created fresh for each job
- Never touches your Mac’s
login.keychain-db - Is encrypted and secured with a random password
- Is automatically destroyed after the job finishes
This approach ensures:
- Complete isolation between jobs
- No persistent signing credentials on the build machine
- Safe multi-tenant operation
- Guaranteed cleanup
Encryption at Rest
Section titled “Encryption at Rest”All sensitive credentials are encrypted at rest in the RunnerHub database:
- Apple API keys
- Certificate private keys
- Provisioning profiles
Keys are decrypted only in memory when executing a job and never exposed in logs or artifacts.
No Fastlane Match Required
Section titled “No Fastlane Match Required”You don’t need Fastlane Match or any other credential management tool. RunnerHub manages everything through the Apple API. If you already use Fastlane, it works seamlessly with RunnerHub’s signing setup.
Plan Availability
Section titled “Plan Availability”RunnerHub supports code signing on all plans:
| Plan | Manual Signing (iOS/macOS) | Automatic Signing (iOS/macOS) | Android Signing |
|---|---|---|---|
| Free | Available | Not available | Available |
| PAYG | Available | Available | Available |
| Pro | Available | Available | Available |
| Business | Available | Available | Available |
Manual Signing (Free+) — Upload your own P12 certificates and provisioning profiles. Requires no integration with Apple’s systems.
Automatic Signing (PAYG+) — Let RunnerHub manage certificates and profiles using your Apple API key. Automated certificate and profile creation, renewal, and lifecycle management.
Android Signing (Free+) — Upload your keystore directly on the app for APK/AAB signing. Available on all plans with no additional setup required.
Automatic Mode Setup
Section titled “Automatic Mode Setup”RunnerHub supports multiple Apple credentials per workspace, allowing each app to select which credential to use.
Step 1: Add Apple Credentials (Workspace Level)
Section titled “Step 1: Add Apple Credentials (Workspace Level)”Go to your workspace settings and add one or more Apple App Store Connect API keys. Each credential:
- Gives RunnerHub permission to create certificates and fetch provisioning profiles
- Has a display name (e.g., “Team A”, “Personal Account”) for easy identification
- Can be used by any app in your workspace
See API Key Setup for detailed instructions.
Step 2: Configure Per-App Signing (App Level)
Section titled “Step 2: Configure Per-App Signing (App Level)”For each app in your workspace, specify:
- Bundle ID: Your app’s unique identifier (e.g.,
com.example.myapp) - Signing Type: How your app should be signed (
development,adhoc,appstore, ordeveloper-idfor macOS outside-App-Store distribution) - Signing Mode: Select
Automaticto use workspace credentials - Apple Credential: Select which credential from your workspace to use (required for automatic mode)
See Per-App Signing Configuration for details.
Step 3: Run Your Build
Section titled “Step 3: Run Your Build”When a job runs with automatic signing enabled:
- RunnerHub fetches the selected API key and app configuration
- Sets up the ephemeral keychain with your certificate
- Injects signing environment variables (e.g.,
DEVELOPMENT_TEAM,PROVISIONING_PROFILE_SPECIFIER) - Patches your Xcode project for manual signing
- Your build uses the configured signing credentials automatically
Manual Mode Setup
Section titled “Manual Mode Setup”Step 1: Export Your P12 Certificate
Section titled “Step 1: Export Your P12 Certificate”From your local machine:
- Open Keychain Access
- Find your Apple Distribution or Development certificate
- Right-click → Export
- Choose Personal Information Exchange (.p12) format
- Set a strong password
- Save the file
Step 2: Upload P12 and Provisioning Profiles
Section titled “Step 2: Upload P12 and Provisioning Profiles”In the RunnerHub dashboard:
- Go to App Settings → Code Signing
- Select Manual as the signing mode
- Click Upload Certificate
- Select your .p12 file and enter the password
- Click Add Provisioning Profile and upload each .mobileprovision file
- Verify your bundle ID matches the profiles
Step 3: Run Your Build
Section titled “Step 3: Run Your Build”When a job runs with manual signing enabled:
- RunnerHub sets up the ephemeral keychain with your uploaded certificate
- Installs your provisioning profiles
- Injects the same signing environment variables as automatic mode
- Your build uses the uploaded credentials automatically
Android Signing Setup
Section titled “Android Signing Setup”Android signing is configured directly on each app and supports APK/AAB signing with your keystore.
Prerequisites
Section titled “Prerequisites”You’ll need your Android keystore file (.jks or .keystore) and its passwords. If you don’t have one, you can:
- Use Android Studio’s Key Store Manager to generate one, or
- Use the command-line tool:
keytool -genkey -v -keystore my-keystore.jks ...
Step 1: Upload Your Keystore
Section titled “Step 1: Upload Your Keystore”In the RunnerHub dashboard:
- Go to App Settings → Code Signing
- Click Upload Android Keystore
- Select your .jks/.keystore file
- Enter the keystore password
- Enter the key alias (the name of the key within the keystore)
- Enter the key password (may be the same as the keystore password)
- Click Save
RunnerHub encrypts and stores your keystore securely. The file is never displayed in logs or artifacts.
Step 2: Use in Your Build
Section titled “Step 2: Use in Your Build”In your runnerhub.yml, reference the keystore via environment variables:
- name: Build APK run: | ./gradlew assembleRelease \ -Pandroid.injected.signing.store.file=$ANDROID_KEYSTORE_PATH \ -Pandroid.injected.signing.store.password=$ANDROID_KEYSTORE_PASSWORD \ -Pandroid.injected.signing.key.alias=$ANDROID_KEY_ALIAS \ -Pandroid.injected.signing.key.password=$ANDROID_KEY_PASSWORDOr configure it in your build.gradle:
signingConfigs { release { storeFile file(System.getenv('ANDROID_KEYSTORE_PATH')) storePassword System.getenv('ANDROID_KEYSTORE_PASSWORD') keyAlias System.getenv('ANDROID_KEY_ALIAS') keyPassword System.getenv('ANDROID_KEY_PASSWORD') }}Android Environment Variables
Section titled “Android Environment Variables”When you upload a keystore, RunnerHub automatically injects these variables:
ANDROID_KEYSTORE_PATH=/path/to/keystore/file.jksANDROID_KEYSTORE_PASSWORD=<your-keystore-password>ANDROID_KEY_ALIAS=<your-key-alias>ANDROID_KEY_PASSWORD=<your-key-password>These variables are available throughout your job and can be used by Gradle, custom scripts, or any build tool.
Supported Platforms
Section titled “Supported Platforms”- iOS — Development, ad-hoc, and App Store signing (manual and automatic modes)
- macOS — Development and App Store signing (manual and automatic modes)
- Android — Upload keystore for signing (configured per-app, available on all plans)
- Flutter — iOS/macOS signing via Automatic/Manual modes, Android signing via per-app keystore
- React Native — iOS/macOS signing via Automatic/Manual modes, Android signing via per-app keystore (configure both when building for both platforms)
Signing is tied to your app’s platform configuration. For Flutter and React Native apps, configure both Apple and Android signing in the same Code Signing tab if you’re building for both platforms.
Environment Variables Injected
Section titled “Environment Variables Injected”RunnerHub injects signing environment variables into your pipeline steps. Both automatic and manual modes inject the same set of variables.
Common Variables (Both Modes)
Section titled “Common Variables (Both Modes)”CODE_SIGN_STYLE=ManualDEVELOPMENT_TEAM=<your-team-id>PROVISIONING_PROFILE=<profile-uuid>RH_PROVISIONING_PROFILE_UUID=<profile-uuid>RH_PROVISIONING_PROFILE_NAME=<profile-name>RH_KEYCHAIN_PATH=<ephemeral-keychain-path>OTHER_CODE_SIGN_FLAGS=--keychain <ephemeral-keychain-path>RH_EXPORT_OPTIONS_PLIST=<path-to-plist>Automatic Mode Only
Section titled “Automatic Mode Only”APP_STORE_CONNECT_API_KEY_PATH=<path-to-p8-key>APP_STORE_CONNECT_API_KEY_KEY_ID=<key-id>APP_STORE_CONNECT_API_KEY_ISSUER_ID=<issuer-id>APPLE_TEAM_ID=<your-team-id>These variables are available to your pipeline steps, Xcode, Fastlane, and any scripts.
Auto-Generated ExportOptions.plist
Section titled “Auto-Generated ExportOptions.plist”When auto-sign is enabled, RunnerHub automatically generates an ExportOptions.plist file in the workspace root (if one doesn’t already exist). This plist file contains all the necessary export options for building and exporting IPAs:
- method: Maps to your signing type (
app-store-connectfor appstore,ad-hocfor adhoc,developmentfor development) - teamID: Your Apple Team ID
- signingStyle: Always
manual(RunnerHub handles signing) - provisioningProfiles: Maps bundle ID to provisioning profile UUID
The file is exposed via the RH_EXPORT_OPTIONS_PLIST environment variable, which you can use directly in your export commands:
xcodebuild -exportArchive \ -archivePath $PWD/build/MyApp.xcarchive \ -exportPath $PWD/build \ -exportOptionsPlist $RH_EXPORT_OPTIONS_PLISTWhen is the plist generated?
- Only when auto-sign is enabled for your app
- Only when both the provisioning profile name and bundle identifier are available
- Only if no existing
ExportOptions.plistexists in the repository root (your existing plist takes precedence)
This approach allows you to use your own custom plist if needed, while still providing RunnerHub-generated one for convenience when you want it.
Comparing Automatic and Manual Modes
Section titled “Comparing Automatic and Manual Modes”| Feature | Automatic | Manual |
|---|---|---|
| Certificate Management | RunnerHub creates/manages via Apple API | You upload your own P12 |
| Profile Management | RunnerHub creates/manages via Apple API | You upload .mobileprovision files |
| Apple API Required | Yes | No |
| Best For | Most users, automated certificate handling | Existing certificate workflows |
| Setup Complexity | Moderate (add API key, configure app) | Moderate (export cert, upload files) |
| Profile Expiry Handling | Automatic renewal | Manual renewal required |
For more details, see Certificates & Profiles.
Note on Android Signing: Android signing is configured per-app by uploading your keystore directly. No workspace-level setup is required. See Android Signing Setup above for details.
Security Model
Section titled “Security Model”RunnerHub’s signing implementation is designed for secure multi-tenant operation:
- Per-job isolation: Each job gets its own ephemeral keychain
- No credentials persist: All signing artifacts are deleted after the job
- Workspace isolation: One workspace cannot access another’s credentials
- Encrypted storage: All credentials are encrypted at rest
- Masked logs: Sensitive values are redacted in job logs
See Code Signing Troubleshooting if you encounter issues.