Skip to content

Repository files navigation

Redmine Periodic Task logo

Redmine periodictask Test Redmine plugins directory License: MIT

Recurring issues for Redmine. Some work comes back on a schedule: renew the SSL certificate once a year, run the security checks every 3 months, write the weekly report every Monday, rotate the on-call duty. Redmine has no built-in recurring issues. This plugin adds them. You define a periodic task (an issue template plus a recurrence rule) and the plugin creates the issue when it is due.

  • Repeat every N days, business days, weeks (on chosen weekdays), months (day of month or 3rd Wednesday) or years. Optional end date or maximum number of runs.
  • Generated issues get everything a hand-made issue has: tracker, status, priority, assignee or assignee rotation, category, version, custom fields, watchers, attachments, subtasks and relations to other issues.
  • Date variables in subject and description (Weekly report **WEEKISO**/**YEAR**).
  • Choose what happens when the previous issue is still open: create anyway, skip, close it or wait.
  • Runs from cron, without cron (checked on web requests) or from any external scheduler through a check URL.
  • REST API, issue list filter and column for generated issues, activity entries, scheduler log.
  • Redmine 5.x to 7.0 (5.1 to 7.0 tested in CI), 15 languages, MIT license.

This blog post tells the history of the plugin and how it works inside. The plugin is also listed in the Redmine plugins directory, where you can rate it.

After installing, enable the Periodic Tasks module on a project, or make it a default module for new projects. The project gets a new "Periodic Tasks" tab. Add your tasks there.

Screenshots

The task list of a project. It shows the rule as a sentence, the next run date, the assignee (or who is next in the rotation) and the last run:

Periodic task list view

Creating or editing a task. The recurrence rule is on top. Below it is a template that looks like Redmine's own issue form: tracker, priority, assignee rotation, category, target version, dates, watchers, subtasks, related issues, custom fields:

Create / edit a periodic task

The Next occurrences preview under the rule. The next run dates appear as chips, and a calendar of the coming months marks every run day. It refreshes as you change the rule:

Next occurrences chips and calendar in the task form

The task page, with the schedule header, the template and the issues generated so far:

Periodic task detail

The plugin settings page: scheduler mode (cron, automatic on web requests, or an external check URL) and the scheduler log:

Scheduler settings and log

How it compares

There are two ways to get recurring issues in Redmine:

  • Template based (this plugin). The recurrence lives in a periodic task, a template stored per project. Issues are generated from it. None of them is "the" recurring issue, so there is nothing to keep open or to copy again, and you can change the template without touching past issues. The task page lists the generated issues, and a filter on the issue list finds them.
  • Issue based (issue_recurring and similar plugins). The recurrence is attached to an existing issue, which is copied or reopened on each occurrence.

Pick this plugin if you think of the work as "a checklist that comes back" rather than "an issue that never closes". Also pick it if you want subtasks, relations, an assignee rotation or a REST API, if you cannot set up cron on your Redmine host, or if you run a recent Redmine (5.1 to 7.0 are tested in CI on every change).

Redmine version support

Old Redmine versions are no longer supported on main. If you run an old version, use the branch from the table below. If you cannot upgrade and still need support, you can hire me. Contact me with the details.

git branch redmine version support
1.x 2.x 3.x 4.x 5.0 5.1 6.0 6.1 7.0
main ? ? ? ? ?
redmine4 ? ? 🚫 🚫 🚫 🚫 🚫
redmine2 🚫 🚫 🚫 🚫 🚫 🚫 🚫

To use the redmine2 branch, add -b redmine2 when cloning: git clone -b redmine2 https://github.com/jperelli/Redmine-Periodic-Task.git plugins/periodictask

Installation

Run these from your Redmine root. The paths below assume /opt/redmine, adjust them to yours:

cd /opt/redmine
git clone https://github.com/jperelli/Redmine-Periodic-Task.git plugins/periodictask
bundle install
bundle exec rake redmine:plugins:migrate NAME=periodictask RAILS_ENV=production

Then restart Redmine so it loads the plugin (see Restarting Redmine).

Debian / Ubuntu redmine package

The Debian redmine 6.x package (Redmine root /usr/share/redmine) loads plugins from /var/lib/redmine/<instance>/plugins/ (the instance is default unless you set REDMINE_INSTANCE), not from /usr/share/redmine/plugins/. A plugin cloned into /usr/share/redmine/plugins/ does not show up under Administration → Plugins, and its rake task fails with uninitialized constant ScheduledTasksChecker, because Redmine still picks up the rake tasks from there. Install into the instance directory instead:

cd /usr/share/redmine
git clone https://github.com/jperelli/Redmine-Periodic-Task.git /var/lib/redmine/default/plugins/periodictask
bundle install
bundle exec rake redmine:plugins:migrate NAME=periodictask RAILS_ENV=production

Or keep the clone in /usr/share/redmine/plugins/periodictask and symlink it: ln -s /usr/share/redmine/plugins/periodictask /var/lib/redmine/default/plugins/periodictask. Then restart Redmine. See /usr/share/doc/redmine/README.Debian for the details of the Debian layout.

Upgrade

cd /opt/redmine/plugins/periodictask
git pull
bundle install
bundle exec rake redmine:plugins:migrate NAME=periodictask RAILS_ENV=production

Then restart Redmine (see Restarting Redmine).

Uninstallation

cd /opt/redmine
bundle exec rake redmine:plugins:migrate NAME=periodictask VERSION=0 RAILS_ENV=production
rm -rf plugins/periodictask

Then restart Redmine (see Restarting Redmine).

Restarting Redmine

How you restart Redmine depends on how you serve it:

  • Puma / Unicorn under systemd: sudo systemctl restart redmine
  • Passenger (Apache or nginx): touch /opt/redmine/tmp/restart.txt
  • Docker: docker compose restart redmine

Configuration

Something has to check, now and then, which tasks are due and create their issues. Pick one of:

Mode Needs Timing Best for
Cron (default) shell access to the server, cron exact classic Linux installs
Automatic on web requests nothing on the first visit after a task is due Windows, Docker, shared hosting, anyone who can't or doesn't want to set up cron
Check URL an external scheduler that can call a URL as exact as the external scheduler exact runs without cron on the Redmine host

You can combine the modes. Running the checker more than once is harmless: a task only runs when its next run date has passed.

Option A: cron (default)

A rake task creates the issues. You run it from cron. Cron has a minimal PATH, so use the absolute path to bundle. Find it with which bundle. With rbenv it looks like /home/redmine/.rbenv/shims/bundle, with a system Ruby like /usr/local/bin/bundle.

Edit the crontab of the user that owns your Redmine install (crontab -e) and add one of the lines below. Replace /opt/redmine with your Redmine root and /usr/local/bin/bundle with the path from which bundle.

Once a day, at 01:00:

0 1 * * * cd /opt/redmine && /usr/local/bin/bundle exec rake redmine:check_periodictasks RAILS_ENV=production

Once per hour:

0 * * * * cd /opt/redmine && /usr/local/bin/bundle exec rake redmine:check_periodictasks RAILS_ENV=production

Every 10 minutes:

*/10 * * * * cd /opt/redmine && /usr/local/bin/bundle exec rake redmine:check_periodictasks RAILS_ENV=production

Option B: automatic on web requests (no cron)

Go to Administration → Plugins → Redmine Periodictask plugin → Configure and set Scheduler to Automatic on web requests. From then on, every request to Redmine (any page, any user, the API too) checks whether the Check interval (default 5 minutes) has passed since the last check. If it has, the checker runs in a background thread of the web process, so the request itself is not slowed down. A row in periodictask_scheduler_locks makes sure only one process runs the check per interval, even with several Puma/Passenger workers or several application servers.

Things to know:

  • Nothing happens while nobody uses Redmine. An issue due on Saturday is created on the first visit on Monday morning. Its start and due dates are still computed from the scheduled date. doc/recurrence-design.md explains how late runs are handled.
  • Issues are created in Redmine's default language. The LOCALE variable described below only applies to the rake task.
  • You can still run the rake task by hand or from cron at the same time.

Option C: check URL (external scheduler)

The plugin exposes GET|POST /periodictask/check?key=<API key>. It runs the checker right away and answers Periodictask: N task(s) run. It is protected like Redmine's own /sys endpoints: enable Administration → Settings → Repositories → Enable WS for repository management and use the API key shown there. The plugin configuration page shows the full URL.

Call it from any scheduler you have, for example:

  • an uptime monitor (UptimeRobot, healthchecks.io, ...) pinging the URL every 5 minutes
  • a GitHub Actions / GitLab CI scheduled workflow running curl -fsS "https://redmine.example.com/periodictask/check?key=..."
  • Windows Task Scheduler running curl.exe -fsS "https://redmine.example.com/periodictask/check?key=..."
  • a Kubernetes CronJob with a curlimages/curl container

The endpoint works whatever the Scheduler setting is.

Scheduler log

The plugin configuration page (Administration → Plugins → Redmine periodictask → Configure) shows the last 50 runs of the checker, whatever triggered them: cron/rake, a web request, the check URL or the Run checker now button. Each row shows when the run started, how many tasks were due, how many issues were created, how long it took, any errors, and notes about tasks that created nothing on purpose (see Previous issue open). Use it to confirm that your cron, uptime monitor or CI schedule is really firing. Consecutive runs that found nothing to do (or only skipped or waited for the same tasks) are grouped in one row, with a run counter and the time of the last one. So the 50 rows cover days of history even with a 5-minute web-request interval.

The Run checker now button on the same page runs the checker at once. Handy to test a setup without waiting for the scheduler.

Administration → Periodic Tasks lists the tasks of every project in one table, with links to each task's page and edit form. Inactive and ended tasks are greyed out. Tasks whose last run failed are marked.

Scheduler settings page with the scheduler log, with a highlighted failed run

Creating a periodic task from an existing issue

On an issue page, users with the Periodic tasks permission see Periodic Tasks → Create periodic task from this issue in the sidebar (when the module is enabled on the project). It opens the new task form prefilled from the issue: subject, description, tracker, priority, category, target version, assignee, parent, estimated hours, % done, custom fields, watchers, and tags or checklist template when those plugins are installed. The status stays at the tracker's default, because the source issue is often closed. The recurrence keeps its defaults. If the issue has a due date in the future, it becomes the first run. Nothing is stored until you submit the form.

Create periodic task from this issue link in the issue sidebar

Recurrence

A task repeats every N days, business days, weeks, months or years. A weekly task can run on several weekdays. A monthly task can run on a day of the month, or on the 1st to 5th (or last) occurrence of one or more weekdays, for example the 3rd Wednesday of every month. doc/recurrence-design.md explains how the next run date is computed, what happens with time zones and missing weekdays, and what happens after the scheduler was down.

To check a rule before saving it, look at Next occurrences under the next run date in the form. It shows the rule as a sentence (each month on the 5th (or last) Friday at 09:00 AM), the next 5 run days as chips (Fri 10/30/2026, hover for the full timestamp) and a Calendar link. The calendar unfolds month grids with the non-working days greyed out and every run day of those months marked, not only the 5 of the chips. Everything refreshes in your time zone when you change the interval, the weekdays, the ordinals, the non-working day adjustment, the next run date or the end condition. Nothing is saved until you submit the form.

A run moved by the non-working day adjustment gets a dashed chip and a struck-through day in the calendar. The tooltip shows the original date. The preview stops at the end condition (see Active flag and end condition): no run after the end date (a run exactly on it is still shown), and no more than the runs left before N, counting the scheduled runs already made. An ended task shows the reason instead of dates. The task page shows the same chips and calendar in its schedule header. The first date is the stored next run date. The following ones are the dates the scheduler will move to after each run, so runs missed while the scheduler was down collapse into the next future date.

Business days follow Redmine's own Administration → Settings → Issue tracking → Non-working days setting (Saturday and Sunday by default), the same one Redmine uses for issue dates. No extra gems, no separate holiday calendar.

The Non-working days option of a task decides what happens when a run falls on one of those days, for any unit:

  • Run on that day (default): the issue is created on the non-working day.
  • Move to the next working day: Saturday August 1st runs on Monday August 3rd.
  • Move to the previous working day: Saturday August 1st runs on Friday July 31st.

The time of day is kept and the schedule itself does not move. "Every month on day 1" still means the 1st, so the next run after a moved August 1st is September 1st. The task list and the task page show the day the task will really run, with the original date next to it.

Active flag and end condition

The Active box of the form (ticked by default) is your on/off switch. Untick it to pause a task without deleting it. The scheduler skips inactive tasks, but they keep their schedule and you can still run them with Run now. The task lists show them greyed out.

By default a task repeats forever. The Ends control of the form can stop it on a date and/or after N runs. When both are set, whichever comes first applies. A task is ended as soon as its next run would fall after the end date, or when the scheduled runs have reached N. Ended is not a setting. It is computed from those two fields, independently of the Active box, so a task can be active and ended at the same time (it will not run either way).

When the scheduler makes the last run, it writes an entry such as Periodic task ended (maximum number of runs reached) to the project activity. The task page and the form show Ended (end date reached) or Ended (maximum number of runs reached) next to the Active box. The task lists show Ends on <date> / Ended on <date> and <n> of <max> runs next to the schedule, and ended rows are greyed out and struck through, like closed issues.

Run now does not count towards N. Neither does an occurrence skipped because the previous issue was still open (see Previous issue open). To let an ended task run again, move the end date or raise N. The Active box alone is not enough. Lowering N below the runs already made ends the task at once. The copy action keeps the end condition and the Active flag and starts the count at 0.

Previous issue open

By default a task creates a new issue on every occurrence, even when nobody closed the one from last time. Unfinished issues pile up (a weekly report nobody writes). The Previous issue open setting of each task decides what a due run does when the issue it generated last time is still open:

Mode What happens while the previous issue is open Schedule
Create a new issue anyway (default) A new issue is created. The old one stays open. Unchanged.
Skip this occurrence Nothing is created. The task page shows The run of … did not create an issue: #123 was still open above the generated issues, and the scheduler log records skipped: #123 is still open in its Notes column. It is not an error, so Last error stays empty. The next run date moves on to the next occurrence. The skipped one is lost.
Close the previous issue The previous issue (and the subtasks the task generated under it) is closed with the first closed status its workflow allows the task author, with a journal note pointing at the new issue. Then the new issue is created. If Redmine refuses to close it (blocked by another issue, open subtasks the task did not create, no closed status), the new issue is still created and the failure shows in Last error and in the scheduler log. Unchanged.
Wait until it is closed Nothing is created and the next run date does not advance. The task stays due. Once the issue is closed, the schedule restarts from its closing day: the next run is the first occurrence after that day at the task's usual time (closed on Monday 15:37, daily at 10:00 → Tuesday 10:00; closed on a Friday, weekly on Wednesdays → next Wednesday; every 2 weeks → two weeks after the closing day). If that is already in the past, the issue is created right away. Anchored to the closing date instead of the fixed calendar, like Todoist's every!.

The Copy action copies the setting, and the task page shows it. Run now ignores it and always creates an issue. "Previous issue" means the newest top-level issue the task generated. Generated subtasks do not count, and an issue deleted from Redmine is ignored. doc/if-previous-open.md (also linked from the help icon next to the setting) explains each mode with examples.

Attachments

A periodic task can carry files: a checklist PDF, a form, a template spreadsheet. The task form has Redmine's standard Files field, and the task page lists the attached files with the usual download and delete links. Every generated issue gets its own copy of each file, with the same author and description. Deleting a file on a generated issue never touches the template, nor the copies on other issues. Copying a task offers to copy its attachments to the new task. Deleting a task deletes its attachments. A file that cannot be copied (for example because it is missing from the file system) does not stop the issue from being created. The failure shows in the task's Last error. Viewing, adding and deleting files requires the Periodic tasks permission of the project.

Periodic task form with the Files field

Assignee and category

The category of a task is optional, like on any issue. So is the assignee. When it is blank, each generated issue follows Redmine's own default assignee rules: the default assignee of the issue category if it has one, otherwise the project's default assignee, otherwise the issue stays unassigned.

Assignee rotation

Besides a single Assignee (user or group), a task can have an Assignee rotation: an ordered list of project members (users only, with a role that allows assignment). Each generated issue goes to the next user in the list, wrapping around at the end. This is the classic first-responder, on-call or release-captain roster. The form shows the ordered list and who is next. The task page and the task lists show Next: .

Users who can no longer be assigned issues in the project (locked, removed from the project, role changed) are skipped and logged in the Rails log. The single Assignee is the fallback. It (or, when blank, Redmine's default assignee) is used when the rotation is empty or when none of its users can be assigned anymore. Editing the list keeps the same user up next when that user is still listed. A copied task gets the same rotation, starting again from its first user.

Run now moves the rotation on exactly like a scheduled run: the generated issue goes to the next user and the following one becomes next. The schedule stays untouched.

Each run (scheduled or Run now) holds a database row lock on the task from generating the issue until the task is saved. So triggers firing at the same time (cron, the web scheduler, the endpoint, Run now) cannot hand the same turn to two issues. A scheduled run also re-checks that the task is still due once it holds the lock, so a task is not generated twice for one occurrence. A scheduled run is one transaction: the issue, the run count, the next run date and the ended activity entry are saved together or, if anything fails half-way, not at all. The failure goes to Last error and the scheduler log, and the other due tasks still run.

Finding the generated issues

Issues created by a periodic task get a recurrence icon next to their subject in the issue list, and the issue page says which task created them. The issue list also gets a Periodic task filter (any / none / is one of the project's tasks, including subprojects; every task you may manage on the global list) and an optional Periodic task column. The column shows the task's subject linked to its page, and you can sort and group by it like any other column. Both work in saved queries and in the REST API: GET /issues.json?periodictask=3 returns the issues generated by task #3, periodictask=* those generated by any task, and periodictask=!* the ones created by hand. The task page links to the issue list with the filter preset (All issues generated by this task).

Variable interpolation

You can use these variables in the subject and description of a periodic task. They are replaced with the matching value when the issue is created.

Variable Description
**DAY** Day of the month, zero-padded (01..31)
**WEEK** Week number of the year, starting with the first Monday as the first day of the first week (00..53)
**NEXT_WEEK** Same as **WEEK** for next week (00..53)
**WEEKISO** ISO 8601 week number of the year (01..53)
**NEXT_WEEKISO** Same as **WEEKISO** for next week (01..53)
**MONTH** Month of the year, zero-padded (01..12)
**PREVIOUS_MONTH** Previous month, zero-padded (01..12)
**NEXT_MONTH** Next month, zero-padded (01..12)
**MONTHNAME** Full month name (e.g. January), localized
**PREVIOUS_MONTHNAME** Full name of the previous month, localized
**NEXT_MONTHNAME** Full name of the next month, localized
**QUARTER** Quarter of the year (1..4)
**YEAR** Four-digit year
**WEEKISO_YEAR** ISO 8601 week-based year of **WEEKISO**
**NEXT_WEEK_YEAR** Four-digit year of **NEXT_WEEK**
**NEXT_WEEKISO_YEAR** ISO 8601 week-based year of **NEXT_WEEKISO**
**PREVIOUS_MONTH_YEAR** Four-digit year of **PREVIOUS_MONTH**
**NEXT_MONTH_YEAR** Four-digit year of **NEXT_MONTH**
**PREVIOUS_ISSUE** #<id> of the issue created by the previous run of the same task (e.g. #1234), empty on the first run

Each shifted variable has its own year companion, and **WEEKISO** has a week-based one. The reason: the year of the shifted date is not always the year of the run. **PREVIOUS_MONTH** with **YEAR** gives 12/2026 when the task runs in January 2026. And an ISO week number belongs to the ISO week-based year, which differs from the calendar year around New Year (2025-12-29 is already ISO week 01 of 2026).

**DAY**, **WEEK**, **WEEKISO**, **WEEKISO_YEAR**, **MONTH**, **MONTHNAME**, **QUARTER** and **YEAR** also accept a day offset, written as +N or -N before the closing ** (N up to 9999). **DAY-1** is the day of the month of the day before the issue is created, **MONTHNAME+10** the month name ten days later. The shifted variables above take no offset.

The offset shifts the whole date, so combined variables stay consistent across month and year boundaries. On 2027-01-01, **DAY-1**/**MONTH-1**/**YEAR-1** renders 31/12/2026.

**PREVIOUS_ISSUE** is not a date. It renders the number of the issue the same task created on its previous run. Weekly report (previous: **PREVIOUS_ISSUE**) gives Redmine's usual #1234 link back to last week's report, and Previous report: with nothing after it on the first run. **PREVIOUS_ISSUE-N** goes back N runs (**PREVIOUS_ISSUE-2** is the one before the previous). Deleted issues are skipped. Subtasks created by the task are not counted.

For localized month names, add LOCALE="de" to the cron job. Available: bg, de, en, es, hr, it, ja, pl, pt-BR, ru, tr, uk, vi, zh, zh-TW.

0 * * * * cd /opt/redmine && /usr/local/bin/bundle exec rake redmine:check_periodictasks RAILS_ENV=production LOCALE="de"

Subtasks and relations

A task can create child issues under each generated issue. Their subjects accept the same variables. It can also create relations from the generated issue to other issues, with any relation type Redmine supports (relates, follows, precedes, blocks, duplicates, copied_to, ...) and a delay for precedes/follows. The target of a relation is either a fixed issue number or Previous generated issue, the issue the same task created on its previous run. That way each weekly report can follow or relate to the one before, and users can walk the chain from Redmine's issue page. On the first run there is no previous issue and the relation is skipped without error. If the previous issue was deleted, the one created before it is used.

REST API

Periodic tasks can be listed, created, updated, deleted and run through Redmine's REST API, in JSON or XML, following the same conventions as the core API (/issues.json, ...). Enable Administration → Settings → API → Enable REST web service and authenticate with an API key (X-Redmine-API-Key header or key= parameter) or HTTP basic auth. The user needs the Periodic tasks permission in the project, and the Periodic tasks module must be enabled, exactly like for the HTML pages.

Method Path Description
GET /projects/:project_id/periodictask.json List the project's tasks. Paginated with limit (default 25, max 100) and offset; the response carries total_count, offset and limit
GET /projects/:project_id/periodictask/:id.json One task
POST /projects/:project_id/periodictask.json Create a task. Answers 201 Created with the task and a Location header
PUT/PATCH /projects/:project_id/periodictask/:id.json Update a task. Only the attributes sent are changed. Answers 204 No Content
DELETE /projects/:project_id/periodictask/:id.json Delete a task. Answers 204 No Content
POST /projects/:project_id/periodictask/:id/run_now.json Generate an issue right away without moving the schedule. Answers 201 Created with {"issue": {"id": ..., "subject": ..., "errors": [...]}} (errors lists non-fatal problems such as a relation that could not be created)
GET /admin/periodictasks.json Administrators only: the tasks of every project, paginated like the project list

:project_id is the project's numeric id or identifier. Replace .json with .xml for XML. Add include=issues to GET requests to list the issues each task generated (issues: [{id, created_at}]), and include=attachments for the task's files (attachments: [{id, filename, filesize, content_type, description, content_url, author, created_on}], like the issues API). Both can be combined (include=issues,attachments). A task from another project answers 404, a missing permission 403, and validation errors 422 with {"errors": ["Subject cannot be blank", ...]}. The rules are the same the form applies: the task is validated as the issue it would create.

A task is rendered with every stored field: id, project, tracker, author, assigned_to, category, fixed_version, priority and status as {id, name} pairs (omitted when not set), subject, description, interval_number, interval_units, weekdays, monthly_mode, month_weeks, weekend_adjustment, set_start_date, due_date_number, due_date_units, estimated_hours, done_ratio, parent_id, checklists_template_id, tags, custom_fields ([{id, name, value}]), watchers ([{id, name}]), rotation ([{id, name}], in turn order), rotation_next ({id, name} of the user the next issue goes to, omitted when there is no rotation or nobody in it can be assigned), subtasks, relations, if_previous_open (create, skip, close_previous or after_completion), last_skipped_issue ({id}) and last_skipped_at (only while the last run did not create an issue because that one was still open), is_active, ended and end_reason (computed from the end condition: ended_by_date, ended_by_count or null), next_run_date, effective_next_run_date (the moment the scheduler will really run it: the same as next_run_date unless Non-working days moves it), end_date, max_occurrences, occurrences_count (scheduled runs made so far), last_assigned_date, last_run (when the last issue was generated), last_error, created_at and updated_at. Times are ISO 8601 in UTC.

Attributes accepted on create/update, under a periodictask key (the same the form posts):

Attribute Value
subject, description Text, **DAY**-style variables allowed
tracker_id, assigned_to_id (user or group), author_id, issue_category_id, fixed_version_id, priority_id, status_id, parent_id Ids of the Redmine objects; author_id defaults to the API user
interval_number, interval_units Integer and one of day, business_day, week, month, year
weekdays Array of weekdays, 0 = Sunday ... 6 = Saturday (Ruby's wday), for weekly and monthly-by-weekday tasks
monthly_mode, month_weeks day_of_month or weekday, and the array of occurrences (1..5) for the latter
weekend_adjustment none, next_working_day or previous_working_day: what to do when a run falls on one of Redmine's non-working days
if_previous_open create, skip, close_previous or after_completion: what a due run does when the previous generated issue is still open (see Previous issue open)
next_run_date ISO 8601 time. Left blank on create, it is computed from the recurrence
end_date, max_occurrences End condition: ISO 8601 time and/or a positive integer; blank for none (see End condition)
set_start_date Boolean, set the issue start date to the generation date
due_date_number, due_date_units Due date as an offset from the generation date
estimated_hours, done_ratio Number, integer 0-100
is_active Boolean, default true; ended is read-only and follows the end condition
custom_fields or custom_field_values [{"id": 1, "value": "MySQL"}] like the core issues API, or a {"1": "MySQL"} hash
watcher_user_ids Array of user ids
rotation_ids Array of user ids, the assignee rotation in turn order; the user up next stays up next when still listed
subtasks Array of {tracker_id, subject, assigned_to_id, estimated_hours}
relations Array of {relation_type, issue_id, delay}; issue_id is a number or previous_issue (the issue generated by the previous run)
tag_list Tags for the generated issue (string or array), with a tagging plugin
checklists_template_id With the checklists plugin
uploads Files to attach to the task, [{token, filename, content_type, description}] with tokens from POST /uploads.json, exactly like the core issues API; they are added to the files the task already has

Sending an array attribute (weekdays, subtasks, ...) replaces the stored rows. Sending [] clears them.

Create a task that opens a "Weekly report" issue every Monday and Friday at 9:00 UTC:

curl -H "X-Redmine-API-Key: $KEY" -H "Content-Type: application/json" -X POST \
  https://redmine.example.com/projects/myproject/periodictask.json \
  -d '{"periodictask": {"subject": "Weekly report **WEEKISO**/**WEEKISO_YEAR**", "tracker_id": 2,
       "assigned_to_id": 5, "interval_number": 1, "interval_units": "week", "weekdays": [1, 5],
       "next_run_date": "2026-10-05T09:00:00Z", "due_date_number": 2, "due_date_units": "day",
       "watcher_user_ids": [7], "custom_fields": [{"id": 3, "value": "Reporting"}]}}'

List the tasks of a project with the issues they generated:

curl -H "X-Redmine-API-Key: $KEY" \
  "https://redmine.example.com/projects/myproject/periodictask.json?include=issues&limit=50"

Run a task immediately:

curl -H "X-Redmine-API-Key: $KEY" -X POST \
  https://redmine.example.com/projects/myproject/periodictask/12/run_now.json

Plugins supported

With redminecrm checklist PRO installed, a periodic task can pick a checklist template for the generated issues.

With a tagging plugin installed (RedmineUP Tags or redmine_tags), the task form gets a Tags field and the generated issues are tagged with it.

Development

Run docker compose up --build and wait until it finishes. In another console run ./provision.sh. It loads sample data so you have something to work with.

Then go to http://127.0.0.1:3000/ and log in with

user: admin
pass: admin

You should see a project named project1 with periodictask enabled.

To run the "cron checker": docker compose exec redmine bundle exec rake redmine:check_periodictasks RAILS_ENV=development. Or enable Automatic on web requests in the plugin configuration and reload any page.

Authors

Top Contributors

License

MIT

About

A redmine plugin that lets you schedule an issue to fire every x days/weeks/months

Topics

Resources

Stars

96 stars

Watchers

12 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages