Schedule jobs in plain English β and run them reliably even on sleeping servers.
Stop writing cron syntax. Stop losing jobs when your server sleeps.
npm i -g easycron-clieasycron external https://your-app.onrender.com/api/task "every 10 minutes"π Generates a GitHub Action π Runs every 10 minutes π Works even if your server is asleep
Free-tier platforms (Render, Railway, Fly.io):
- Put servers to sleep after inactivity
- Kill background cron jobs
- Break scheduled tasks
easycron CLI fixes this using external triggers (GitHub Actions, UptimeRobot)
easycron CLI β generates trigger β GitHub runs it β server wakes β task runs
- Plain English scheduling (
every 10 minutes) - Works on free-tier servers
- GitHub Actions integration
- Smart retry system (handles cold starts)
- Keep-awake mode
- Plugin system
easycron external https://your-app.onrender.com/api/task "every 10 minutes"β Schedule parsed: every 10 minutes
β Cron: */10 * * * *
β GitHub Action generated at:
.github/workflows/easycron-trigger.yml
Commit the YAML to your repo β GitHub triggers your server every 10 minutes β Done.
easycron "every 10 minutes" -- node app.jseasycron explain "every monday at 9am"β Schedule parsed: every monday at 9am
β Cron: 0 9 * * 1
| Input | Cron Output |
|---|---|
every 10 minutes |
*/10 * * * * |
every 2 hours |
0 */2 * * * |
daily at 08:30 |
30 8 * * * |
daily at 2pm |
0 14 * * * |
daily at 2:30pm |
30 14 * * * |
every monday at 09:00 |
0 9 * * 1 |
every fri at 5pm |
0 17 * * 5 |
every weekday at 14:00 |
0 14 * * 1-5 |
every weekend at 10am |
0 10 * * 0,6 |
hourly |
0 * * * * |
midnight |
0 0 * * * |
| Command | Description |
|---|---|
easycron external <url> "<schedule>" |
Generate external trigger configs |
easycron "<schedule>" -- <command> |
Schedule a local cron job |
easycron list |
Show all registered jobs |
easycron remove <id> |
Remove a job by ID |
easycron explain "<schedule>" |
Preview cron translation |
easycron keep-awake <url> |
Prevent free-tier server sleep |
easycron scaffold |
Generate Fast Endpoint boilerplate |
easycron logs <id> |
View execution history for a job |
easycron plugins |
List installed community plugins |
# GitHub Actions (default)
easycron external https://api.my-app.com/task "every 10 minutes"
# UptimeRobot
easycron external https://api.my-app.com/task "every 10 minutes" --provider uptimerobot
# cron-job.org
easycron external https://api.my-app.com/task "daily at 2am" --provider cronjob
# All 3 at once (redundancy mode)
easycron external https://api.my-app.com/task "every 10 minutes" --redundanteasycron external https://api.my-app.com/task "every 10 minutes" --auth "Bearer MY_TOKEN"easycron external https://api.my-app.com/webhook "hourly" --method POST --body '{"action":"sync"}'# Server takes >30s to cold-start? Use 6 retries with 15s delay
easycron external https://api.my-app.com/task "every 10 minutes" --retries 6 --delay 15The generated GitHub Action includes:
- Configurable retry attempts (default: 3) with configurable backoff (default: 10s)
- 30s timeout β tolerates slow cold-starts on free-tier servers
- Follows redirects (
-L) β avoids false failures on 3xx responses - 2xx & 3xx = success β accepts all normal HTTP responses
- 5xx = retries β handles transient 502/503 during server boot
- 4xx = fails immediately β wrong URL/auth won't waste retries
Prevent free-tier servers from sleeping with automatic 14-minute pings:
easycron keep-awake https://your-app.onrender.comThis generates:
- A
/healthendpoint code snippet (Express/Fastify) - A GitHub Action that pings
/healthevery 14 min - An UptimeRobot config as optional backup
# Fastify users
easycron keep-awake https://your-app.onrender.com --framework fastify| Mode | Time Zone |
|---|---|
Local (easycron "..." -- cmd) |
System local timezone |
External (easycron external) |
UTC (GitHub Actions) |
β οΈ When using external triggers, adjust your schedule for UTC.
Your target endpoint must:
- Respond with
200 OKimmediately - Run the actual task asynchronously in the background
// Express.js example
app.get('/api/task', (req, res) => {
res.status(200).send('OK'); // Respond instantly
runHeavyTaskAsync(); // Execute in background
});| Scenario | Use |
|---|---|
| Server always ON | easycron "schedule" -- command |
| Server sleeps (free tier) | easycron external <url> "schedule" |
| Mission-critical task | easycron external <url> "schedule" --redundant |
| Prevent server sleep entirely | easycron keep-awake <url> |
| Slow cold-start (>30s) | easycron external <url> "schedule" --retries 6 --delay 15 |
- Dependent on external services (GitHub Actions, UptimeRobot, cron-job.org)
- Not real-time guaranteed (GitHub can delay schedules during peak hours)
- Subject to network failures outside CLI control
- Not a job queue (use BullMQ/Celery for that)
- Not a replacement for Airflow/enterprise schedulers
Don't know how to set up the endpoint for external triggers? Let easycron generate the boilerplate:
# Express (default)
easycron scaffold
# Fastify
easycron scaffold --framework fastify
# Custom filename
easycron scaffold --filename my-server.jsThe generated file includes:
- Immediate 200 OK response pattern (the API contract)
- Async background execution (non-blocking)
- In-memory idempotency lock (60s cooldown prevents duplicate runs)
/healthendpoint (ready for keep-awake pings)- POST variant with JSON body support
- Optional API key middleware (uncomment to enable)
Extend the parser with community patterns. Drop .js files in ~/.easycron/plugins/:
// ~/.easycron/plugins/custom-patterns.js
module.exports = {
name: 'custom-patterns',
patterns: [
{
match: /^twice daily$/i,
handler: () => ({
cron: '0 0,12 * * *',
fields: { minute: '0', hour: '0,12', dayOfMonth: '*', month: '*', dayOfWeek: '*' },
intervalMinutes: null,
}),
description: 'twice daily β 0 0,12 * * *',
},
],
};# Verify plugins loaded
easycron plugins
# Now this works
easycron explain "twice daily"Security: All community regex patterns are scanned for ReDoS vulnerabilities before loading. Patterns with nested quantifiers, excessive length, or too many quantifiers are automatically rejected.
# View last 20 execution entries for a job
easycron logs <job-id>
# View last 5 entries
easycron logs <job-id> -n 5const {
parseSchedule,
generateGitHubAction,
writeScaffold,
addPlugin,
analyzeRegexSafety,
} = require('easycron-cli');
const result = parseSchedule('every 10 minutes');
console.log(result.cron); // "*/10 * * * *"
console.log(result.fields); // { minute: '*/10', hour: '*', ... }
// Generate trigger programmatically
generateGitHubAction({
url: 'https://my-app.com/api/task',
cron: result.cron,
auth: null,
outputDir: '.',
retries: 3,
delay: 10,
});
// Generate scaffold programmatically
writeScaffold({ framework: 'express', outputDir: './' });
// Register a plugin programmatically
addPlugin({
name: 'my-plugin',
patterns: [{
match: /^every quarter hour$/,
handler: () => ({ cron: '*/15 * * * *', fields: {} }),
description: 'every quarter hour',
}],
});
// Check if a regex is ReDoS-safe
const safety = analyzeRegexSafety(/(a+)+/);
console.log(safety); // { safe: false, reason: 'Nested quantifiers detected' }Since easycron CLI leverages GitHub Actions and free-tier infrastructure to bypass hosting costs, there are a few strict platform limitations you should be aware of:
-
The 60-Day GitHub Sleep GitHub automatically halts scheduled cron workflows in repositories that haven't had any commit activity for 60 days. Fix: Ensure you push a commit (even a minor text edit) to your repository at least once every 2 months to keep your background triggers actively running.
-
Free-Tier Execution Limits (2,000 Minutes) Free GitHub accounts include 2,000 Action Minutes per month. A typical
easycronHTTP ping takes ~5-10 seconds. Running a singlekeep-awakeheartbeat (every 14 mins) uses roughly ~100 minutes a month, which is perfectly safe. However, scheduling dozens of rapid tasks (e.g. 15 apps pinging every 5 minutes) will quickly exhaust your monthly quota. -
Target Server Death If your hosting provider (Render, Railway, etc.) permanently disables your server due to bandwidth overuse or ToS violations,
easycron's requests will result in4xxHTTP errors and the workflow will fail immediately. For5xxerrors, the system will retry up to 3 times before giving up.
MIT
