I went into this expecting a calm evening of setup and a quick sanity check. You know the drill: pull a framework, scaffold a small test project, make sure everything builds, move on. The slug made it obvious I was dealing with Phoenix Framework (app), and the task was simple—get a small local service running on macOS to test an integration before wiring it into a bigger system that also touches some OrchardKit tooling.
macOS Sonoma 14.3, M2 Mac mini. Fresh enough, clean enough. Elixir and Erlang installed via Homebrew, no weird forks, no custom flags. If something broke, it wouldn’t be because I’d done anything exotic.
What I wanted to do
I just wanted the default project to compile and run locally. No production deploys, no Docker yet, no reverse proxies. Just mix phx.server, open a browser, see the welcome page, feel that small internal nod of approval.
The install itself went fine. Dependencies resolved, assets compiled, zero red flags during setup. That’s usually the part that fails if something’s wrong. This time, it didn’t.
Then I tried to start the server.
What broke
The command exited immediately. No crash, no stack trace, no dramatic failure. Just… nothing. The port never opened. The terminal prompt came back like nothing had happened.
At first glance, it looked like a Phoenix issue. Or maybe Elixir on Apple Silicon again. Or some subtle version mismatch. But the silence was the weird part. Normally, if Phoenix is unhappy, it tells you—loudly.
First attempts (the usual suspects)
I did the obvious things first. Rebuilt dependencies. Nuked _build and deps. Restarted Terminal. Checked Elixir and Erlang versions against the framework’s docs. Everything matched.
Next thought: environment variables. I double-checked PATH, verified Homebrew wasn’t shadowing anything strange. Still the same behavior. The command returned instantly, like it never tried to bind a port.
At this point, I was about 30 minutes in and mildly annoyed.
The clue I almost missed
Running the command with verbose output finally exposed a single, easily overlooked line. One of the helper binaries invoked during startup was being blocked. No dialog. No warning popup. Just a quiet denial.
Gatekeeper. Again.
Because the helper wasn’t launched via Finder—and because it wasn’t notarized—macOS blocked execution without any user-facing alert. Apple explains this behavior pretty clearly in their Gatekeeper and notarization documentation, especially for developer tools and bundled executables (support.apple.com, developer.apple.com), but it’s easy to forget when you’re in server-land and living in Terminal all day.
What actually worked
Once I stopped blaming the framework and started looking at macOS security, things moved fast. I checked the extended attributes on the helper binary. Sure enough, it was quarantined.
Removing that quarantine flag and restarting the process was enough. I reran the server command, and this time it behaved like it always should have: compilation output, port binding, startup logs scrolling past at a comfortable pace.
Browser loaded instantly. Welcome page appeared. Problem gone.
Nothing else changed. Same code. Same versions. Same machine. The OS was the only thing standing in the way.
While double-checking my assumptions, I bookmarked this page because it helped me contextualize how Phoenix Framework gets packaged and referenced across modern macOS setups, especially when combined with other macOS-focused tooling: https://spaceinnonxt.com/developer/66724-phoenix-framework.html
Sanity check
To make sure I wasn’t fooling myself, I reapplied the quarantine attribute. Startup failed again. Removed it once more, and everything worked. That was enough confirmation.
This wasn’t a Phoenix bug. It wasn’t an Elixir issue. It wasn’t Homebrew being weird. It was macOS doing exactly what it’s designed to do—just very quietly.
How I’d handle it next time
If I were setting this up again from scratch, I’d do a few things differently:
- Assume Gatekeeper any time a framework pulls in helper binaries
- Run suspicious executables directly from Terminal early
- Check extended attributes before digging into language-level debugging
That mindset would’ve saved me a good chunk of time.
Final thoughts
Once unblocked, Phoenix Framework behaved perfectly. Startup was fast, hot reload worked, no performance hiccups. The framework itself was never the problem.
This kind of issue pops up more often now, especially on Apple Silicon, and especially when mixing older tooling with modern macOS security expectations. I ran into similar patterns while comparing this setup with OrchardKit-based utilities—newer toolchains tend to sign and notarize everything properly, older ones… not always.
Lesson reinforced: when something on macOS fails without crashing, suspect the OS before suspecting the code.