Run OpenAI-Compatible API Proxy as macOS Background Service
Problem
I closed my terminal and my AI proxy died. The next morning, I rebooted my Mac and had to manually start the proxy again. Then the proxy crashed during a streaming response, and I didn’t notice until my application started failing with connection errors.
Here’s what my workflow looked like:
# Every day: Open terminal, start proxy$ ai-menshen -config ~/.config/ai-menshen/config.toml
# If terminal closes: Proxy dies# If Mac reboots: Proxy doesn't restart# If proxy crashes: No automatic recovery
# Multiple terminals with proxy processes:Terminal 1: ai-menshen runningTerminal 2: ai-menshen running (conflict!)Terminal 3: Which one is the real proxy?I was spending time managing the proxy instead of using it. The process was fragile—terminal sessions die, reboots happen, crashes occur.
What happened?
I searched for solutions to run services in the background. On Linux, I’d use systemd. But on macOS, the native service manager is launchd.
I discovered that ai-menshen already provides a ready-to-use launchd plist file. The tool’s repository includes configs/net.liujiacai.ai-menshen.plist configured for:
- Auto-start on login (RunAtLoad)
- Automatic crash recovery (KeepAlive)
- Centralized logging to
/tmp/ai-menshen-stderr.log - Standard binary path at
~/.local/bin/ai-menshen - Standard config path at
~/.config/ai-menshen/config.toml
The architecture is straightforward:
BEFORE: Manual terminal management┌─────────────┐│ Terminal │──starts──▶│ ai-menshen │──▶│ OpenAI API ││ window │ │ │ │ │└─────────────┘ └─────────────┘ └────────────┘ Fragile Manual only Dies on crash Dies on close No auto-restart No logging
AFTER: launchd managed service┌─────────────┐ ┌─────────────┐ ┌────────────┐│ launchd │──manages─▶│ ai-menshen │──▶│ OpenAI API ││ (macOS) │ │ (background)│ │ │└─────────────┘ └─────────────┘ └────────────┘ Auto-start Always running Continuous Crash recovery No terminal Logging to fileHow to solve it?
Step 1: Verify installation
I first ensured ai-menshen was properly installed:
# Check binary existsls ~/.local/bin/ai-menshen
# If not installed, run the installercurl -fsSL https://raw.githubusercontent.com/jiacai2050/ai-menshen/main/install.sh | sh
# Verify versionai-menshen -versionThe binary should be at ~/.local/bin/ai-menshen. If it’s elsewhere, I’d need to update the plist later.
Step 2: Create configuration
I generated the config file:
mkdir -p ~/.config/ai-menshenai-menshen -gen-config > ~/.config/ai-menshen/config.toml
# Edit to add your upstream API keyvi ~/.config/ai-menshen/config.tomlThe config path must match what’s in the plist: ~/.config/ai-menshen/config.toml.
Step 3: Copy the plist
I copied the provided plist to the LaunchAgents directory:
# Create LaunchAgents directory if neededmkdir -p ~/Library/LaunchAgents
# Copy the plistcp configs/net.liujiacai.ai-menshen.plist ~/Library/LaunchAgents/
# Verify it's therels ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistLaunchAgents are user-level services that run when you log in. LaunchDaemons are system-level services that run at boot before login. For a personal proxy, LaunchAgents is the right choice.
Step 4: Load the service
I loaded the service into launchd:
launchctl load ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plist
# The service starts immediately (RunAtLoad = true)Step 5: Verify it’s running
I checked the service status:
launchctl list | grep ai-menshen
# Expected output shows the process ID:# 12345 0 net.liujiacai.ai-menshen# PID exit code label
# If the second column shows a non-zero number, the service crashed# If the PID is blank, the service isn't runningI also tested the proxy directly:
curl http://localhost:8080/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer your-token" \ -d '{"model": "gpt-4o", "messages": [{"role": "user", "content": "test"}]}'
# Open dashboard in browseropen http://localhost:8080Step 6: Check logs
I viewed the service logs:
# Standard error outputtail -f /tmp/ai-menshen-stderr.log
# Standard output (if configured)tail -f /tmp/ai-menshen-stdout.logThe logs showed startup messages and any errors.
Service management commands
Stop the service
launchctl unload ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plist
# The proxy stops immediatelyRestart the service
launchctl unload ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistlaunchctl load ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistReload after plist changes
If I edited the plist, I needed to unload and reload:
# FIRST unloadlaunchctl unload ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plist
# THEN make changesvi ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plist
# THEN reloadlaunchctl load ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistCustomizing the plist
The provided plist has default paths. If my paths differ, I edited the plist:
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict> <key>Label</key> <string>net.liujiacai.ai-menshen</string> <key>ProgramArguments</key> <array> <!-- UPDATE THIS PATH to match your binary location --> <string>/Users/YOUR_NAME/.local/bin/ai-menshen</string> <string>-config</string> <!-- UPDATE THIS PATH to match your config location --> <string>/Users/YOUR_NAME/.config/ai-menshen/config.toml</string> </array> <key>RunAtLoad</key> <true/> <!-- Start immediately when loaded --> <key>KeepAlive</key> <true/> <!-- Restart on crash --> <key>StandardErrorPath</key> <string>/tmp/ai-menshen-stderr.log</string> <key>StandardOutPath</key> <string>/tmp/ai-menshen-stdout.log</string></dict></plist>Key plist keys explained:
Label: Unique identifier for the serviceProgramArguments: Command to run (binary + args)RunAtLoad: Start immediately when loaded (true/false)KeepAlive: Restart automatically if process dies (true/false)StandardErrorPath: Where stderr output goesStandardOutPath: Where stdout output goesThe reason
Why does launchd work better than manual terminal management?
Auto-start on login: RunAtLoad ensures the proxy starts when you load the plist. Combined with placing it in ~/Library/LaunchAgents, macOS loads it automatically when you log in.
Automatic crash recovery: KeepAlive tells launchd to restart the process if it exits. If the proxy crashes during a streaming response or runs out of memory, launchd brings it back up within seconds.
Background operation: No terminal window needed. The process runs as a daemon, freeing up your terminals for other work.
Centralized logging: All output goes to known files. I can check /tmp/ai-menshen-stderr.log anytime to see what happened.
Standard macOS pattern: launchd is the native way to manage services on macOS. It’s reliable, well-documented, and integrated with the OS.
Common mistakes
Mistake 1: Wrong paths in plist
<!-- BAD: Generic path that doesn't exist --><string>/Users/YOUR_NAME/.local/bin/ai-menshen</string>
<!-- GOOD: Your actual username --><string>/Users/johndoe/.local/bin/ai-menshen</string>I checked my actual path:
# Find where ai-menshen is installedwhich ai-menshen# Output: ~/.local/bin/ai-menshen
# Get absolute pathrealpath ~/.local/bin/ai-menshenMistake 2: Not unloading before plist changes
# BAD: Edit while loadedvi ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plist# Changes don't take effect until reload!
# GOOD: Unload, edit, reloadlaunchctl unload ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistvi ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistlaunchctl load ~/Library/LaunchAgents/net.liujiacai.ai-menshen.plistMistake 3: Ignoring stderr log
# If the service shows a non-zero exit code in launchctl list:launchctl list | grep ai-menshen# Output: - 1 net.liujiacai.ai-menshen (exit code 1 = error)
# CHECK THE LOG:cat /tmp/ai-menshen-stderr.log
# Common errors:# - "config file not found" → Check config path# - "permission denied" → Check file permissions# - "address already in use" → Another instance runningMistake 4: Multiple proxy instances
# If the proxy won't start, check for other instances:ps aux | grep ai-menshen
# Kill stray processes before starting launchd service:pkill -f ai-menshenSummary
In this post, I showed how to run ai-menshen as a macOS launchd background service. The key point is using launchd for auto-start on login and automatic crash recovery—no manual terminal management required.
The provided plist handles everything:
- Auto-start via
RunAtLoad - Crash recovery via
KeepAlive - Logging to
/tmp/ai-menshen-stderr.log
I went from fragile terminal sessions that died on close to a robust background service that survives reboots and crashes. The proxy is now always ready to handle AI API requests.
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
- 👨💻 ai-menshen GitHub Repository
- 👨💻 launchd Official Documentation
- 👨💻 LaunchAgents vs LaunchDaemons
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments