NoMacSign in
← Blog

I shipped an iPhone app from a Hetzner Linux server

The workflow that led to NoMac: keep the repository and coding agent on a Hetzner Linux VPS, build remotely on macOS, and test every release on an iPhone.

· 9 min read


NoMac began with a very personal problem: my coding agent and project lived on a Hetzner Linux server, but the app I wanted to ship was for the iPhone. The server was always available, easy to reach, and a good place for long-running agent work. It was also the one place where Xcode could never run.

I did not need to turn the VPS into a Mac. I needed a clean boundary: keep the repository and agent on Linux, send an exact project snapshot to macOS when it was time to compile, then put the signed result on my iPhone through TestFlight. That is the workflow behind NoMac.

The Hetzner server is the persistent development workspace, not the iOS build machine. Xcode and code signing run on a separate cloud Mac. The native app is then distributed by Apple through TestFlight.

Why use a VPS for an iOS project?

A small cloud server is a useful home for an AI coding agent because it does not disappear when a laptop sleeps. You can reconnect from another computer or a phone, leave tests and analysis running, and keep the repository in one stable environment. The agent also has the normal Linux tools used by modern software projects.

Hetzner describes its cloud servers as virtual machines that can run Linux distributions including Ubuntu, Debian, Fedora, Rocky Linux, and AlmaLinux. Its cloud server overview also distinguishes shared and dedicated resources. You do not need a special server class for a small Swift repository; choose capacity for your agent and its non-Xcode workloads.

The limit is the operating system. Apple's current Xcode system requirements list supported macOS versions. A normal Linux VPS can edit Swift source, but it cannot install the supported Xcode toolchain or perform an App Store distribution build.

The architecture that works

PlaceWhat lives thereWhat it does
Hetzner VPSGit repository, coding agent, Node.js, project toolsWrites code, reviews diffs, calls MCP tools
NoMac cloud MacTemporary project snapshot and Apple toolchainCompiles, signs, and uploads the build
App Store ConnectApp record, processed builds, beta groupsControls TestFlight and store state
iPhoneTestFlight and the beta appProves the app works for a real user

The useful feature is the return path. A failed build should come back to the Linux agent as a classified problem with the relevant evidence. Then the same agent that changed the code can fix it, upload a new snapshot, and try again. You do not need to download an Xcode log, find the important line, and paste it into a chat.

Set up the Hetzner workspace

1. Create and secure the server

Hetzner lets you choose the location, Linux image, networking, SSH key, firewall, backups, and server size during creation. Its server creation guide notes that the Console can add an SSH key only while the server is being created. Add the key at that point, enable a firewall, and avoid using the root account as the everyday agent user.

  • ·Use SSH keys rather than a reusable root password.
  • ·Create an unprivileged user for the repository and coding agent.
  • ·Open only the network ports you really need.
  • ·Enable backups or snapshots, and keep the Git remote as a second recovery path.
  • ·Install only the language runtimes and agent tools used by the project.

A public VPS receives automated login attempts. Convenience is not a reason to place an Apple private key, signing certificate, or plaintext service token in the repository.

2. Put the project where the agent runs

ssh agent@your-server
git clone <your-private-repository-url>
cd <your-project>
git status

Install your chosen coding agent using its official instructions. Keep project-specific guidance in AGENTS.md or the equivalent file the agent supports: how to test, which files are generated, the bundle identifier, and which actions require approval. Do not put secrets in that guidance.

3. Connect NoMac on the server

Run the device login inside the same Linux user account that runs the agent:

npx @nomac/cli login

The command prints a browser approval step. Open it on your own device and approve the short code. You do not have to copy the App Store Connect private key into the SSH session or the agent's context.

Then register npx @nomac/cli mcp with the agent. For example:

# Claude Code
claude mcp add nomac -- npx @nomac/cli mcp

# OpenAI Codex
codex mcp add nomac -- npx @nomac/cli mcp

Cursor and Grok Build use configuration files instead. The Cursor guide and Grok Build guide contain the exact formats.

My working release loop

Make one reviewable change

I ask the agent to inspect first, explain its plan, and finish one vertical slice. A vertical slice is something the user can actually do—for example, create and reopen a note—not a pile of unrelated models and placeholder screens.

The repository must contain a complete iOS project or workspace. Linux can host and edit an.xcodeproj; it simply cannot prove that the project is valid until the macOS build runs.

Push a snapshot and smoke-test it

The agent calls push_project and then build with workflow=smoke. If compilation fails, it polls status, calls get_failure, and uses the classified error instead of guessing from the final line of a long log.

Push the current project to NoMac and run a smoke build.
Wait for the final status. If it fails, retrieve the failure details, separate
source errors from project, dependency, and signing errors, then propose the
smallest fix before editing. Push again after every change.

Release to TestFlight

A passing smoke build says the project compiles; it does not create a beta. For that, the agent starts workflow=release. The remote Mac archives and signs the app, and the build is uploaded to App Store Connect. After Apple processes it, TestFlight can install it on the iPhone.

This is where the setup becomes more than remote CI. I can notice a problem on the phone, tell the agent on the server exactly what happened, and let it carry the fix back through the build pipeline. The server is the workshop; the phone is the test bench.

What I would not put on the VPS

  • ·The App Store Connect .p8 file in the project. It is a long-lived account credential and should never be committed or exposed to the model.
  • ·Manual signing certificates. Let the managed release service handle certificates and provisioning instead of copying them between machines.
  • ·Production user data. A development agent does not need a database dump to build an iPhone interface.
  • ·An unrestricted unattended submit command. Test builds are reversible; an App Store submission should wait for a human decision.

Also review what push_project will include. Ignore environment files, editor state, dependency caches, compiled output, local databases, and signing material. A smaller snapshot is faster and easier to reason about.

What still needs a human

The VPS and agent can automate the mechanical path, but they cannot truthfully answer legal and product questions for you. You still need to create the Apple account and app record, approve the initial credentials, test the app, verify the privacy answers, and authorize the final submission.

Run review_lint before release and let the agent prepare metadata and screenshots. Then review the claims against the actual build. Apple's privacy label, age rating, export compliance, content rights, and agreements describe your app and business, not the source tree.

Why this became NoMac

The important realization was not that AI could write Swift on Linux. Text editors have always been able to write Swift on Linux. The change was giving the agent a reliable action after it wrote the code: build it with Xcode, see the result, fix it, and deliver it to a phone without a Mac sitting on my desk.

If your agent already lives on a Hetzner VPS, you can keep that setup. Add the missing macOS release boundary instead of rebuilding your development environment around Apple hardware. For the same workflow controlled from a phone, continue with how to build an iPhone app from your iPhone.

Ship it without a Mac.

Signed builds, TestFlight and App Store submission — driven by your agent.

Start shipping