diff --git a/src/sbbs3/logon.cpp b/src/sbbs3/logon.cpp
index 41721fe5c9021e3ed3d629ad2df9091f04dbdbd2..d343d2e1d18f4bee351162a7aee534882622fbd3 100644
--- a/src/sbbs3/logon.cpp
+++ b/src/sbbs3/logon.cpp
@@ -626,10 +626,12 @@ uint sbbs_t::logonstats()
 	if((tm.tm_mday>update_tm.tm_mday && tm.tm_mon==update_tm.tm_mon)
 		|| tm.tm_mon>update_tm.tm_mon || tm.tm_year>update_tm.tm_year) {
 
-		safe_snprintf(msg, sizeof(msg), "New Day - Prev: %s ",timestr(stats.date));
+		sys_status |= SS_NEW_DAY;
+		if(tm.tm_mon != update_tm.tm_mon)
+			sys_status |= SS_NEW_MONTH;
+		safe_snprintf(msg, sizeof(msg), "New Day%s - Prev: %s "
+			,(sys_status & SS_NEW_MONTH) ? " and Month" :"", timestr(stats.date));
 		logline(LOG_NOTICE, "!=", msg);
-
-		sys_status|=SS_DAILY;       /* New Day !!! */
 		safe_snprintf(path, sizeof(path), "%slogon.lst",cfg.data_dir);    /* Truncate logon list (LEGACY) */
 		int file;
 		if((file=nopen(path,O_TRUNC|O_CREAT|O_WRONLY))==-1) {
diff --git a/src/sbbs3/main.cpp b/src/sbbs3/main.cpp
index a2787733c50d2d4f3e2b419dd688a394b0274461..52925db21b3a8e48a3284717041e7becb549c5e0 100644
--- a/src/sbbs3/main.cpp
+++ b/src/sbbs3/main.cpp
@@ -4842,6 +4842,14 @@ void sbbs_t::daily_maint(void)
 		online = false;
 		lprintf(result ? LOG_ERR : LOG_INFO, "Daily event: '%s' returned %d", cmd, result);
 	}
+	if((sys_status & SS_NEW_MONTH) && cfg.sys_monthly[0]) {
+		lputs(LOG_INFO, "DAILY: Running monthly event");
+		const char* cmd = cmdstr(cfg.sys_monthly,nulstr,nulstr,NULL);
+		online = ON_LOCAL;
+		int result = external(cmd, EX_OFFLINE);
+		online = false;
+		lprintf(result ? LOG_ERR : LOG_INFO, "Monthly event: '%s' returned %d", cmd, result);
+	}
 	lputs(LOG_INFO, "DAILY: System maintenance ended");
 	sys_status&=~SS_DAILY;
 }
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index 60968a32da57da34bbedca7d88a1337d5557eaee..c2c7a6b2df9a6f7febbc89626574497c3d95023b 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -617,7 +617,8 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define SS_USERON		(1<<3)	/* A User is logged on to the BBS				*/
 #define SS_LCHAT		(1<<4)	/* Local chat in progress						*/
 #define SS_ANSCAP		(1<<6)	/* Capture ANSI codes too						*/
-#define SS_DAILY		(1<<9)	/* Execute System Daily Event on logoff 		*/
+#define SS_NEW_MONTH	(1<<8)	// Execute System Monthly Event
+#define SS_NEW_DAY		(1<<9)	// Execute System Daily Event
 #define SS_INUEDIT		(1<<10)	/* Inside Alt-Useredit section 				*/
 #define SS_ABORT		(1<<11) /* Global abort input or output flag			*/
 #define SS_SYSPAGE		(1<<12) /* Paging sysop								*/
@@ -637,6 +638,8 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define SS_QWKLOGON		(1<<30) /* QWK logon									*/
 #define SS_FASTLOGON	(1U<<31)/* Fast logon									*/
 
+#define SS_DAILY		(SS_NEW_DAY | SS_NEW_MONTH)
+
 								/* Bits in 'mode' for getkey and getstr     */
 #define K_NONE		0			/* Use as a place holder for no mode flags	*/
 #define K_UPPER 	(1<<0) 		/* Converts all letters to upper case		*/
