A simple, reliable cron job scheduler with notifications and monitoring.
Schedule recurring tasks, get notified when they succeed or fail, and monitor everything from a web dashboard.
Simple alternative to complex schedulers - If you find traditional cron too basic but Kubernetes CronJobs too heavy, Jobster is the middle ground.
Built-in notifications - Get Slack/email alerts when jobs fail, without writing wrapper scripts.
Job history and logs - See when jobs ran, how long they took, and what went wrong.
Easy monitoring - Optional web dashboard.
Portable - Single binary, runs anywhere Linux runs. No containers required.
Small and stays small - About 7 MB of memory running headless, 8 MB with the dashboard, and it does not creep. Job output is streamed to the log rather than buffered, and run history is bounded, so a scheduler that has been up for months uses what it used on day one.
β
Database backups - Schedule nightly backups with Slack notifications
β
Health checks - Monitor APIs every 5 minutes, alert on failures
β
Report generation - Generate daily/weekly reports automatically
β
Data synchronization - Sync data between systems on schedule
β
Cleanup tasks - Delete old files, clear caches, rotate logs
β
DevOps automation - Deployment checks, certificate renewals
Download pre-built binary (Recommended):
# Download latest release
curl -LO https://github.com/caevv/jobster/releases/latest/download/jobster-linux-amd64
chmod +x jobster-linux-amd64
sudo mv jobster-linux-amd64 /usr/local/bin/jobster
# Verify
jobster --versionOr build from source (see CONTRIBUTING.md for details):
git clone https://github.com/caevv/jobster
cd jobster
go build -o jobster ./cmd/jobsterCreate a simple backup job:
# Add job using CLI
jobster job add nightly-backup \
--schedule "@daily" \
--command "/usr/local/bin/backup.sh"
# List configured jobs
jobster job list
# Run the scheduler (stays running)
jobster run --config jobster.yamlThat's it! Your job will run every day at midnight.
For production use, deploy Jobster as a systemd service:
# 1. Download and extract installer
curl -LO https://github.com/caevv/jobster/releases/latest/download/install.sh
chmod +x install.sh
# 2. Install as system service
sudo ./install.sh
# 3. Add your jobs
sudo -u jobster jobster job add backup \
--schedule "@daily" \
--command "/usr/local/bin/backup.sh" \
--config /etc/jobster/jobster.yaml
# 4. Start and enable
sudo systemctl enable jobster
sudo systemctl start jobster
# 5. Check status
sudo systemctl status jobsterYour jobs are now running as a system service with auto-restart on failures.
See Deployment for full production setup guide.
# Add backup job
jobster job add postgres-backup \
--schedule "0 2 * * *" \
--command "/opt/backup/pg_backup.sh" \
--timeout 1800Then configure notifications in jobster.yaml:
jobs:
- id: "postgres-backup"
schedule: "0 2 * * *"
command: "/opt/backup/pg_backup.sh"
timeout_sec: 1800
hooks:
on_success:
- agent: "send-slack.sh"
with:
channel: "#ops"
message: "β
Database backup completed"
on_error:
- agent: "send-slack.sh"
with:
channel: "#alerts"
message: "π¨ Database backup FAILED"jobster job add api-health-check \
--schedule "*/5 * * * *" \
--command "curl -f https://api.example.com/health" \
--timeout 30jobster job add weekly-report \
--schedule "@weekly" \
--command "/usr/local/bin/generate_report.py" \
--env "REPORT_TYPE=weekly" \
--env "OUTPUT_DIR=/var/reports"jobster job add cleanup-old-logs \
--schedule "0 3 * * *" \
--command "find /var/log -name '*.log' -mtime +30 -delete" \
--timeout 600Jobster supports flexible schedule formats:
| Format | Example | Description |
|---|---|---|
| Cron | 0 2 * * * |
Daily at 2:00 AM |
| Cron | */15 * * * * |
Every 15 minutes |
| Shortcuts | @hourly |
Every hour at :00 |
| Shortcuts | @daily |
Daily at midnight |
| Shortcuts | @weekly |
Weekly (Sunday midnight) |
| Shortcuts | @monthly |
Monthly (1st midnight) |
| Intervals | @every 5m |
Every 5 minutes |
| Intervals | @every 2h |
Every 2 hours |
| Intervals | @every 30s |
Every 30 seconds |
Create jobster.yaml for advanced configuration:
# Optional defaults (system timezone used if omitted)
defaults:
timezone: "America/New_York" # Job schedule timezone
agent_timeout_sec: 10 # Timeout for notification scripts
job_retries: 3 # Retry failed jobs
job_backoff_strategy: "exponential"
# Logging configuration (optional)
logging:
level: "info" # debug, info, warn, error
format: "json" # json or text
output: "/var/log/jobster.log" # file path, "stderr", "stdout", or "discard"
# Where to store job history
store:
driver: "bbolt" # "bbolt" (recommended) or "json"
path: "./.jobster.db"
retention_runs: 500 # runs kept per job; 0 keeps every run
retention_days: 30 # drop runs older than this; 0 keeps any age
# Your jobs
jobs:
- id: "backup"
schedule: "@daily"
command: "/usr/local/bin/backup.sh"
workdir: "/opt/backup" # Run command in this directory
timeout_sec: 3600 # Kill job after 1 hour
env: # Environment variables
BACKUP_TARGET: "production"
AWS_REGION: "us-east-1"See examples/ for more configuration examples.
# Add a job
jobster job add <job-id> --schedule "<cron>" --command "<command>"
# List all jobs
jobster job list [--config jobster.yaml]
# Remove a job
jobster job remove <job-id> [--config jobster.yaml]
# Interactive mode (prompts for details)
jobster job add --interactiveFull options for adding jobs:
jobster job add my-job \
--schedule "@daily" \ # When to run (required)
--command "/path/to/script" \ # What to run (required)
--config jobster.yaml \ # Config file (default: jobster.yaml)
--workdir /some/directory \ # Working directory
--timeout 600 \ # Timeout in seconds
--env KEY=VALUE \ # Environment variables (repeatable)
--env ANOTHER=VALUE# Run in foreground (no dashboard)
jobster run --config jobster.yaml
# Run with terminal UI dashboard (interactive)
jobster tui --config jobster.yaml
# Run with web dashboard
jobster serve --config jobster.yaml --addr :8080
# Validate configuration
jobster validate --config jobster.yamlRun Jobster with a beautiful, interactive terminal dashboard:
jobster tui --config jobster.yamlFeatures:
- π¨ Beautiful interface - Modern, colorful terminal UI
- β‘ Real-time updates - Live job status and run history
- π― Interactive navigation - Keyboard controls (β/β, j/k)
- π Stats at a glance - Success rates, running jobs, recent runs
Keyboard shortcuts:
β/βorj/k- Navigate job listenter- View job details (history, logs, stats)esc- Go back to job listg- Jump to topG- Jump to bottomr- Refresh dataq- Quit
When running with jobster serve, access the dashboard at http://localhost:8080:
- Job Status - See all jobs and their schedules
- Recent Runs - View execution history
- Logs - Check stdout/stderr from jobs
- Stats - Success rates, run times
API endpoints:
GET /- Dashboard UIGET /api/jobs- List jobs (JSON)GET /api/runs- Recent runs (JSON)GET /api/health- Health check
Deploy Jobster as a system service with proper isolation:
# 1. Download binary
curl -LO https://github.com/caevv/jobster/releases/latest/download/jobster-linux-amd64
chmod +x jobster-linux-amd64
sudo mv jobster-linux-amd64 /usr/local/bin/jobster
# 2. Download systemd files
curl -LO https://github.com/caevv/jobster/releases/latest/download/install.sh
chmod +x install.sh
# 3. Install service
sudo ./install.sh
# 4. Configure jobs
sudo nano /etc/jobster/jobster.yaml
# or use: sudo -u jobster jobster job add ...
# 5. Start service
sudo systemctl enable jobster
sudo systemctl start jobster
# 6. Monitor
sudo systemctl status jobster
sudo journalctl -u jobster -fService directories:
/usr/local/bin/jobster- Binary/etc/jobster/jobster.yaml- Configuration/etc/jobster/agents/- Notification scripts/var/lib/jobster/- Job history database/var/log/jobster/- Log files
Managing the service:
# Start/stop/restart
sudo systemctl start jobster
sudo systemctl stop jobster
sudo systemctl restart jobster
# View logs
sudo journalctl -u jobster -f
sudo journalctl -u jobster --since "1 hour ago"
# Add/list/remove jobs
sudo -u jobster jobster job list --config /etc/jobster/jobster.yaml
sudo -u jobster jobster job add <id> --schedule "<cron>" --command "<cmd>"
sudo -u jobster jobster job remove <id>Optional: Enable web dashboard
By default, Jobster runs in headless mode. To enable the dashboard:
# Stop headless service
sudo systemctl stop jobster
sudo systemctl disable jobster
# Enable dashboard service
sudo systemctl enable jobster-dashboard
sudo systemctl start jobster-dashboard
# Access at http://server-ip:8080See systemd/README.md for detailed deployment documentation.
Run the whole stack with Compose:
# 1. Write your jobs (container paths: store in /data, logging to stdout)
cp examples/docker.yaml my-jobs.yaml
# 2. Pull the released image and start the scheduler and dashboard
JOBSTER_CONFIG=./my-jobs.yaml docker compose up -d
# 3. Check it
curl -fsS http://localhost:8080/api/health
docker compose logs -fReleased images are published to ghcr.io/caevv/jobster for linux/amd64 and
linux/arm64. Compose tracks latest; pin a release with
JOBSTER_TAG=1.2.3 docker compose up -d, or pass --build to build the working
tree instead.
The image is a static binary on Alpine (~48 MB) running as a non-root user, with
bash, curl, and jq available for agents. Run history and agent state live
in the jobster-data volume, so a redeploy keeps them.
It idles at about 7 MB of memory, and stays there: job output is streamed to
the history log rather than buffered, and run history is bounded by
store.retention_runs. See Memory for the measurements.
Container paths:
/etc/jobster/jobster.yaml- Configuration (mount your own over it)/etc/jobster/agents/- Your agents/data- Run history, agent state (mount a volume)
Deploy the Compose stack straight from the repository:
- Create Service -> Compose, provider Git, compose path
./docker-compose.yml(the compose file names a published image, so the server downloads Jobster instead of compiling it) - Advanced -> Volumes / Mounts -> File Mount named
jobster.yaml, holding your jobs - Environment:
JOBSTER_CONFIG=../files/jobster.yaml, plusTZand any agent secrets - Deploy, then add a domain in the Domains tab pointing at service
jobster, port8080
The dashboard has no authentication - put it behind an authenticating proxy before exposing it publicly.
See DOCKER.md for the full reference: image layout, custom agents, persistence, multi-arch builds, and troubleshooting.
Measured in a container, one job scheduled, no traffic:
| Memory | |
|---|---|
jobster run (headless) |
~7 MB |
jobster serve (dashboard) |
~8 MB |
Under deliberately hostile load - a job every second, each printing 200 KB, with history capped at 20 runs - it holds flat across 181 runs: 13.5 MB at the first minute and 13.5 MB at the third, with the log directory, the database and the kept run count all steady.
Two things keep it there. Job output is streamed to its history log file with
only the last 10,000 characters kept in memory, so a job printing 20 MB costs
the same as one printing nothing. Run history is bounded by
store.retention_runs (500 per job by default), so memory, disk and dashboard
query time stop growing with uptime.
Under 1 MB of that footprint is Jobster itself; the rest is the Go runtime floor. See Memory for the full measurements.
Send notifications when jobs succeed or fail using "agents" - simple scripts that run at specific points:
pre_run- Before job startspost_run- After job finishes (always runs)on_success- Only when job succeedson_error- Only when job fails
1. Create agent script (/etc/jobster/agents/send-slack.sh):
#!/usr/bin/env bash
set -euo pipefail
# Read configuration
config="${CONFIG_JSON:-}"
[ -n "$config" ] || config='{}'
channel="$(jq -r '.channel // "#ops"' <<<"$config")"
message="$(jq -r '.message' <<<"$config")"
# Send to Slack
curl -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-Type: application/json' \
-d "{\"channel\":\"$channel\",\"text\":\"$message\"}"chmod +x /etc/jobster/agents/send-slack.sh2. Configure job to use agent:
jobs:
- id: "backup"
schedule: "@daily"
command: "/usr/local/bin/backup.sh"
hooks:
on_success:
- agent: "send-slack.sh"
with:
channel: "#ops"
message: "β
Backup completed successfully"
on_error:
- agent: "send-slack.sh"
with:
channel: "#alerts"
message: "π¨ Backup FAILED - check logs immediately"3. Set Slack webhook URL (in systemd service or environment):
export SLACK_WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"Create /etc/jobster/agents/send-email.sh:
#!/usr/bin/env bash
config="${CONFIG_JSON:-}"
[ -n "$config" ] || config='{}'
to="$(jq -r '.to' <<<"$config")"
subject="$(jq -r '.subject' <<<"$config")"
body="Job: $JOB_ID\nStatus: $([ $EXIT_CODE -eq 0 ] && echo 'Success' || echo 'Failed')\nRun ID: $RUN_ID"
echo -e "$body" | mail -s "$subject" "$to"hooks:
on_error:
- agent: "send-email.sh"
with:
to: "ops@example.com"
subject: "Job failure: backup"Agents can be written in any language (Bash, Python, Node.js, Go). They receive information via environment variables:
| Variable | Description |
|---|---|
JOB_ID |
Job identifier |
JOB_COMMAND |
Command that ran |
HOOK |
Hook type (pre_run, on_success, etc.) |
RUN_ID |
Unique run ID |
EXIT_CODE |
Job exit code |
START_TS |
Start timestamp |
END_TS |
End timestamp |
CONFIG_JSON |
Your agent configuration as JSON |
See agents/ for more examples.
Check service status:
sudo systemctl status jobster
sudo journalctl -u jobster --since "1 hour ago"Verify schedule syntax:
jobster validate --config /etc/jobster/jobster.yamlCheck timezone:
defaults:
timezone: "America/New_York" # Make sure this matches your expected timezoneCheck job timeout:
jobs:
- id: "long-running-job"
timeout_sec: 7200 # Increase if job takes longer than 10 minutes (default: 600)Check working directory:
jobs:
- id: "my-job"
workdir: "/path/to/workdir" # Command runs in this directoryTest command manually:
# Run as jobster user
sudo -u jobster /path/to/commandCheck agent is executable:
ls -l /etc/jobster/agents/send-slack.sh # Should show +x permission
chmod +x /etc/jobster/agents/send-slack.shCheck agent environment variables:
# Test agent manually
export CONFIG_JSON='{"channel":"#test","message":"test"}'
export JOB_ID="test-job"
export SLACK_WEBHOOK_URL="https://..."
/etc/jobster/agents/send-slack.shEnable agent allow-list (optional security):
security:
allowed_agents:
- "send-slack.sh"
- "send-email.sh"Check file permissions:
# Jobster user needs read access to scripts
sudo chown -R jobster:jobster /etc/jobster
sudo chmod -R 755 /etc/jobster/agentsCheck command permissions:
# Test as jobster user
sudo -u jobster /path/to/commandCheck for runaway jobs:
# View active jobs
ps aux | grep jobster
# Check logs for stuck jobs
sudo journalctl -u jobster | grep "execution completed"Add timeouts to all jobs:
jobs:
- id: "my-job"
timeout_sec: 600 # Kill after 10 minutes- π Documentation: examples/ and AGENTS.md
- π Bug Reports: GitHub Issues
- π‘ Feature Requests: GitHub Issues
- π₯ Contributing: See CONTRIBUTING.md
Apache License 2.0 - See LICENSE for details.
Built with β€οΈ using Go