Minimal Dockerized app that discovers Node.js tasks from a mounted volume and runs them on cron schedules.
- Auto-discovery of task folders from a mounted directory.
- Each task folder must contain task.js and config.yaml.
- Cron-based scheduling using config.yaml.
- Manual Run now from the web interface.
- Edit task name and schedule in the UI, with write-back to config.yaml.
- Run history and per-run log viewing in UI.
- File-based persistence for run metadata and logs.
- GitHub Actions workflow for publishing Docker images to GHCR.
Each subfolder inside volumes/tasks represents one task.
Example:
volumes/tasks/example-task/
config.yaml
task.js
# volumes/tasks/example-task/config.yaml
name: Example task
schedule: "*/5 * * * *"
# Optional: re-run the task up to `retries` extra times after a failure,
# waiting `retryDelaySeconds` between attempts.
retries: 0
retryDelaySeconds: 0// volumes/tasks/example-task/task.js
console.log("Task started", new Date().toISOString());
console.log("Task finished");
process.exit(0);Use Node process exit code to report success/failure (0 is success).
- Install dependencies.
npm install-
Create your task folders under
volumes/tasks. -
Start the app.
npm start- Open the UI:
http://localhost:3000
This project bumps package.json patch version on every commit via a local Git pre-commit hook.
npm run setup-hooksPull the published image:
docker pull ghcr.io/loic294/node-task-runner:latestCreate a docker-compose.yml file:
services:
app:
image: ghcr.io/loic294/node-task-runner:latest
ports:
- "3000:3000"
environment:
PORT: "3000"
TASKS_DIR: /app/volumes/tasks
LOGS_DIR: /app/volumes/logs
DATA_DIR: /app/volumes/data
DISCOVERY_INTERVAL_MS: "30000"
RETENTION_DAYS: "90"
TASK_TIMEZONE: "America/Los_Angeles"
MAX_LOG_BYTES: "200000"
TASK_TIMEOUT_MS: "0"
volumes:
- ./volumes/tasks:/app/volumes/tasks
- ./volumes/logs:/app/volumes/logs
- ./volumes/data:/app/volumes/data
restart: unless-stoppedStart the stack:
docker compose up -dMounted paths:
- volumes/tasks -> /app/volumes/tasks (read-only)
- volumes/logs -> /app/volumes/logs
- volumes/data -> /app/volumes/data