{"id":48943656,"url":"https://github.com/paperkite-hq/cronbase","last_synced_at":"2026-04-17T15:07:56.446Z","repository":{"id":345395584,"uuid":"1185739861","full_name":"paperkite-hq/cronbase","owner":"paperkite-hq","description":"Beautiful self-hosted cron job manager with web UI. Replace crontab with a modern dashboard for defining, executing, and monitoring scheduled tasks.","archived":false,"fork":false,"pushed_at":"2026-04-03T12:03:17.000Z","size":2637,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-03T17:05:56.865Z","etag":null,"topics":["automation","bun","cron","dashboard","job-manager","monitoring","scheduler","self-hosted","supercronic-alternative","typescript"],"latest_commit_sha":null,"homepage":"https://paperkite-hq.github.io/cronbase","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"agpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/paperkite-hq.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","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":"2026-03-18T22:35:22.000Z","updated_at":"2026-04-03T12:03:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/paperkite-hq/cronbase","commit_stats":null,"previous_names":["paperkite-hq/cronbase"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/paperkite-hq/cronbase","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fcronbase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fcronbase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fcronbase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fcronbase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/paperkite-hq","download_url":"https://codeload.github.com/paperkite-hq/cronbase/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/paperkite-hq%2Fcronbase/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31933791,"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":["automation","bun","cron","dashboard","job-manager","monitoring","scheduler","self-hosted","supercronic-alternative","typescript"],"created_at":"2026-04-17T15:07:55.524Z","updated_at":"2026-04-17T15:07:56.438Z","avatar_url":"https://github.com/paperkite-hq.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/public/logo.png\" width=\"96\" height=\"96\" alt=\"cronbase logo\" /\u003e\n  \u003ch1 align=\"center\"\u003ecronbase\u003c/h1\u003e\n  \u003cp align=\"center\"\u003eOpen-source self-hosted cron job manager with web dashboard\u003c/p\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://paperkite-hq.github.io/cronbase/\"\u003eDocumentation\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#features\"\u003eFeatures\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#quick-start\"\u003eQuick Start\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#installation\"\u003eInstallation\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#configuration\"\u003eConfiguration\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#cli-reference\"\u003eCLI\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#api-reference\"\u003eAPI\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#alerting\"\u003eAlerting\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#monitoring\"\u003eMonitoring\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#docker\"\u003eDocker\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#security\"\u003eSecurity\u003c/a\u003e \u0026bull;\n  \u003ca href=\"#example-configurations\"\u003eExamples\u003c/a\u003e \u0026bull;\n  \u003ca href=\"CHANGELOG.md\"\u003eChangelog\u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/paperkite-hq/cronbase/releases/latest\"\u003e\u003cimg src=\"https://img.shields.io/github/v/release/paperkite-hq/cronbase\" alt=\"Latest Release\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/paperkite-hq/cronbase/actions/workflows/ci.yml\"\u003e\u003cimg src=\"https://github.com/paperkite-hq/cronbase/actions/workflows/ci.yml/badge.svg\" alt=\"CI\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://img.shields.io/endpoint?url=https://paperkite-hq.github.io/cronbase/coverage.json\" alt=\"Coverage\"\u003e\n  \u003ca href=\"https://github.com/paperkite-hq/cronbase/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/badge/license-AGPL--3.0-blue\" alt=\"License: AGPL-3.0\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://bun.sh\"\u003e\u003cimg src=\"https://img.shields.io/badge/runtime-Bun-f9f1e1?logo=bun\" alt=\"Powered by Bun\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\nReplace `crontab -e` with a modern web dashboard for defining, executing, and monitoring scheduled tasks. Think **\"Uptime Kuma for cron jobs\"** — but cronbase actually *runs* your jobs too.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg alt=\"cronbase demo\" src=\"docs/public/demo.svg\" width=\"750\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/public/screenshots/dashboard-dark.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/public/screenshots/dashboard-light.png\"\u003e\n    \u003cimg alt=\"cronbase dashboard\" src=\"docs/public/screenshots/dashboard-dark.png\" width=\"700\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cem\u003eAnimated terminal demo \u0026bull; Dark and light themes \u0026bull; Real-time job status \u0026bull; Execution history\u003c/em\u003e\n\u003c/p\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eMore screenshots\u003c/summary\u003e\n\n**Execution History** — filter by job, see status, duration, exit codes, and output:\n\n\u003cp align=\"center\"\u003e\n  \u003cpicture\u003e\n    \u003csource media=\"(prefers-color-scheme: dark)\" srcset=\"docs/public/screenshots/history-dark.png\"\u003e\n    \u003csource media=\"(prefers-color-scheme: light)\" srcset=\"docs/public/screenshots/history-light.png\"\u003e\n    \u003cimg alt=\"cronbase execution history\" src=\"docs/public/screenshots/history-dark.png\" width=\"700\"\u003e\n  \u003c/picture\u003e\n\u003c/p\u003e\n\n\u003c/details\u003e\n\n- **Zero dependencies** — single binary, SQLite storage, embedded web UI\n- **Built on Bun** — fast startup, low memory, TypeScript-native\n- **Battle-tested cron parser** — 5-field expressions, presets (`@daily`, `@hourly`), month/day names\n- **Full observability** — execution history, stdout/stderr capture, duration tracking\n- **Webhook + email alerting** — Slack, Discord, any HTTP endpoint, or SMTP email — no external dependencies\n- **Prometheus metrics** — `/metrics` endpoint for Grafana, AlertManager, and any Prometheus-compatible stack\n\n## Features\n\n**Job Management**\n- Create, list, enable/disable, and remove jobs via CLI or REST API\n- Per-job timeouts with graceful SIGTERM → SIGKILL escalation\n- Automatic retry with exponential backoff\n- Working directory and environment variables per job\n- Tags and descriptions for organization\n\n**Web Dashboard**\n- Real-time job status with auto-refresh\n- Execution history with stdout/stderr viewer\n- Create, edit, and trigger jobs from the UI\n- Dark/light theme (persisted)\n- Statistics: success rate, total executions, enabled count\n\n**Alerting**\n- Webhook notifications on success, failure, or timeout\n- Auto-detects Slack and Discord URLs — sends rich formatted messages\n- SMTP email alerts — built-in SMTP client, no external dependencies\n- Per-job alert configuration (webhooks, email, or both)\n- Non-blocking async delivery with 10s timeout\n\n**Monitoring**\n- Prometheus-compatible `/metrics` endpoint — scrape with Prometheus, graph in Grafana\n- Job counts, execution counters, duration summaries, scheduler state, database size\n- Unauthenticated (safe for monitoring scrapers alongside `/health`)\n\n**Operations**\n- Global pause/resume for maintenance windows (with optional auto-resume timer)\n- YAML or JSON config files for declarative job definitions\n- Docker image with health check endpoint\n- Graceful shutdown on SIGINT/SIGTERM\n- SQLite with WAL mode for concurrent access\n- stdout/stderr capture capped at 1 MiB per execution\n\n## Quick Start\n\n**With Docker** (no prerequisites):\n\n```bash\ndocker run -d \\\n  --name cronbase \\\n  -p 7433:7433 \\\n  -v cronbase-data:/data \\\n  ghcr.io/paperkite-hq/cronbase start --demo\n```\n\nOpen **http://localhost:7433** — the dashboard is live with 3 sample jobs pre-loaded.\n\nAdd your first job:\n\n```bash\ndocker exec cronbase cronbase add \\\n  --name \"hello\" \\\n  --schedule \"*/5 * * * *\" \\\n  --command \"echo Hello from cronbase!\"\n```\n\nOr use Docker Compose for a persistent setup:\n\n```bash\ncurl -O https://raw.githubusercontent.com/paperkite-hq/cronbase/main/docker-compose.yml\ndocker compose up -d\n```\n\n**With Bun** (from source):\n\n```bash\ngit clone https://github.com/paperkite-hq/cronbase.git\ncd cronbase \u0026\u0026 bun install\n\ncronbase add --name \"hello\" --schedule \"*/5 * * * *\" --command \"echo Hello!\"\ncronbase start   # → http://localhost:7433\n```\n\n## Installation\n\n### Docker\n\n```bash\ndocker run -d \\\n  --name cronbase \\\n  -p 7433:7433 \\\n  -v cronbase-data:/data \\\n  ghcr.io/paperkite-hq/cronbase\n```\n\nOr with Docker Compose:\n\n```bash\ncurl -O https://raw.githubusercontent.com/paperkite-hq/cronbase/main/docker-compose.yml\ndocker compose up -d\n```\n\n### From source\n\n```bash\ngit clone https://github.com/paperkite-hq/cronbase.git\ncd cronbase\nbun install\n```\n\n## Configuration\n\n### Config file\n\nDefine jobs declaratively in YAML or JSON. Jobs are synced on startup — existing jobs (matched by name) are updated, new ones are created.\n\n```yaml\n# cronbase.yaml\njobs:\n  - name: backup-db\n    schedule: \"0 2 * * *\"\n    command: pg_dump mydb \u003e /backups/db-$(date +%Y%m%d).sql\n    timeout: 300\n    retry:\n      maxAttempts: 2\n      baseDelay: 60\n    description: Nightly database backup\n    on_failure: https://hooks.slack.com/services/T.../B.../xxx\n\n  - name: cleanup-logs\n    schedule: \"@daily\"\n    command: find /var/log -name '*.gz' -mtime +30 -delete\n    description: Remove old compressed logs\n\n  - name: health-check\n    schedule: \"*/5 * * * *\"\n    command: curl -sf https://myapp.com/health || exit 1\n    timeout: 30\n    retry:\n      maxAttempts: 1\n    on_failure: https://discord.com/api/webhooks/xxx/yyy\n\n  - name: sync-data\n    schedule: \"0 */6 * * *\"\n    command: rsync -az /data/ backup-server:/backups/data/\n    timeout: 3600\n    description: Sync data to backup server every 6 hours\n\n  - name: cert-check\n    schedule: \"0 9 * * 1\"\n    command: |\n      openssl s_client -connect myapp.com:443 -servername myapp.com \u003c/dev/null 2\u003e/dev/null | \\\n      openssl x509 -noout -dates\n    description: Weekly TLS certificate expiry check\n```\n\nValidate before starting:\n\n```bash\ncronbase validate --path cronbase.yaml   # check for errors without touching the DB\ncronbase start --config cronbase.yaml    # load on startup\n```\n\n### Environment variable\n\n| Variable | Default | Description |\n|---|---|---|\n| `CRONBASE_DB` | `./cronbase.db` | SQLite database path |\n| `CRONBASE_API_TOKEN` | *(none)* | Bearer token for API and dashboard authentication ([details](#security)) |\n| `CRONBASE_TIMEZONE` | *(UTC)* | IANA timezone for schedule interpretation (e.g. `America/New_York`, `Europe/London`). Cron fields are treated as wall-clock time in this timezone. |\n| `CRONBASE_SMTP_HOST` | *(none)* | SMTP server hostname (required to enable email alerts) |\n| `CRONBASE_SMTP_PORT` | `587` | SMTP server port |\n| `CRONBASE_SMTP_SECURE` | `false` | Set to `true` for TLS/SMTPS on connect (port 465) |\n| `CRONBASE_SMTP_FROM` | `cronbase@localhost` | Sender address for alert emails |\n| `CRONBASE_SMTP_USERNAME` | *(none)* | SMTP AUTH username (optional) |\n| `CRONBASE_SMTP_PASSWORD` | *(none)* | SMTP AUTH password (optional) |\n\n### Cron expressions\n\nStandard 5-field cron format plus presets:\n\n| Field | Range | Special |\n|---|---|---|\n| Minute | 0-59 | `*`, `,`, `-`, `/` |\n| Hour | 0-23 | `*`, `,`, `-`, `/` |\n| Day of month | 1-31 | `*`, `,`, `-`, `/` |\n| Month | 1-12 | `*`, `,`, `-`, `/`, names (jan-dec) |\n| Day of week | 0-7 | `*`, `,`, `-`, `/`, names (sun-sat). 0 and 7 both mean Sunday |\n\n**Presets**: `@yearly`, `@annually`, `@monthly`, `@weekly`, `@daily`, `@midnight`, `@hourly`\n\n**Examples**:\n- `*/15 * * * *` — every 15 minutes\n- `0 2 * * *` — daily at 2:00 AM\n- `0 9 * * mon-fri` — weekdays at 9:00 AM\n- `0 0 1 * *` — first of every month at midnight\n- `@hourly` — top of every hour\n\n## CLI Reference\n\n```\ncronbase start [--port 7433] [--db ./cronbase.db] [--config cronbase.yaml] [--demo]\n                                                    Start scheduler + web UI\n  --demo                 Pre-load 3 sample jobs on first launch (empty DB only)\n\ncronbase add --name \u003cname\u003e --schedule \u003ccron\u003e --command \u003ccmd\u003e [options]\n  --cwd \u003cdir\u003e            Working directory (default: .)\n  --timeout \u003cseconds\u003e    Kill job after N seconds\n  --retries \u003ccount\u003e      Max retry attempts on failure (default: 0)\n  --retry-delay \u003csecs\u003e   Base delay for exponential backoff (default: 30)\n  --description \u003ctext\u003e   Optional description\n  --disabled             Create job in disabled state\n\ncronbase list                                       List all jobs\ncronbase edit \u003cname\u003e [options]                      Update an existing job\ncronbase history [--job \u003cname\u003e] [--limit 20]        Show execution history\ncronbase logs \u003cname\u003e [--limit 1]                    Show output from recent executions\ncronbase run \u003cname\u003e                                 Manually trigger a job\ncronbase remove \u003cname\u003e                              Remove a job\ncronbase enable \u003cname\u003e                              Enable a disabled job\ncronbase disable \u003cname\u003e                             Disable a job\ncronbase stats                                      Show summary statistics\ncronbase pause [--until \u003cdatetime\u003e]                  Pause all scheduled execution\ncronbase resume                                     Resume scheduled execution\ncronbase validate [--path cronbase.yaml]            Validate config file (no DB changes)\ncronbase doctor [--config cronbase.yaml]            Check runtime environment and config\ncronbase import [--dry-run]                         Import jobs from system crontab\ncronbase export                                     Export jobs as YAML config\ncronbase prune [--days 90]                          Prune old execution history\n\nGlobal flags:\n  --json                 Output in JSON format (list, history, stats, run, export)\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eCLI output examples\u003c/summary\u003e\n\n**Adding a job:**\n```\n$ cronbase add --name \"backup-db\" --schedule \"0 2 * * *\" \\\n    --command \"pg_dump mydb \u003e /backups/db.sql\" --timeout 300 --retries 2\n✓ Job added: backup-db\n  Schedule: 0 2 * * * (At 02:00)\n  Command:  pg_dump mydb \u003e /backups/db.sql\n  Next run: 3/19/2026, 7:00:00 PM\n```\n\n**Listing jobs:**\n```\n$ cronbase list\nName                      Schedule             Status     Last Run               Next Run\n────────────────────────────────────────────────────────────────────────────────────────────────────\nbackup-db                 0 2 * * *            — never    —                      3/19/2026, 7:00:00 PM\ncleanup-logs              @daily               — never    —                      3/19/2026, 5:00:00 PM\nhealth-check              */5 * * * *          — never    —                      3/18/2026, 10:55:00 PM\n\n3 job(s)\n```\n\n**Inspecting a job:**\n```\n$ cronbase show backup-db\nJob: backup-db\nDescription: Nightly database backup\nEnabled: yes\n\nSchedule: 0 2 * * * (At 02:00)\nCommand: pg_dump mydb \u003e /backups/db.sql\nWorking dir: .\n\nNext run: 3/20/2026, 2:00:00 AM\nLast run: 3/19/2026, 2:00:05 AM\nLast status: ✗ failed\n\nTimeout: 300s\nRetries: 2 (delay: 60s)\n\nCreated: 3/15/2026, 9:30:00 AM\n```\n\n**Running a job manually:**\n```\n$ cronbase run health-check\nRunning: health-check (curl -sf https://myapp.com/health)\n✓ success (127ms, exit 0)\n```\n\n**Viewing execution history:**\n```\n$ cronbase history --limit 5\nJob                  Status     Duration   Exit   Attempt  Started\n──────────────────────────────────────────────────────────────────────────────────────────\nhealth-check         ✓ success  127ms      0      0        3/18/2026, 10:53:42 PM\n```\n\n**Viewing job output:**\n```\n$ cronbase logs backup-db\n✗ failed (5.2s, exit 1) at 3/19/2026, 2:00:05 AM\n\n--- stdout ---\npg_dump: dumping database \"mydb\"...\n\n--- stderr ---\npg_dump: error: connection to server on socket \"/var/run/postgresql/.s.PGSQL.5432\" failed: No such file or directory\n```\n\n**Statistics:**\n```\n$ cronbase stats\nJobs:      3 total, 3 enabled\nLast 24h:  1 successes, 0 failures\nSuccess:   100%\n```\n\n**Exporting jobs:**\n```\n$ cronbase export\njobs:\n  - name: backup-db\n    schedule: \"0 2 * * *\"\n    command: pg_dump mydb \u003e /backups/db.sql\n    timeout: 300\n    retry:\n      maxAttempts: 2\n      baseDelay: 60\n  - name: cleanup-logs\n    schedule: \"@daily\"\n    command: find /var/log -name '*.gz' -mtime +30 -delete\n  - name: health-check\n    schedule: \"*/5 * * * *\"\n    command: curl -sf https://myapp.com/health\n    timeout: 30\n```\n\n\u003c/details\u003e\n\n### Migrating from crontab\n\n```bash\n# Preview what would be imported\ncronbase import --dry-run\n\n# Import all crontab entries\ncronbase import\n```\n\ncronbase reads your system crontab (`crontab -l`), generates job names from commands, and adds them to the database. Existing jobs (by name) are skipped.\n\n### Backup \u0026 restore\n\nExport all jobs as a YAML config file, then reload on another machine:\n\n```bash\n# Export current jobs\ncronbase export \u003e cronbase.yaml\n\n# Restore on another machine\ncronbase start --config cronbase.yaml\n```\n\nThe exported YAML includes schedules, timeouts, retries, environment variables, tags, and alert webhook configuration — everything needed to fully reconstruct your setup.\n\n## API Reference\n\nThe web dashboard and REST API are served on the same port (default `7433`).\n\n### Health\n\n```\nGET /health\n```\n\nReturns scheduler status, job counts, and database size.\n\n### Metrics\n\n```\nGET /metrics\n```\n\nPrometheus exposition format. Returns job counts, execution counters, duration summaries, scheduler state, and database size. Unauthenticated — safe for Prometheus scrapers.\n\nExample output:\n\n```\n# HELP cronbase_info cronbase version information.\n# TYPE cronbase_info gauge\ncronbase_info{version=\"0.3.0\"} 1\n\n# HELP cronbase_jobs_total Number of configured jobs by status.\n# TYPE cronbase_jobs_total gauge\ncronbase_jobs_total{status=\"enabled\"} 12\ncronbase_jobs_total{status=\"disabled\"} 3\n\n# HELP cronbase_executions_total Total number of job executions by status.\n# TYPE cronbase_executions_total counter\ncronbase_executions_total{status=\"success\"} 4521\ncronbase_executions_total{status=\"failed\"} 23\ncronbase_executions_total{status=\"timeout\"} 2\ncronbase_executions_total{status=\"skipped\"} 0\n\n# HELP cronbase_scheduler_paused Whether the scheduler is paused (1 = paused, 0 = running).\n# TYPE cronbase_scheduler_paused gauge\ncronbase_scheduler_paused 0\n\n# HELP cronbase_db_size_bytes Size of the SQLite database file in bytes.\n# TYPE cronbase_db_size_bytes gauge\ncronbase_db_size_bytes 245760\n```\n\nAdd to your `prometheus.yml`:\n\n```yaml\nscrape_configs:\n  - job_name: cronbase\n    static_configs:\n      - targets: [\"localhost:7433\"]\n```\n\n### Jobs\n\n```\nGET    /api/jobs              List all jobs\nPOST   /api/jobs              Create a job\nGET    /api/jobs/:id          Get job details\nPUT    /api/jobs/:id          Update a job\nDELETE /api/jobs/:id          Delete a job\nPATCH  /api/jobs/:id/toggle   Enable/disable a job\nPOST   /api/jobs/:id/run      Trigger immediate execution\n```\n\n### Alerts\n\n```\nGET    /api/jobs/:id/alerts   Get alert configuration\nPUT    /api/jobs/:id/alerts   Set alert configuration\nDELETE /api/jobs/:id/alerts   Remove alert configuration\n```\n\n### Executions\n\n```\nGET /api/executions           List execution history (?jobId=N\u0026limit=20)\nGET /api/executions/:id       Get execution detail (with stdout/stderr)\n```\n\n### Scheduler\n\n```\nGET  /api/scheduler/status    Check if the scheduler is paused\nPOST /api/scheduler/pause     Pause all scheduled execution (body: {\"until\": \"ISO8601\"})\nPOST /api/scheduler/resume    Resume scheduled execution\n```\n\n### Utilities\n\n```\nGET /api/stats                Summary statistics (job counts, 24h success/failure)\nGET /api/cron/describe?expr=  Validate and describe a cron expression\n```\n\n## Alerting\n\ncronbase sends webhook notifications when jobs complete. It auto-detects the webhook platform from the URL and formats messages accordingly.\n\n### Slack\n\nUses Block Kit formatting with color-coded attachments:\n\n```yaml\non_failure: https://hooks.slack.com/services/T.../B.../xxx\n```\n\n### Discord\n\nUses rich embeds with color and fields:\n\n```yaml\non_failure: https://discord.com/api/webhooks/xxx/yyy\n```\n\n### Generic webhook\n\nAny other URL receives the raw JSON payload:\n\n```json\n{\n  \"event\": \"failed\",\n  \"job\": { \"id\": 1, \"name\": \"backup-db\", \"schedule\": \"0 2 * * *\", \"command\": \"...\" },\n  \"execution\": {\n    \"id\": 42,\n    \"status\": \"failed\",\n    \"exitCode\": 1,\n    \"durationMs\": 5230,\n    \"startedAt\": \"2025-01-15T02:00:00.000Z\",\n    \"finishedAt\": \"2025-01-15T02:00:05.230Z\",\n    \"stdoutTail\": \"...\",\n    \"stderrTail\": \"pg_dump: error: connection refused\",\n    \"attempt\": 2\n  },\n  \"timestamp\": \"2025-01-15T02:00:05.235Z\"\n}\n```\n\n### Email (SMTP)\n\ncronbase includes a built-in SMTP client — no external mail libraries needed. Set the SMTP environment variables and add email recipients to your config:\n\n```bash\nexport CRONBASE_SMTP_HOST=\"smtp.gmail.com\"\nexport CRONBASE_SMTP_PORT=465\nexport CRONBASE_SMTP_SECURE=true\nexport CRONBASE_SMTP_FROM=\"alerts@example.com\"\nexport CRONBASE_SMTP_USERNAME=\"alerts@example.com\"\nexport CRONBASE_SMTP_PASSWORD=\"app-password-here\"\n\ncronbase start --config cronbase.yaml\n```\n\n```yaml\njobs:\n  - name: backup-db\n    schedule: \"0 2 * * *\"\n    command: pg_dump mydb \u003e /backups/db.sql\n    on_failure_email: ops@example.com\n    on_complete_email: ops@example.com, oncall@example.com\n```\n\nEmails include the job name, schedule, duration, exit code, and the last 500 characters of stderr/stdout.\n\n### Config file alert shortcuts\n\nWebhooks:\n\n```yaml\njobs:\n  - name: my-job\n    schedule: \"@hourly\"\n    command: ./task.sh\n    on_failure: https://hooks.slack.com/...   # Alert on failure + timeout\n    on_success: https://hooks.slack.com/...   # Alert on success only\n    on_complete: https://hooks.slack.com/...  # Alert on every execution\n```\n\nEmail:\n\n```yaml\njobs:\n  - name: my-job\n    schedule: \"@hourly\"\n    command: ./task.sh\n    on_failure_email: ops@example.com          # Email on failure + timeout\n    on_success_email: ops@example.com          # Email on success only\n    on_complete_email: ops@example.com         # Email on every execution\n```\n\nYou can combine webhooks and email on the same job — both fire independently.\n\n## Monitoring\n\ncronbase exposes a Prometheus-compatible `/metrics` endpoint for integration with Grafana, AlertManager, and any Prometheus-compatible monitoring stack.\n\n```bash\ncurl http://localhost:7433/metrics\n```\n\nThe endpoint is **unauthenticated** (like `/health`) — safe for Prometheus scrapers even when `CRONBASE_API_TOKEN` is set.\n\n**Exposed metrics:**\n\n| Metric | Type | Description |\n|---|---|---|\n| `cronbase_info` | gauge | Always 1, carries `version` label |\n| `cronbase_jobs_total` | gauge | Job count by status (enabled/disabled) |\n| `cronbase_executions_total` | counter | Cumulative executions by status (success/failed/timeout/skipped) |\n| `cronbase_execution_duration_seconds` | summary | Duration count and sum for recent executions |\n| `cronbase_scheduler_paused` | gauge | 1 if paused, 0 if running |\n| `cronbase_db_size_bytes` | gauge | SQLite database file size |\n\n**Prometheus scrape config:**\n\n```yaml\nscrape_configs:\n  - job_name: cronbase\n    static_configs:\n      - targets: [\"localhost:7433\"]\n```\n\n## Docker\n\n### Pre-built image\n\n```bash\ndocker run -d --name cronbase -p 7433:7433 -v cronbase-data:/data ghcr.io/paperkite-hq/cronbase\n```\n\n### Build from source\n\n```bash\ndocker build -t cronbase .\ndocker run -d --name cronbase -p 7433:7433 -v cronbase-data:/data cronbase\n```\n\n### With config file\n\n```bash\ndocker run -d --name cronbase \\\n  -p 7433:7433 \\\n  -v cronbase-data:/data \\\n  -v ./cronbase.yaml:/app/cronbase.yaml \\\n  ghcr.io/paperkite-hq/cronbase start --db /data/cronbase.db --config /app/cronbase.yaml\n```\n\n### Docker Compose\n\n```yaml\nservices:\n  cronbase:\n    image: ghcr.io/paperkite-hq/cronbase\n    ports:\n      - \"7433:7433\"\n    volumes:\n      - cronbase-data:/data\n      - ./cronbase.yaml:/app/cronbase.yaml\n    command: [\"start\", \"--db\", \"/data/cronbase.db\", \"--config\", \"/app/cronbase.yaml\"]\n    restart: unless-stopped\n\nvolumes:\n  cronbase-data:\n```\n\n### Health check\n\nThe Docker image includes a built-in health check against `/health`. You can also use it externally:\n\n```bash\ncurl http://localhost:7433/health\n```\n\n## Security\n\ncronbase supports token-based authentication for both the API and web dashboard via the `CRONBASE_API_TOKEN` environment variable.\n\n### Setting up authentication\n\nSet the `CRONBASE_API_TOKEN` environment variable before starting cronbase:\n\n```bash\nexport CRONBASE_API_TOKEN=\"your-secret-token\"\ncronbase start\n```\n\nWith Docker:\n\n```bash\ndocker run -d --name cronbase \\\n  -p 7433:7433 \\\n  -e CRONBASE_API_TOKEN=\"your-secret-token\" \\\n  -v cronbase-data:/data \\\n  ghcr.io/paperkite-hq/cronbase\n```\n\nWhen a token is configured:\n\n- **API routes** (`/api/*`) require a `Bearer` token in the `Authorization` header:\n  ```bash\n  curl -H \"Authorization: Bearer your-secret-token\" http://localhost:7433/api/jobs\n  ```\n- **Dashboard** access requires a `?token=` query parameter:\n  ```\n  http://localhost:7433/?token=your-secret-token\n  ```\n- **`/health`** remains unauthenticated — safe for Docker health checks and external monitoring.\n- **CORS** — when authentication is enabled, the wildcard `Access-Control-Allow-Origin: *` header is omitted to prevent cross-origin exploitation. CORS preflight (`OPTIONS`) requests are always allowed.\n\n### Non-localhost warning\n\nIf you bind cronbase to a non-localhost address (e.g., `--host 0.0.0.0`) without setting `CRONBASE_API_TOKEN`, cronbase will log a warning:\n\n\u003e WARNING: No API token set and server is network-accessible. Set CRONBASE_API_TOKEN or bind to 127.0.0.1 to prevent unauthorized access.\n\n### Best practices\n\n- **Always set `CRONBASE_API_TOKEN`** when exposing cronbase beyond localhost.\n- **Use a reverse proxy** (nginx, Caddy, Traefik) with TLS termination in front of cronbase for production deployments.\n- **Generate strong tokens** — e.g., `openssl rand -hex 32`.\n- Token comparison uses constant-time algorithms to prevent timing attacks.\n\n## Comparison\n\n| Feature | cronbase | crontab | Supercronic | Ofelia | dkron | healthchecks.io |\n|---|---|---|---|---|---|---|\n| Web dashboard | Yes | No | No | No | Yes | Yes |\n| Job execution | Yes | Yes | Yes | Yes (Docker) | Yes | No (monitoring only) |\n| Execution history | Yes | No | No | Limited | Yes | No |\n| stdout/stderr capture | Yes | Via mail | stdout/stderr | Docker logs | Limited | No |\n| Retry with backoff | Yes | No | No | No | Yes | No |\n| Webhook + email alerts | Yes | No | No | Slack only | Yes | Yes |\n| Prometheus metrics | Yes | No | No | No | Yes | No |\n| Config file | YAML/JSON | crontab | crontab | Docker labels | JSON | N/A |\n| Dependencies | None (Bun) | None | None (Go binary) | Docker | etcd/Consul | SaaS |\n| Self-hosted | Yes | Yes | Yes | Yes | Yes | Optional |\n\n## Programmatic API\n\nUse cronbase as a library in your TypeScript/Bun projects:\n\n```typescript\nimport { Scheduler, Store } from \"cronbase\";\n\nconst scheduler = new Scheduler({ dbPath: \"./my-jobs.db\", port: 7433 });\n\nscheduler.getStore().addJob({\n  name: \"backup\",\n  schedule: \"@daily\",\n  command: \"pg_dump mydb \u003e /backups/$(date +%Y%m%d).sql\",\n  timeout: 300,\n  retry: { maxAttempts: 2, baseDelay: 60 },\n});\n\nscheduler.start();\n```\n\n## Example Configurations\n\nThe `examples/` directory contains ready-to-use YAML configurations for common use cases. Copy and adapt them for your needs:\n\n| File | Description |\n|---|---|\n| [`database-backup.yaml`](examples/database-backup.yaml) | PostgreSQL + MySQL backups with cleanup of old files |\n| [`health-checks.yaml`](examples/health-checks.yaml) | Monitor web apps, APIs, databases, Redis, and SSL expiry |\n| [`log-rotation.yaml`](examples/log-rotation.yaml) | Compress, archive, and rotate log files |\n| [`data-sync.yaml`](examples/data-sync.yaml) | Rsync-based data synchronization |\n| [`maintenance.yaml`](examples/maintenance.yaml) | Temp file cleanup, cache warming, disk usage alerts |\n\nUse `cronbase start --config examples/health-checks.yaml` to try one immediately.\n\nFor full documentation including guides, API reference, and more examples, see the **[cronbase docs](https://paperkite-hq.github.io/cronbase/)**.\n\n## Contributing\n\nSee [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and guidelines.\n\n## License\n\n[AGPL-3.0](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperkite-hq%2Fcronbase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpaperkite-hq%2Fcronbase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpaperkite-hq%2Fcronbase/lists"}