Beagle
Beagle is a macOS job orchestrator. You define jobs in beagle.yaml and beagle manages them through launchd, abstracting away plist files and launchctl commands.
Configuration Format
Config file: beagle.yaml
version: 1
defaults:
timezone: America/Chicago # IANA timezone for scheduled jobs
working_dir: /absolute/path # must be absolute
logs_dir: /absolute/path # override default log location
throttle_seconds: 30 # minimum seconds between runs (>= 0)
env:
KEY: value # environment variables for all jobs
circuit_breaker:
max_failures: 5 # failures before tripping (>= 0)
window_seconds: 600 # failure counting window (>= 0)
cooldown_seconds: 1800 # cooldown before retrying (>= 0)
jobs:
my_job:
type: schedule # "schedule" or "service"
command: ["/absolute/path/to/binary", "--flag"]
schedule:
cron: "0 5 1 * *" # 5-field cron (required for schedule)
timezone: America/New_York # per-job timezone override
restart: never # "never", "on-failure", or "always"
enabled: true # default true; set false to skip
working_dir: /absolute/path # per-job override
env:
KEY: value # merged with defaults.env
throttle_seconds: 60 # per-job override
circuit_breaker: # per-job override
max_failures: 3
window_seconds: 300
cooldown_seconds: 900
Validation Rules
- •
versionmust be1. - •At least one job is required.
- •Job IDs must match
^[a-z0-9][a-z0-9_-]{1,63}$. - •
command[0]must be an absolute path.commandmust be non-empty. - •
working_dir(both defaults and per-job) must be absolute if set. - •
schedule.cronis required forschedulejobs and forbidden forservicejobs. - •
schedule.cronmust have exactly 5 fields. - •
timezonevalues must be valid IANA timezone names. - •
throttle_secondsand allcircuit_breakerfields must be >= 0. - •
restartmust be one of:never,on-failure,always.
Commands
| Command | Description |
|---|---|
beagle validate | Validate config file |
beagle apply | Reconcile managed jobs with launchd |
beagle ls | List configured jobs and their state |
beagle status <job> | Show detailed status for a job |
beagle logs <job> [--stderr] [--tail N] | Show job stdout (or stderr) logs |
beagle failures [--job <job>] [--limit N] | Show recent failures |
beagle run-now <job> | Trigger an immediate run |
beagle enable <job> | Enable a job |
beagle disable <job> | Disable a job |
beagle doctor | Run environment diagnostics |
beagle profile register <name> <config-path> | Register a named profile |
beagle profile ls | List all profiles |
beagle profile use <name> | Set the active profile |
beagle profile rm <name> | Remove a profile |
Global flags: --config <path>, --profile <name>.
Where <job> appears above, you can use a bare job ID or <profile>:<job> selector syntax.
Profile Workflow
Profiles let you manage multiple repos, each with their own beagle.yaml.
# Register profiles pointing at different config files beagle profile register myapp /Users/me/src/myapp/beagle.yaml beagle profile register infra /Users/me/src/infra/beagle.yaml # List and switch beagle profile ls beagle profile use infra # Target a specific profile's job without switching beagle status myapp:daily_backup # Remove a profile beagle profile rm infra
Profile names must match ^[a-z0-9][a-z0-9_-]{0,63}$ (min 1 char). Job IDs use {1,63} (min 2 chars).
Config Resolution Precedence
- •
--config <path>(explicit path) - •
--profile <name>(named profile) - •Active profile (set via
beagle profile use) - •
./beagle.yamlin the current directory
The profile:job selector syntax in a job argument overrides --profile for that command.
Each profile gets its own namespace, which isolates launchd labels and log directories so jobs from different repos never collide.
Key Paths
| What | Path |
|---|---|
| Project config | beagle.yaml (in project root) |
| Profile registry | ~/.config/beagle/profiles.yaml |
| Run history DB | ~/.local/share/beagle/beagle.db |
| Job logs | ~/.local/share/beagle/logs/<namespace>/<job>/stdout.log |
| Job stderr | ~/.local/share/beagle/logs/<namespace>/<job>/stderr.log |
| Launchd plists | ~/Library/LaunchAgents/com.beagle.<user>.<namespace>.<job>.plist |
Common Workflows
Adding a Scheduled Job
- •Add a
scheduletype job tobeagle.yamlwith acronexpression. - •Run
beagle validateto check the config. - •Run
beagle applyto install the job into launchd. - •Verify with
beagle lsandbeagle status <job>.
Adding a Service
- •Add a
servicetype job tobeagle.yaml(nocronfield). - •Set
restart: on-failureorrestart: alwaysas appropriate. - •Run
beagle validatethenbeagle apply.
Setting Up Multi-Repo Profiles
- •Register each repo:
beagle profile register <name> /path/to/repo/beagle.yaml. - •Switch context:
beagle profile use <name>. - •All commands now target that profile's config. Use
profile:jobto reach across profiles without switching.
Debugging a Failing Job
- •
beagle failures --job <job>to see recent failure history with exit codes. - •
beagle logs <job>andbeagle logs <job> --stderrto inspect output. - •
beagle status <job>to check whether the job is loaded and enabled. - •
beagle doctorto verify the environment (home dir, launchd backend). - •
beagle run-now <job>to trigger a manual run and observe behavior.