Recurrence is harder than it looks.
Build reliable recurring schedules locally, then scale the same model to production with rrule.net.
Recurring schedules seem simple until real-world constraints appear:
- timezones and DST
- long-running schedules
- end-of-month and leap-year edge cases
- reliability over time
rrulenet gives you a deterministic RRULE-based model for both local execution and cloud-managed schedules.
Use rrulenet to:
- run local recurring jobs from the command line
- run a persistent local scheduler on your machine
- manage cloud schedules on
rrule.net
- Node.js 24+
npm install -g @rrulenet/cli
rrulenet --helprrulenet --help
rrulenet local add "FREQ=DAILY;BYHOUR=9;BYMINUTE=0;BYSECOND=0" -- echo "sync"
rrulenet local list
rrulenet listLocal data is stored in ./.rrulenet relative to your current working directory.
Override with:
RRULENET_DATA_DIR=/path/to/data rrulenet local listrrulenet config set cloud.api_url https://api.rrule.net
rrulenet config set cloud.token <your_api_key_or_token>
RRULENET_TOKEN=<your_api_key_or_token> rrulenet cloud listThe CLI supports both local execution and cloud schedule management.
| Capability | Local CLI | rrule.net cloud |
|---|---|---|
| RRULE scheduling | ✅ | ✅ |
| Local command execution | ✅ | ❌ |
| Persistent background runner | ✅ | ✅ managed |
| Cloud-managed schedules | ❌ | ✅ |
| API-backed schedule operations | via cloud commands |
✅ |
Use the CLI to build, test, and run schedules locally.
Use rrule.net when you want managed cloud execution and API-backed schedule operations.
rrulenet local run can be managed by a user-level service manager. The CLI does
not install services automatically, but it can generate a template for:
launchdon macOSsystemd --useron Linux
The generated service writes logs in RRULENET_DATA_DIR:
rrulenet-runner.out.logrrulenet-runner.err.log
Generate a plist:
rrulenet local service print --target launchd > ~/Library/LaunchAgents/net.rrule.local-runner.plistLoad and start it:
launchctl unload ~/Library/LaunchAgents/net.rrule.local-runner.plist 2>/dev/null
launchctl load ~/Library/LaunchAgents/net.rrule.local-runner.plist
launchctl start net.rrule.local-runnerStop it:
launchctl stop net.rrule.local-runner
launchctl unload ~/Library/LaunchAgents/net.rrule.local-runner.plistCheck logs:
tail -f ./.rrulenet/rrulenet-runner.out.log
tail -f ./.rrulenet/rrulenet-runner.err.logIf you use a custom data directory, generate the template with:
rrulenet local service print --target launchd --data-dir /path/to/dataGenerate a unit:
mkdir -p ~/.config/systemd/user
rrulenet local service print --target systemd-user > ~/.config/systemd/user/rrulenet-local-runner.serviceReload and start it:
systemctl --user daemon-reload
systemctl --user enable --now rrulenet-local-runnerStop it:
systemctl --user disable --now rrulenet-local-runnerCheck status and logs:
systemctl --user status rrulenet-local-runner
journalctl --user -u rrulenet-local-runner -f
tail -f ./.rrulenet/rrulenet-runner.out.log
tail -f ./.rrulenet/rrulenet-runner.err.logTo change the polling interval or binary path:
rrulenet local service print --target systemd-user --interval-ms 10000 --bin /absolute/path/to/rrulenetFor development, npm link exposes the global rrulenet binary from your local
checkout without a separate global install:
cd cli
npm install
npm run build
npm link
rrulenet --helpThe local scheduler uses Node's native node:sqlite module.
On current Node 24+ releases, node:sqlite does not require the old
--experimental-sqlite flag anymore. Node may still print an
ExperimentalWarning when the module is loaded.
If you want to hide that warning when running the CLI, use:
NODE_OPTIONS=--disable-warning=ExperimentalWarning rrulenet local listThis suppresses ExperimentalWarning messages for the Node process. We do not
recommend using broader flags such as --no-warnings.
MIT
This license applies to the CLI source code in this repository only. Hosted
rrule.net services and APIs are governed separately.