diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md
index ea788d33743273a7051e7a64ec9a658e54c5fb69..d52c79888ba2a69693d981ce06d8467d68d20749 100644
--- a/docs/usage/configuration-options.md
+++ b/docs/usage/configuration-options.md
@@ -1411,6 +1411,7 @@ before 5:00am
 after 10pm and before 5:00am
 after 10pm and before 5am every weekday
 on friday and saturday
+every 3 months on the first day of the month
 ```
 
 One example might be that you don't want Renovate to run during your typical business hours, so that your build machines don't get clogged up testing `package.json` updates. You could then configure a schedule like this at the repository level:
diff --git a/lib/config/presets/internal/schedule.ts b/lib/config/presets/internal/schedule.ts
index 6281b100d289c1629c3d49eefeb6b0c91688e83b..9de5cb1c6740f4b5827090b6f50645f6385fbad3 100644
--- a/lib/config/presets/internal/schedule.ts
+++ b/lib/config/presets/internal/schedule.ts
@@ -17,6 +17,10 @@ export const presets: Record<string, Preset> = {
     description: 'Schedule monthly',
     schedule: ['before 3am on the first day of the month'],
   },
+  quarterly: {
+    description: 'Schedule quarterly',
+    schedule: ['every 3 months on the first day of the month'],
+  },
   weekends: {
     description: 'Schedule for weekends',
     schedule: ['every weekend'],
diff --git a/lib/workers/branch/schedule.spec.ts b/lib/workers/branch/schedule.spec.ts
index 53679c566673389e2d8f43e679c13ace469c5a6e..ccd5f46f624a5b65628df98bff86196959fa04b8 100644
--- a/lib/workers/branch/schedule.spec.ts
+++ b/lib/workers/branch/schedule.spec.ts
@@ -61,6 +61,11 @@ describe('workers/branch/schedule', () => {
         schedule.hasValidSchedule(['on the first day of the month'])[0]
       ).toBe(true);
     });
+    it('returns true for schedules longer than 1 month', () => {
+      expect(schedule.hasValidSchedule(['every 3 months'])[0]).toBe(true);
+      expect(schedule.hasValidSchedule(['every 6 months'])[0]).toBe(true);
+      expect(schedule.hasValidSchedule(['every 12 months'])[0]).toBe(true);
+    });
     it('returns true if schedule has an end time', () => {
       expect(schedule.hasValidSchedule(['before 6:00am'])[0]).toBe(true);
     });
@@ -227,5 +232,29 @@ describe('workers/branch/schedule', () => {
       const res = schedule.isScheduledNow(config);
       expect(res).toBe(false);
     });
+    it('approves schedule longer than 1 month', () => {
+      config.schedule = ['every 3 months'];
+      mockDate.set('2017-07-01T06:00:00.000'); // Locally Saturday, 1 July 2017 6am
+      const res = schedule.isScheduledNow(config);
+      expect(res).toBe(true);
+    });
+    it('rejects schedule longer than 1 month', () => {
+      config.schedule = ['every 6 months'];
+      mockDate.set('2017-02-01T06:00:00.000'); // Locally Thursday, 2 February 2017 6am
+      const res = schedule.isScheduledNow(config);
+      expect(res).toBe(false);
+    });
+    it('approves schedule longer than 1 month with day of month', () => {
+      config.schedule = ['every 3 months on the first day of the month'];
+      mockDate.set('2017-07-01T06:00:00.000'); // Locally Saturday, 1 July 2017 6am
+      const res = schedule.isScheduledNow(config);
+      expect(res).toBe(true);
+    });
+    it('rejects schedule longer than 1 month with day of month', () => {
+      config.schedule = ['every 3 months on the first day of the month'];
+      mockDate.set('2017-02-01T06:00:00.000'); // Locally Thursday, 2 February 2017 6am
+      const res = schedule.isScheduledNow(config);
+      expect(res).toBe(false);
+    });
   });
 });