diff --git a/src/sbbs3/scfg/scfgxtrn.c b/src/sbbs3/scfg/scfgxtrn.c
index c2addfb24e729f95cf1c605269221136197ea22d..198a3a28173377c21b5539a894829134fe3f1116 100644
--- a/src/sbbs3/scfg/scfgxtrn.c
+++ b/src/sbbs3/scfg/scfgxtrn.c
@@ -329,12 +329,13 @@ void fevents_cfg()
 		snprintf(opt[i++],MAX_OPLN,"%-32.32s%s","Logon Event",cfg.sys_logon);
 		snprintf(opt[i++],MAX_OPLN,"%-32.32s%s","Logout Event",cfg.sys_logout);
 		snprintf(opt[i++],MAX_OPLN,"%-32.32s%s","Daily Event",cfg.sys_daily);
+		snprintf(opt[i++],MAX_OPLN,"%-32.32s%s","Monthly Event",cfg.sys_monthly);
 		opt[i][0]=0;
 		uifc.helpbuf=
 			"`External Events:`\n"
 			"\n"
 			"From this menu, you can configure the logon and logout events, and the\n"
-			"system daily event.\n"
+			"system daily and monthly (off-line) events.\n"
 		;
 		switch(uifc.list(WIN_ACT|WIN_SAV|WIN_CHE|WIN_BOT|WIN_RHT,0,0,60,&event_dflt,0
 			,"Fixed Events",opt)) {
@@ -385,6 +386,18 @@ void fevents_cfg()
 				uifc.input(WIN_MID|WIN_SAV,0,0,"Daily Event"
 					,cfg.sys_daily,sizeof(cfg.sys_daily)-1,K_EDIT);
 
+				break;
+			case 3:
+				uifc.helpbuf=
+					"`Monthly Event:`\n"
+					"\n"
+					"Enter a command line for a program that will run once each new month.\n"
+					SCFG_CMDLINE_PREFIX_HELP
+					SCFG_CMDLINE_SPEC_HELP
+				;
+				uifc.input(WIN_MID|WIN_SAV,0,0,"Monthly Event"
+					,cfg.sys_monthly,sizeof(cfg.sys_monthly)-1,K_EDIT);
+
 				break;
 		}
 	}
diff --git a/src/sbbs3/scfgdefs.h b/src/sbbs3/scfgdefs.h
index 2f7316078a3daf07b26524f3fa5e16c7c97fd743..2c757e5ed223ff44857f18703df3540e5206fa26 100644
--- a/src/sbbs3/scfgdefs.h
+++ b/src/sbbs3/scfgdefs.h
@@ -465,6 +465,7 @@ typedef struct
 	enum date_fmt	sys_date_fmt;
 	char			sys_date_sep;
 	bool			sys_date_verbal;
+	char 			sys_monthly[LEN_CMD+1];	   /* Monthly event */
 	char 			sys_daily[LEN_CMD+1];	   /* Daily event */
 	char 			sys_logon[LEN_CMD+1];	   /* Logon event */
 	char 			sys_logout[LEN_CMD+1];	   /* Logoff event */
diff --git a/src/sbbs3/scfglib1.c b/src/sbbs3/scfglib1.c
index 3ba078f6f355bddbf1fbd1595c4f98b1d57fcc42..740603d9db3408757b4a05a101c68baa53df85e4 100644
--- a/src/sbbs3/scfglib1.c
+++ b/src/sbbs3/scfglib1.c
@@ -138,6 +138,7 @@ bool read_main_cfg(scfg_t* cfg, char* error, size_t maxerrlen)
 	SAFECOPY(cfg->sys_logon, iniGetString(ini, "logon_event", "cmd", "", value));
 	SAFECOPY(cfg->sys_logout, iniGetString(ini, "logout_event", "cmd", "", value));
 	SAFECOPY(cfg->sys_daily, iniGetString(ini, "daily_event", "cmd", "", value));
+	SAFECOPY(cfg->sys_monthly, iniGetString(ini, "monthly_event", "cmd", "", value));
 
 	named_str_list_t** sections = iniParseSections(ini);
 
diff --git a/src/sbbs3/scfgsave.c b/src/sbbs3/scfgsave.c
index 8830a928169a99033c43e489faa3ea598de2f146..e282785b2da9d1d8f75793443eabf19810e83335 100644
--- a/src/sbbs3/scfgsave.c
+++ b/src/sbbs3/scfgsave.c
@@ -203,6 +203,7 @@ bool write_main_cfg(scfg_t* cfg)
 	iniSetString(&ini, "logon_event", "cmd", cfg->sys_logon, NULL);
 	iniSetString(&ini, "logout_event", "cmd", cfg->sys_logout, NULL);
 	iniSetString(&ini, "daily_event", "cmd", cfg->sys_daily, NULL);
+	iniSetString(&ini, "monthly_event", "cmd", cfg->sys_monthly, NULL);
 
 	{
 		const char* name = "expired";