What Is the Ansible cron Module?
The Ansible cron module (ansible.builtin.cron) manages cron jobs on remote hosts — create, modify, and delete scheduled tasks without manually editing crontab files. It's idempotent: running the same task twice doesn't create duplicate entries.
Parameters
| Parameter | Default | Description |
|---|---|---|
name | (required) | Description of the cron job (used as unique identifier) |
job | — | Command to execute |
state | present | present to create, absent to remove |
minute | * | Minute (0-59) |
hour | * | Hour (0-23) |
day | * | Day of month (1-31) |
month | * | Month (1-12) |
weekday | * | Day of week (0-6, 0=Sunday) |
special_time | — | reboot, hourly, daily, weekly, monthly, yearly |
user | current user | User whose crontab to modify |
cron_file | — | File in /etc/cron.d/ instead of user crontab |
env | false | Manage environment variable instead of job |
disabled | false | Comment out the job (keep but don't run) |
backup | false | Backup crontab before modifying |
Create a Cron Job
---
- name: Manage cron jobs
hosts: all
become: true
tasks:
# Run every day at 2:30 AM
- name: Database backup
ansible.builtin.cron:
name: "Database backup"
minute: "30"
hour: "2"
job: "/opt/scripts/backup-db.sh >> /var/log/backup.log 2>&1"
# Run every 5 minutes
- name: Health check
ansible.builtin.cron:
name: "App health check"
minute: "*/5"
job: "/opt/scripts/healthcheck.sh"
# Run every Monday at 6 AM
- name: Weekly report
ansible.builtin.cron:
name: "Weekly report"
minute: "0"
hour: "6"
weekday: "1"
job: "/opt/scripts/weekly-report.sh"
# Run on the 1st of every month
- name: Monthly cleanup
ansible.builtin.cron:
name: "Monthly cleanup"
minute: "0"
hour: "3"
day: "1"
job: "/opt/scripts/monthly-cleanup.sh"
Special Time Strings
# Run at reboot
- ansible.builtin.cron:
name: "Start app on boot"
special_time: reboot
job: "/opt/myapp/start.sh"
# Run hourly
- ansible.builtin.cron:
name: "Sync files"
special_time: hourly
job: "/opt/scripts/sync.sh"
# Run daily (midnight)
- ansible.builtin.cron:
name: "Log rotation"
special_time: daily
job: "/usr/sbin/logrotate /etc/logrotate.conf"
# Run weekly (Sunday midnight)
- ansible.builtin.cron:
name: "Full backup"
special_time: weekly
job: "/opt/scripts/full-backup.sh"
Cron Environment Variables
# Set PATH for cron jobs
- ansible.builtin.cron:
name: PATH
env: true
job: "/usr/local/bin:/usr/bin:/bin"
# Set MAILTO
- ansible.builtin.cron:
name: MAILTO
env: true
job: "admin@example.com"
# Set SHELL
- ansible.builtin.cron:
name: SHELL
env: true
job: /bin/bash
# Then add the actual cron job
- ansible.builtin.cron:
name: "My scheduled task"
minute: "0"
hour: "*/4"
job: "/opt/scripts/task.sh"
System Cron Files (/etc/cron.d/)
# Create a system-wide cron file (requires user field)
- ansible.builtin.cron:
name: "App monitoring"
cron_file: myapp-monitoring
user: appuser
minute: "*/5"
job: "/opt/myapp/monitor.sh"
# Creates /etc/cron.d/myapp-monitoring
# Remove a system cron file
- ansible.builtin.cron:
name: "App monitoring"
cron_file: myapp-monitoring
user: appuser
state: absent
Remove a Cron Job
# Remove by name
- ansible.builtin.cron:
name: "Old backup job"
state: absent
# Remove a system cron file
- ansible.builtin.cron:
name: "Old monitoring"
cron_file: old-monitoring
state: absent
Disable Without Removing
# Disable (comments out the entry)
- ansible.builtin.cron:
name: "Database backup"
minute: "30"
hour: "2"
job: "/opt/scripts/backup-db.sh"
disabled: true
# Re-enable
- ansible.builtin.cron:
name: "Database backup"
minute: "30"
hour: "2"
job: "/opt/scripts/backup-db.sh"
disabled: false
Practical Patterns
Application Server Cron Jobs
---
- name: Configure application cron jobs
hosts: appservers
become: true
tasks:
- name: Set cron environment
ansible.builtin.cron:
name: "{{ item.name }}"
env: true
job: "{{ item.value }}"
user: appuser
loop:
- { name: PATH, value: "/opt/myapp/bin:/usr/local/bin:/usr/bin:/bin" }
- { name: APP_ENV, value: "production" }
- name: Cache cleanup every 15 minutes
ansible.builtin.cron:
name: "Cache cleanup"
minute: "*/15"
job: "/opt/myapp/bin/clean-cache"
user: appuser
- name: Session cleanup daily at 3 AM
ansible.builtin.cron:
name: "Session cleanup"
minute: "0"
hour: "3"
job: "/opt/myapp/bin/clean-sessions --older-than 24h"
user: appuser
- name: Send metrics every minute
ansible.builtin.cron:
name: "Send metrics"
job: "/opt/myapp/bin/send-metrics >> /dev/null 2>&1"
user: appuser
Database Maintenance
---
- name: Database cron jobs
hosts: dbservers
become: true
tasks:
- name: Incremental backup every 6 hours
ansible.builtin.cron:
name: "DB incremental backup"
minute: "0"
hour: "*/6"
job: "pg_dump mydb | gzip > /backup/mydb-$(date +\\%Y\\%m\\%d-\\%H\\%M).sql.gz"
user: postgres
- name: Full backup Sunday 1 AM
ansible.builtin.cron:
name: "DB full backup"
minute: "0"
hour: "1"
weekday: "0"
job: "/opt/scripts/full-db-backup.sh"
user: postgres
- name: Vacuum daily
ansible.builtin.cron:
name: "DB vacuum"
special_time: daily
job: "psql -c 'VACUUM ANALYZE;' mydb"
user: postgres
- name: Remove old backups
ansible.builtin.cron:
name: "Purge old backups"
minute: "0"
hour: "4"
job: "find /backup -name '*.sql.gz' -mtime +30 -delete"
user: root
Variable-Driven Cron Jobs
---
- name: Deploy cron jobs from variable
hosts: all
become: true
vars:
cron_jobs:
- name: "Log cleanup"
minute: "0"
hour: "2"
job: "find /var/log/myapp -name '*.log' -mtime +7 -delete"
- name: "Certificate check"
minute: "0"
hour: "8"
weekday: "1"
job: "/opt/scripts/check-certs.sh"
- name: "Status report"
special_time: hourly
job: "/opt/scripts/status.sh"
tasks:
- name: Create all cron jobs
ansible.builtin.cron:
name: "{{ item.name }}"
minute: "{{ item.minute | default(omit) }}"
hour: "{{ item.hour | default(omit) }}"
day: "{{ item.day | default(omit) }}"
weekday: "{{ item.weekday | default(omit) }}"
special_time: "{{ item.special_time | default(omit) }}"
job: "{{ item.job }}"
loop: "{{ cron_jobs }}"
Cron Time Expression Cheatsheet
| Schedule | minute | hour | day | month | weekday |
|---|---|---|---|---|---|
| Every minute | * | * | * | * | * |
| Every 5 min | */5 | * | * | * | * |
| Every hour | 0 | * | * | * | * |
| Daily midnight | 0 | 0 | * | * | * |
| Daily 2:30 AM | 30 | 2 | * | * | * |
| Weekdays 9 AM | 0 | 9 | * | * | 1-5 |
| Mon & Thu 6 PM | 0 | 18 | * | * | 1,4 |
| 1st of month | 0 | 0 | 1 | * | * |
| Every 15 min | */15 | * | * | * | * |
Troubleshooting
Cron Job Not Running
Check if the crontab was created:
crontab -l # User crontab
cat /etc/cron.d/myfile # System crontab
Common issues:
- Missing
PATH— set it via cron env variable - Output not redirected — add
>> /var/log/job.log 2>&1 - Wrong user — check
userparameter
Duplicate Entries
The name parameter is the unique key. If you change the name, a new entry is created (old one stays). Remove the old one explicitly:
- ansible.builtin.cron:
name: "Old job name"
state: absent
- ansible.builtin.cron:
name: "New job name"
job: "/opt/scripts/task.sh"
Related Articles
- Ansible systemd Timer
- Ansible at Module (One-Time Scheduling)
- Ansible Shell Module
- Ansible Best Practices
Conclusion
ansible.builtin.cron manages cron jobs idempotently — the name field is the unique identifier, so running the same task twice won't create duplicates. Use time fields (minute, hour, day, month, weekday) for precise scheduling, or special_time for common intervals. Set cron_file for system-wide cron in /etc/cron.d/. Always redirect output in cron jobs (>> /var/log/job.log 2>&1) and set the PATH environment variable to avoid "command not found" issues.