Flow provides a declarative alternative to macOS launchd for managing background services. Instead of scattered .plist files, all services are defined in a single flow.toml configuration.
| launchd | Flow |
|---|---|
| Plist files scattered across ~/Library/LaunchAgents | Single ~/.config/flow/flow.toml |
| Services auto-start on login (hidden cost) | Explicit control: autostart or on-demand |
| Hard to audit what's running | f daemon status shows everything |
| XML format, verbose | TOML format, readable |
| No easy way to temporarily disable | f daemon stop <name> |
# See what's running
f daemon status
# Start a service
f daemon start glm4
# Stop a service
f daemon stop glm4
# Audit macOS launchd services
f macos audit
# Disable a launchd service and migrate to Flow
f macos disable com.example.service
# Then add [[daemon]] entry to flow.toml# List all non-Apple launchd services
f macos list
# See what's currently running
f macos status
# Get recommendations
f macos audit# View plist contents for a service
f macos info com.example.serviceThis shows the binary, arguments, working directory, and environment variables needed to recreate the service in Flow.
f macos disable com.example.serviceThis runs launchctl bootout and launchctl disable to prevent the service from starting.
[[daemon]]
name = "example"
binary = "/path/to/binary"
args = ["--port", "8080"]
working_dir = "/path/to/workdir"
port = 8080
health_url = "http://127.0.0.1:8080/health"
autostart = false # true = starts on login
boot = false # true = starts on system boot
restart = "on-failure" # "never", "on-failure", "always"
description = "Example service"
# Optional: environment variables
[daemon.env]
API_KEY = "secret"f daemon start example| Field | Description |
|---|---|
name |
Unique identifier for the daemon |
binary |
Path to executable |
| Field | Default | Description |
|---|---|---|
command |
- | Subcommand to run (e.g., "server") |
args |
[] |
Arguments passed to binary/command |
working_dir |
- | Working directory for the process |
port |
- | Port the service listens on |
host |
127.0.0.1 |
Host the service binds to |
health_url |
- | URL to check if service is healthy |
autostart |
false |
Start automatically when Flow starts |
boot |
false |
Start on system boot |
autostop |
false |
Stop when leaving project |
restart |
- | Restart policy: never, on-failure, always |
retry |
- | Max restart attempts |
ready_delay |
- | Ms to wait before considering ready |
ready_output |
- | Output pattern to match for readiness |
description |
- | Human-readable description |
env |
{} |
Environment variables |
[[daemon]]
name = "glm4"
binary = "/path/to/venv/bin/python"
args = ["-m", "mlx_lm.server", "--model", "mlx-community/Qwen2.5-7B-Instruct-4bit", "--port", "8080"]
working_dir = "/path/to/mlx-lm"
port = 8080
health_url = "http://127.0.0.1:8080/health"
autostart = false
description = "MLX local LLM server"[[daemon]]
name = "watchman"
binary = "/opt/homebrew/bin/watchman"
args = ["--foreground", "--logfile=/path/to/log"]
autostart = true
boot = true
restart = "on-failure"
description = "Facebook Watchman file watcher"[[daemon]]
name = "api"
binary = "/path/to/node"
args = ["/path/to/server.js"]
working_dir = "/path/to/project"
port = 3000
health_url = "http://127.0.0.1:3000/health"
autostart = false
restart = "on-failure"
retry = 3
description = "API server"
[daemon.env]
NODE_ENV = "production"
DATABASE_URL = "postgres://localhost/db"Configure which services are allowed or should be blocked in your flow.toml:
[macos]
# Services matching these patterns won't be flagged
allowed = [
"com.nikiv.*",
"com.github.facebook.watchman",
"limit.maxfiles",
]
# Services matching these patterns will be recommended for removal
blocked = [
"com.google.*", # Google updaters
"com.adobe.*", # Adobe background services
"us.zoom.*", # Zoom daemon
"com.microsoft.update.*",
"com.dropbox.*",
"com.spotify.webhelper",
]f daemon status # Show all daemon status
f daemon start <name> # Start a daemon
f daemon stop <name> # Stop a daemon
f daemon restart <name> # Restart a daemon
f daemon logs <name> # View daemon logsf macos list [--user] [--system] [--json] # List launchd services
f macos status # Show running non-Apple services
f macos audit [--json] # Audit with recommendations
f macos info <service> # Show service details
f macos disable <service> [-y] # Disable a service
f macos enable <service> # Re-enable a service
f macos clean [--dry-run] [-y] # Disable known bloatware- Start with audit: Run
f macos auditto see what's running unnecessarily - Disable bloatware first: Run
f macos cleanto disable known bloatware - Migrate essential services: Add services you need to
flow.toml - Use autostart sparingly: Only set
autostart = truefor truly essential services - Set health URLs: Enables Flow to verify services are actually running
- Use restart policies:
restart = "on-failure"for production services
# Check if binary exists
ls -la /path/to/binary
# Try running manually
/path/to/binary --args
# Check logs
f daemon logs <name># Force disable
f macos disable <service> -y
# Verify
launchctl list | grep <service>
# Manual removal if needed
launchctl bootout gui/$(id -u)/<service>
launchctl disable gui/$(id -u)/<service># Get details from plist
f macos info <service>
# Or manually
plutil -convert json -o - ~/Library/LaunchAgents/<service>.plist | jq