cd /news/machine-learning/tips-for-running-stable-background-m… · home topics machine-learning article
[ARTICLE · art-85427] src=dev.to ↗ pub= topic=machine-learning verified=true sentiment=· neutral

Tips for Running Stable Background ML Inference on macOS

A developer from Workstyle Tech shared tips for running background ML inference services on macOS, highlighting pitfalls such as missing GNU coreutils commands and locale-related errors. The post recommends using nohup and disown for backgrounding, setting LC_ALL=C to avoid 'Illegal byte sequence' errors in log processing, and polling health endpoints instead of using fixed sleeps. These practices aim to improve reliability of startup scripts for services like FastAPI and uvicorn.

read3 min views1 publishedAug 4, 2026

📝 Originally published (in Japanese) at

[forge.workstyle.tech].

Running an inference service as a background process on macOS, with a Linux server mindset, can lead to subtle issues. Things like "a one-liner that works on Linux doesn't work on Mac" or "grepping logs results in garbled text errors and crashes" — these are minor but time-consuming problems.

This article compiles a collection of short tips gathered from running a Seed-VC based voice conversion service (FastAPI + uvicorn, local 127.0.0.1:8770

) as a background process on macOS. It focuses on macOS-specific pitfalls not covered in Linux-centric articles.

setsid

/ timeout

are not available on macOS First, it's important to note that macOS (BSD-based) does not include GNU coreutils' setsid or timeout by default. If you use these commands, which are used for backgrounding and timed execution on Linux, directly in a Mac script, you'll get a

command not found

error.There are two solutions:

coreutils

via Homebrew and use gsetsid

/gtimeout

For background scripts that avoid external dependencies, using standard tools as alternatives is a safer option.

nohup

  • disown

for background processes In environments without setsid

, the combination of nohup

and disown

is reliable for keeping processes alive even after closing the shell.

nohup bash scripts/start-backend.sh > backend.log 2>&1 &
disown

nohup

... Ignores hangup signals (SIGHUP), keeping the process alive even after the terminal is closed> backend.log 2>&1

... Redirects standard output and standard error to a log file&

... Runs the process in the backgrounddisown

... Removes the job from the shell's job table, preventing it from being terminated when the shell is closedWhile nohup

alone usually keeps the process alive, adding disown

ensures that closing the terminal won't accidentally terminate the process.

tr

/ grep

fail with binary data in logs → use LC_ALL=C

This was the most problematic issue on Mac. Inference logs may contain progress bar control characters or, occasionally, garbled multibyte sequences. When processed by macOS's tr

or grep

, you'll see:

tr: Illegal byte sequence

This happens because the locale is set to UTF-8, causing invalid byte sequences to be treated as "invalid characters" and throwing an exception.

The solution is to set the locale to C (pass-through as byte sequences) for those commands.

LC_ALL=C tr -d '\r' < backend.log > backend.clean.log
LC_ALL=C grep "ERROR" backend.log

Setting LC_ALL=C

treats text as "bytes" rather than "characters," preventing crashes due to invalid sequences. This is safer for pipelines that process or search logs programmatically.

models takes time, so sending requests immediately after starting with nohup

will fail because the service isn't ready. Using sleep 10

as a workaround is unreliable—too short for slow machines and too long for fast ones.

The proper approach is to poll the service's health endpoint until it returns a 200 status. In this setup, the health endpoint is http://127.0.0.1:8770/health

, so we poll it.

until curl -sf http://127.0.0.1:8770/health >/dev/null; do
  sleep 1
done
echo "backend ready"

curl -sf

exits with a non-zero status on failure, so combining it with until

allows you to wait until the service is ready. Waiting based on state, not a fixed delay, significantly improves the reliability of startup scripts.

pkill

using pattern matching For background processes without a saved PID, pkill

with a command-line pattern is convenient for stopping them.

pkill -f "uvicorn server:app"

The -f

option matches the entire command line, so including specific details like the port or app name in the pattern prevents unrelated processes from being affected. For more precision, save the PID during startup and target it directly.

setsid

/timeout

coreutils

(gsetsid

/gtimeout

) or use standard tool alternativesnohup ... & disown

tr

/grep

to fail with Illegal byte sequence

LC_ALL=C

sleep

delays, until curl -sf

pkill -f "specific pattern"

── more in #machine-learning 4 stories · sorted by recency
── more on @workstyle tech 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/tips-for-running-sta…] indexed:0 read:3min 2026-08-04 ·