{"id":34611188,"url":"https://github.com/roadrunner-plugins/cron","last_synced_at":"2026-04-17T13:01:48.260Z","repository":{"id":323172888,"uuid":"1092352257","full_name":"roadrunner-plugins/cron","owner":"roadrunner-plugins","description":"A native RoadRunner plugin that manages scheduled command execution within the RoadRunner process itself. The plugin will parse cron expressions, execute shell commands at scheduled intervals, and provide comprehensive observability through Prometheus metrics and debug logging.","archived":false,"fork":false,"pushed_at":"2025-11-13T06:25:56.000Z","size":30,"stargazers_count":23,"open_issues_count":2,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-26T02:58:08.045Z","etag":null,"topics":["php","roadrunner","roadrunner-plugin"],"latest_commit_sha":null,"homepage":"https://build.roadrunner.dev/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/roadrunner-plugins.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-11-08T13:22:14.000Z","updated_at":"2025-12-06T20:04:47.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/roadrunner-plugins/cron","commit_stats":null,"previous_names":["roadrunner-plugins/cron"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/roadrunner-plugins/cron","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roadrunner-plugins%2Fcron","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roadrunner-plugins%2Fcron/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roadrunner-plugins%2Fcron/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roadrunner-plugins%2Fcron/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/roadrunner-plugins","download_url":"https://codeload.github.com/roadrunner-plugins/cron/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/roadrunner-plugins%2Fcron/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31930100,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-17T12:37:54.787Z","status":"ssl_error","status_checked_at":"2026-04-17T12:37:25.095Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["php","roadrunner","roadrunner-plugin"],"created_at":"2025-12-24T14:13:25.128Z","updated_at":"2026-04-17T13:01:48.193Z","avatar_url":"https://github.com/roadrunner-plugins.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RoadRunner Cron Plugin\n\nA native RoadRunner plugin for executing scheduled tasks using cron expressions. Eliminates the need for external cron\ndaemons by managing scheduled command execution within the RoadRunner process itself.\n\n## Features\n\n- **Standard Cron Syntax**: Supports both 5-field and 6-field (with seconds) cron expressions\n- **Special Expressions**: Built-in support for `@daily`, `@hourly`, `@every`, and more\n- **Overlap Prevention**: Configurable control over concurrent job executions\n- **Timeout Management**: Graceful termination with SIGTERM/SIGKILL progression\n- **Log Rotation**: Size-based automatic log rotation with configurable retention\n- **Prometheus Metrics**: Comprehensive observability including execution counts, durations, and errors\n- **Graceful Shutdown**: Respects running jobs during RoadRunner shutdown\n\n## Configuration\n\nAdd a `cron` section to your `.rr.yaml`:\n\n```yaml\nversion: \"3\"\n\ncron:\n  # Global settings\n  timezone: \"UTC\"              # Timezone for schedule interpretation (default: UTC)\n  grace_period: \"30s\"          # Time to wait for jobs during shutdown (default: 30s)\n\n  # Scheduled jobs\n  jobs:\n    - name: \"cache-warmup\"\n      command: \"php artisan cache:warmup\"\n      schedule: \"0 */6 * * *\"  # Every 6 hours\n      allow_overlap: false     # Prevent concurrent executions (default: false)\n      timeout: \"5m\"            # Maximum execution time\n      log_file: \"/var/log/cron/cache-warmup.log\"\n      max_log_size: 10485760   # 10MB (default)\n      max_log_files: 5         # Keep 5 rotated files (default)\n\n    - name: \"queue-cleanup\"\n      command: \"php artisan queue:prune-failed --hours=48\"\n      schedule: \"@daily\"\n      timeout: \"10m\"\n      log_file: \"/var/log/cron/queue-cleanup.log\"\n\n    - name: \"health-check\"\n      command: \"php artisan app:health-check\"\n      schedule: \"@every 30s\"   # Every 30 seconds\n      allow_overlap: true      # Allow concurrent checks\n      timeout: \"5s\"\n      log_file: \"/var/log/cron/health-check.log\"\n\n# Optional: Enable metrics\nmetrics:\n  address: \"0.0.0.0:2112\"\n```\n\n## Cron Expression Syntax\n\n### Standard 5-field format:\n\n```\n* * * * *\n│ │ │ │ │\n│ │ │ │ └─── Day of week (0-6, Sunday=0)\n│ │ │ └───── Month (1-12)\n│ │ └─────── Day of month (1-31)\n│ └───────── Hour (0-23)\n└─────────── Minute (0-59)\n```\n\n### Extended 6-field format (with seconds):\n\n```\n* * * * * *\n│ │ │ │ │ │\n│ │ │ │ │ └─── Day of week (0-6)\n│ │ │ │ └───── Month (1-12)\n│ │ │ └─────── Day of month (1-31)\n│ └───────── Hour (0-23)\n└─────────── Minute (0-59)\nSecond (0-59)\n```\n\n### Special expressions:\n\n- `@yearly` / `@annually` - Run once a year at midnight on January 1st\n- `@monthly` - Run once a month at midnight on the first day\n- `@weekly` - Run once a week at midnight on Sunday\n- `@daily` / `@midnight` - Run once a day at midnight\n- `@hourly` - Run at the beginning of every hour\n- `@every \u003cduration\u003e` - Run at fixed intervals (e.g., `@every 5m`, `@every 30s`)\n\n### Examples:\n\n```yaml\nschedule: \"*/15 * * * *\"      # Every 15 minutes\nschedule: \"0 2 * * *\"         # Daily at 2 AM\nschedule: \"0 0 * * 0\"         # Every Sunday at midnight\nschedule: \"30 3 15 * *\"       # 3:30 AM on the 15th of each month\nschedule: \"@every 1h30m\"      # Every 1 hour 30 minutes\n```\n\n## Configuration Options\n\n### Job Configuration\n\n| Field           | Type     | Required | Default    | Description                                    |\n|-----------------|----------|----------|------------|------------------------------------------------|\n| `name`          | string   | Yes      | -          | Unique identifier for the job                  |\n| `command`       | string   | Yes      | -          | Shell command to execute                       |\n| `schedule`      | string   | Yes      | -          | Cron expression                                |\n| `allow_overlap` | bool     | No       | `false`    | Allow concurrent executions                    |\n| `timeout`       | duration | No       | none       | Maximum execution time                         |\n| `log_file`      | string   | No       | -          | Path to log file (output discarded if not set) |\n| `max_log_size`  | int64    | No       | `10485760` | Max log file size in bytes (10MB)              |\n| `max_log_files` | int      | No       | `5`        | Number of rotated log files to keep            |\n\n### Global Configuration\n\n| Field          | Type     | Required | Default | Description                                   |\n|----------------|----------|----------|---------|-----------------------------------------------|\n| `timezone`     | string   | No       | `UTC`   | Timezone for schedule interpretation          |\n| `grace_period` | duration | No       | `30s`   | Time to wait for running jobs during shutdown |\n\n## Log Output\n\nWhen `log_file` is configured, the plugin writes command output with execution markers:\n\n```bash\n=== Job 'cache-warmup' started at 2024-01-15T10:00:00Z ===\nCache warmup started...\nProcessing 1500 items...\nCache warmup completed successfully\n=== Job 'cache-warmup' completed at 2024-01-15T10:02:15Z ===\n\n=== Job 'cache-warmup' started at 2024-01-15T16:00:00Z ===\nCache warmup started...\nError: Connection timeout\n=== Job 'cache-warmup' failed at 2024-01-15T16:00:30Z: exit status 1 ===\n```\n\nLog files are automatically rotated when they reach `max_log_size`. Old logs are kept according to `max_log_files`\nsetting.\n\n## Prometheus Metrics\n\nThe plugin exposes the following metrics when the metrics plugin is enabled:\n\n### Counters:\n\n- `roadrunner_cron_executions_total{job_name, status}` - Total executions (status: success, failure)\n- `roadrunner_cron_skipped_total{job_name}` - Skipped executions due to overlap prevention\n- `roadrunner_cron_timeout_total{job_name}` - Executions terminated due to timeout\n\n### Histograms:\n\n- `roadrunner_cron_execution_duration_seconds{job_name}` - Execution duration\n    - Buckets: 0.1, 0.5, 1, 5, 10, 30, 60, 120, 300 seconds\n\n### Gauges:\n\n- `roadrunner_cron_running_jobs{job_name}` - Currently running job instances\n\n### Example Queries:\n\n**Job success rate:**\n\n```promql\nrate(roadrunner_cron_executions_total{status=\"success\"}[5m]) \n/ \nrate(roadrunner_cron_executions_total[5m])\n```\n\n**Jobs timing out:**\n\n```promql\nincrease(roadrunner_cron_timeout_total[1h])\n```\n\n**Average execution duration:**\n\n```promql\nrate(roadrunner_cron_execution_duration_seconds_sum[5m]) \n/ \nrate(roadrunner_cron_execution_duration_seconds_count[5m])\n```\n\n**Jobs currently running:**\n\n```promql\nsum(roadrunner_cron_running_jobs) by (job_name)\n```\n\n## Behavior Details\n\n### Overlap Prevention\n\nWhen `allow_overlap: false` (default):\n\n- If a job is already running, subsequent scheduled executions are skipped\n- Skipped executions are logged and counted in metrics\n- No queuing of missed executions\n\nWhen `allow_overlap: true`:\n\n- Multiple instances can run concurrently\n- No limit on concurrent executions (use with caution for resource-intensive commands)\n\n### Timeout Handling\n\nWhen a job exceeds its configured timeout:\n\n1. SIGTERM is sent to the process\n2. Plugin waits 5 seconds for graceful shutdown\n3. If still running, SIGKILL is sent\n4. Timeout is logged and recorded in metrics\n\n### Graceful Shutdown\n\nWhen RoadRunner stops:\n\n1. No new job executions are scheduled\n2. SIGTERM is sent to all running processes\n3. Plugin waits for `grace_period` (default: 30s)\n4. Remaining processes are terminated with SIGKILL\n5. All log files are properly closed\n\n## Logging\n\nEnable debug logging to see detailed execution information:\n\n```yaml\nlogs:\n  level: debug\n```\n\nDebug messages include:\n\n- Job scheduling: \"Job scheduled: next execution at...\"\n- Execution start: \"Executing job 'name': command\"\n- Successful completion: \"Job 'name' completed successfully in 1.5s\"\n- Failures: \"Job 'name' failed with exit code 1\"\n- Skipped executions: \"Skipping job 'name' - previous execution still running\"\n- Timeouts: \"Job 'name' timed out after 5m\"\n\n## Best Practices\n\n1. **Use meaningful job names**: They appear in logs and metrics\n2. **Set appropriate timeouts**: Prevent runaway processes\n3. **Configure log rotation**: Especially for frequently-running jobs\n4. **Monitor metrics**: Set up alerts for failures and timeouts\n5. **Test schedules**: Use `@every` for testing before deploying complex cron expressions\n6. **Avoid overlap for heavy jobs**: Set `allow_overlap: false` for resource-intensive commands\n7. **Stagger schedules**: Don't schedule all jobs at the same time (e.g., avoid all jobs at midnight)\n8. **Handle SIGTERM in scripts**: Make long-running commands respond to termination signals\n\n## Limitations\n\n- Jobs are not persisted across RoadRunner restarts\n- No distributed coordination (for multi-instance deployments)\n- No built-in retry logic for failed jobs\n- All jobs use the same timezone (no per-job timezone support)\n- Command execution happens in shell context (security consideration)\n\n## License\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froadrunner-plugins%2Fcron","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Froadrunner-plugins%2Fcron","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Froadrunner-plugins%2Fcron/lists"}