IPSec .mobileconfig files not working on either macOS or iOS
Describe the bug
The IPSec .mobileconfig files to use macOS's or iOS's built-in VPN generated by algo cause the VPN configuration to never connect properly. However, if you use the Wireguard .mobileconfig files and install the Wireguard app, the algo VPN works fine. I don't think I have anything else that could be interfering, but I'm not 100% sure (quit other VPN apps, turned off built-in Firewall, lockdown mode not turned on).
I've tested this with the current HEAD of master (, would be nice to change this to "main") or the v2.0.0 tag. I'm running Sequoia 15.7.1 on an M1 MacBook Air, iOS 18.7.1 on an iPhone 12 mini, and iPadOS 17.7.10 on an iPad 6. I am using DigitalOcean with the s-1vcpu-512mb-10gb droplet size, using the SFO3 location. I last created a droplet using Algo 1.1 in Feb 2024 with this same DigitalOcean config and everything worked properly.
Hopefully I'm not missing something obscenely obvious.
UPDATE 2026-03-04: Just wanted to make this visible to people who won't read down a huge wall of text. This bug appears to be fixed with a few simple extra commands. Please leave a note in this bug if they fix your issue too! After you completely finish creating and setting up your algo VPN, issue the following commands in the Terminal from your algo repo folder:
ssh -F configs/YOUR_SERVER_IP/ssh_config name_of_algo_vpn
sudo ipsec restartTo Reproduce
Steps to reproduce the behavior:
- Do a fresh git clone on the algo repo. You can either use the HEAD of master, or checkout the v2.0.0 tag.
- Once the repo is cloned, make changes to config.cfg as necessary. I've changed the name of the three default users, and I changed DigitalOcean to use the s-1vcpu-512mb-10gb size.
- Run
./algoin the directory of the newly cloned git repo. - Once finished, navigate to the configs/IP_ADDRESS/ipsec/apple folder, and install the appropriate .mobileconfig file on whichever device you're trying to use. (Double-click the file on macOS or AirDrop to iOS device, then go through the rigamarole in the Settings apps to actually install the profile.)
- Once the config file is actually installed on the macOS or iOS device, go to the VPN section of the Settings app, and try to turn on the VPN. You'll notice a brief "Connecting..." status message, and then the switch reverts back to off and "Disconnected". If you have "Connect on Demand" activated in the profile, you'll see this happen over and over and over: the VPN will try to connect, fail, and then try to connect again.
- Navigate to the configs/IP_ADDRESS/wireguard/apple folder, and go into the "ios" or "macos" folder as appropriate. Again, install the correct .mobileconfig file for the device you're trying to use. In my case, I went into the "ios" folder and AirDropped the .mobileconfig file to my iPhone, installed WireGuard, and clicked the switch for my VPN config in the Wireguard app. This time, the VPN connects correctly, and I can navigate to websites (and can confirm the VPN is being used because ads are being blocked).
Expected behavior
I expect the IPSec .mobileconfig files to allow the VPN to connect using the built-in Apple VPN software.
Additional context
Here's what I see in the Console on macOS when attempting to connect the VPN using the IPSec profile:
default 11:39:48.564225-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Received a start command from VPN[989]
default 11:39:48.564265-0700 nesessionmanager Registering session NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]
default 11:39:48.564490-0700 nesessionmanager <NESMServer: 0x1028c13c0>: Register Enterprise VPN Session: NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]
default 11:39:48.564517-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Successfully registered
default 11:39:48.564534-0700 nesessionmanager -[NESMVPNSession unsetDefaultDropAll]: VPN setting IP Drop-All to 0 (Non-Persistent)
default 11:39:48.565530-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: status changed to connecting
default 11:39:48.565745-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateIdle: received start message
default 11:39:48.565761-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Leaving state NESMVPNSessionStateIdle
default 11:39:48.565774-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Entering state NESMVPNSessionStatePreparingNetwork
default 11:39:48.565912-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Leaving state NESMVPNSessionStatePreparingNetwork
default 11:39:48.565927-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Entering state NESMVPNSessionStateStarting
default 11:39:48.565937-0700 nesessionmanager NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]): Sending start command
default 11:39:48.568212-0700 Network NEVPNStatusDidChange:
default 11:39:48.710014-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Plugin NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]) initialized with Mach-O UUIDs (
"60E29CA8-3844-301A-975D-5D41BAB070DA",
"9B072267-A3F4-3F8D-B15F-59AFAD04DDE5"
)
default 11:39:48.713830-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateStarting: plugin NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]) started with PID 9710 error (null)
default 11:39:48.959281-0700 Network /AppleInternal/Library/BuildRoots/4~B5vaugC0N2TQDQ_R5HAg9k4TI3GiWGSqEu_YMV8/Library/Caches/com.apple.xbs/Sources/NetworkPref/NetworkExtension/Model/NetworkPaneSettings.swift:508 notificationDebounceCore() updating observableService for <NetworkSettingsExtension.ANPServiceVPNandProxies: 0x600002db4000>
default 11:39:49.156498-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateStarting: plugin NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]) did detach from IPC
default 11:39:49.159476-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: didSetStatus - 0
default 11:39:49.159519-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateStarting: plugin NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]) disconnected with reason Tunnel was terminated by the server
default 11:39:49.160129-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Leaving state NESMVPNSessionStateStarting
default 11:39:49.160170-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Entering state NESMVPNSessionStateStopping, timeout 20 seconds
default 11:39:49.160223-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: config request: pushing handler [(null)] (null)
default 11:39:49.160259-0700 nesessionmanager <NESMServer: 0x1028c13c0>: Request to uninstall session: NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]
default 11:39:49.160294-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: status changed to disconnecting
default 11:39:49.160507-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Updated network agent (inactive, compulsory, not-user-activiated, not-kernel-activated)
default 11:39:49.161815-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateStopping: session is now uninstalled
default 11:39:49.161926-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateStopping: plugin already disconnected, disposing all plugins
default 11:39:49.161961-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Leaving state NESMVPNSessionStateStopping
default 11:39:49.161998-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Entering state NESMVPNSessionStateDisposing, timeout 5 seconds
default 11:39:49.162910-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: config request: popping handler [(null)] (null)
default 11:39:49.163892-0700 nesessionmanager NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]): Tearing down plugin connection
default 11:39:49.164901-0700 Network NEVPNStatusDidChange:
default 11:39:49.165880-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateDisposing: plugin NEVPNTunnelPlugin(com.apple.NetworkExtension.IKEv2Provider[inactive]) dispose complete
default 11:39:49.165913-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)] in state NESMVPNSessionStateDisposing: all plugins have disposed
default 11:39:49.166318-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Leaving state NESMVPNSessionStateDisposing
default 11:39:49.166356-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: Entering state NESMVPNSessionStateIdle
default 11:39:49.166378-0700 nesessionmanager -[NESMVPNSession unsetDefaultDropAll]: VPN setting IP Drop-All to 0 (Non-Persistent)
default 11:39:49.167066-0700 nesessionmanager NESMIKEv2VPNSession[Primary Tunnel:AlgoVPN simx-algo-vpn IKEv2:F6CFCB8B-E165-49AB-8B43-E037CB36FE19:(null)]: status changed to disconnected, last stop reason Tunnel was terminated by the server
default 11:39:49.169557-0700 nesessionmanager -[NESMVPNSession unsetDefaultDropAll]: VPN setting IP Drop-All to 0 (Non-Persistent)
default 11:39:49.177881-0700 Network NEVPNStatusDidChange:
default 11:39:49.515207-0700 Network /AppleInternal/Library/BuildRoots/4~B5vaugC0N2TQDQ_R5HAg9k4TI3GiWGSqEu_YMV8/Library/Caches/com.apple.xbs/Sources/NetworkPref/NetworkExtension/Model/NetworkPaneSettings.swift:508 notificationDebounceCore() updating observableService for <NetworkSettingsExtension.ANPServiceVPNandProxies: 0x600002db4000>Full log
simx@MacBookAir ~/D/algo ((v2.0.0))> ./algo
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Using CPython 3.13.7 interpreter at: /opt/homebrew/opt/[email protected]/bin/python3.13
Creating virtual environment at: .venv
Built algo @ file:///Users/simx/Development/algo
Installed 20 packages in 376ms
PLAY [Algo VPN Setup] *****************************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************
ok: [localhost]
TASK [Playbook dir stat] **************************************************************************************************
ok: [localhost]
TASK [Ensure Ansible is not being run in a world writable directory] ******************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
TASK [Ensure the requirements installed] **********************************************************************************
ok: [localhost]
TASK [Extract ansible version from pyproject.toml] ************************************************************************
ok: [localhost]
TASK [Parse ansible version requirement] **********************************************************************************
ok: [localhost]
TASK [Get current ansible package version] ********************************************************************************
ok: [localhost]
TASK [Extract ansible version from uv package list] ***********************************************************************
ok: [localhost]
TASK [Verify Python meets Algo VPN requirements] **************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
TASK [Verify Ansible meets Algo VPN requirements] *************************************************************************
ok: [localhost] => {
"changed": false,
"msg": "All assertions passed"
}
PLAY [Ask user for the input] *********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************
ok: [localhost]
[Cloud prompt]
What provider would you like to use?
1. DigitalOcean
2. Amazon Lightsail
3. Amazon EC2
4. Microsoft Azure
5. Google Compute Engine
6. Hetzner Cloud
7. Vultr
8. Scaleway
9. OpenStack (DreamCompute optimised)
10. CloudStack (Exoscale optimised)
11. Linode
12. Install to existing Ubuntu latest LTS server (for more advanced users)
Enter the number of your desired provider
:
TASK [Cloud prompt] *******************************************************************************************************
ok: [localhost]
TASK [Set facts based on the input] ***************************************************************************************
ok: [localhost]
[VPN server name prompt]
Name the vpn server
[algo]
:
TASK [VPN server name prompt] *********************************************************************************************
ok: [localhost]
[Cellular On Demand prompt]
Do you want macOS/iOS clients to enable "Connect On Demand" when connected to cellular networks?
[y/N]
:
TASK [Cellular On Demand prompt] ******************************************************************************************
ok: [localhost]
[Wi-Fi On Demand prompt]
Do you want macOS/iOS clients to enable "Connect On Demand" when connected to Wi-Fi?
[y/N]
:
TASK [Wi-Fi On Demand prompt] *********************************************************************************************
ok: [localhost]
[Trusted Wi-Fi networks prompt]
List the names of any trusted Wi-Fi networks where macOS/iOS clients should not use "Connect On Demand"
(e.g., your home network. Comma-separated value, e.g., HomeNet,OfficeWifi,AlgoWiFi)
:
TASK [Trusted Wi-Fi networks prompt] **************************************************************************************
ok: [localhost]
[Retain the PKI prompt]
Do you want to retain the keys (PKI)? (required to add users in the future, but less secure)
[y/N]
:
TASK [Retain the PKI prompt] **********************************************************************************************
ok: [localhost]
[DNS adblocking prompt]
Do you want to enable DNS ad blocking on this VPN server?
[y/N]
:
TASK [DNS adblocking prompt] **********************************************************************************************
ok: [localhost]
[SSH tunneling prompt]
Do you want each user to have their own account for SSH tunneling?
[y/N]
:
TASK [SSH tunneling prompt] ***********************************************************************************************
ok: [localhost]
TASK [Set facts based on the input] ***************************************************************************************
ok: [localhost]
PLAY [Provision the server] ***********************************************************************************************
TASK [Gathering Facts] ****************************************************************************************************
ok: [localhost]
--> Please include the following block of text when reporting issues:
Algo running on: macOS 15.7.1
Created from git fork. Last commit: 8dc21ce docs: Add FAQ entries for single cipher support and censorship circumvention (#14827)
uv Python environment:
warning: The `tool.uv.dev-dependencies` field (used in `pyproject.toml`) is deprecated and will be removed in a future release; use `dependency-groups.dev` instead
Python 3.13.7
uv 0.8.23 (Homebrew 2025-10-04)
Runtime variables:
algo_provider "digitalocean"
algo_ondemand_cellular "True"
algo_ondemand_wifi "True"
algo_ondemand_wifi_exclude "X3828nNoc3ccnxr99hdGlvbnMgV2ktRmkgNg=="
algo_dns_adblocking "True"
algo_ssh_tunneling "False"
wireguard_enabled "True"
dns_encryption "True"
TASK [Display the invocation environment] *********************************************************************************
changed: [localhost]
TASK [Install cloud provider dependencies] ********************************************************************************
ok: [localhost]
TASK [Generate the SSH private key] ***************************************************************************************
changed: [localhost]
TASK [Generate the SSH public key] ****************************************************************************************
changed: [localhost]
TASK [Copy the private SSH key to /tmp] ***********************************************************************************
changed: [localhost]
TASK [Include a provisioning role] ****************************************************************************************
included: cloud-digitalocean for localhost
[cloud-digitalocean : pause]
Enter your API token. The token must have read and write permissions (https://cloud.digitalocean.com/settings/api/tokens):
(output is hidden):
TASK [cloud-digitalocean : pause] *****************************************************************************************
ok: [localhost]
TASK [cloud-digitalocean : Set the token as a fact] ***********************************************************************
ok: [localhost]
TASK [cloud-digitalocean : Get regions] ***********************************************************************************
ok: [localhost]
TASK [cloud-digitalocean : Set facts about the regions] *******************************************************************
ok: [localhost]
TASK [cloud-digitalocean : Set default region] ****************************************************************************
ok: [localhost]
[cloud-digitalocean : pause]
What region should the server be located in?
1. ams3 Amsterdam 3
2. atl1 Atlanta 1
3. blr1 Bangalore 1
4. fra1 Frankfurt 1
5. lon1 London 1
6. nyc1 New York 1
7. nyc2 NSource: trailofbits/algo