diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index d750f0800b70083311112904c90658110d33b96c..6488399708f2db3cb4d1d574cc3b2925db1c1e5c 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -330,6 +330,23 @@ include 'filename'
       </listitem>
      </varlistentry>
 
+     <variablelist>
+     <varlistentry id="guc-recovery-config-directory" xreflabel="recovery_config_directory">
+      <term><varname>recovery_config_directory</varname> (<type>string</type>)</term>
+      <indexterm>
+       <primary><varname>recovery_config_directory</> configuration parameter</primary>
+      </indexterm>
+      <listitem>
+       <para>
+         Specifies the directory to use for the recovery.conf file. Note
+         the server requires read and write permission on this directory
+         because the file will be renamed to recovery.done at the end of
+         recovery.
+         This parameter can only be set at server start.
+       </para>
+      </listitem>
+     </varlistentry>
+
      <varlistentry id="guc-config-file" xreflabel="config_file">
       <term><varname>config_file</varname> (<type>string</type>)</term>
       <indexterm>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 07c68adf0bcda74ddd6ae102e6da281733d792e7..2f91bc88eaa884ba14054c31b99c14aadbb0ba4e 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -62,6 +62,7 @@
 
 extern bool bootstrap_data_checksums;
 
+char	recoveryConfPath[MAXPGPATH];
 /* File path names (all relative to $PGDATA) */
 #define RECOVERY_COMMAND_FILE	"recovery.conf"
 #define RECOVERY_COMMAND_DONE	"recovery.done"
@@ -4163,7 +4164,8 @@ readRecoveryCommandFile(void)
 			   *head = NULL,
 			   *tail = NULL;
 
-	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r");
+	snprintf(recoveryConfPath, MAXPGPATH, "%s/%s", RecoveryConfDir, RECOVERY_COMMAND_FILE);
+	fd = AllocateFile(recoveryConfPath, "r");
 	if (fd == NULL)
 	{
 		if (errno == ENOENT)
@@ -4171,7 +4173,7 @@ readRecoveryCommandFile(void)
 		ereport(FATAL,
 				(errcode_for_file_access(),
 				 errmsg("could not open recovery command file \"%s\": %m",
-						RECOVERY_COMMAND_FILE)));
+						recoveryConfPath)));
 	}
 
 	/*
@@ -4345,7 +4347,7 @@ readRecoveryCommandFile(void)
 		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
 			ereport(WARNING,
 					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
-							RECOVERY_COMMAND_FILE),
+							recoveryConfPath),
 					 errhint("The database server will regularly poll the pg_xlog subdirectory to check for files placed there.")));
 	}
 	else
@@ -4353,7 +4355,7 @@ readRecoveryCommandFile(void)
 		if (recoveryRestoreCommand == NULL)
 			ereport(FATAL,
 					(errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
-							RECOVERY_COMMAND_FILE)));
+							recoveryConfPath)));
 	}
 
 	/* Enable fetching from archive recovery area */
@@ -4395,6 +4397,7 @@ static void
 exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo)
 {
 	char		recoveryPath[MAXPGPATH];
+	char		recoveryDonePath[MAXPGPATH];
 	char		xlogpath[MAXPGPATH];
 
 	/*
@@ -4459,12 +4462,13 @@ exitArchiveRecovery(TimeLineID endTLI, XLogSegNo endLogSegNo)
 	 * Rename the config file out of the way, so that we don't accidentally
 	 * re-enter archive recovery mode in a subsequent crash.
 	 */
-	unlink(RECOVERY_COMMAND_DONE);
-	if (rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE) != 0)
+	snprintf(recoveryDonePath, MAXPGPATH, "%s/%s", RecoveryConfDir, RECOVERY_COMMAND_DONE);
+	unlink(recoveryDonePath);
+	if (rename(recoveryConfPath, recoveryDonePath) != 0)
 		ereport(FATAL,
 				(errcode_for_file_access(),
 				 errmsg("could not rename file \"%s\" to \"%s\": %m",
-						RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE)));
+						recoveryConfPath, recoveryDonePath)));
 
 	ereport(LOG,
 			(errmsg("archive recovery complete")));
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 9f51929191d5b23d12e13068843117a988df3436..8bf702191730472cfad72f0a7a1f750592cf4548 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -46,6 +46,7 @@ int			MyPMChildSlot;
  * explicitly.
  */
 char	   *DataDir = NULL;
+char	   *RecoveryConfDir = NULL;
 
 char		OutputFileName[MAXPGPATH];	/* debugging output file */
 
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index 24ca97d55c73ab5072de0bc29d629fce2145c769..3d48eb8c9d35740dbdd8112ed3d74eec64a85a76 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -99,6 +99,25 @@ SetDataDir(const char *dir)
 	DataDir = new;
 }
 
+/*
+ * Set recovery config directory, but make sure it's an absolute path.  Use this,
+ * never set RecoveryConfDir directly.
+ */
+void
+SetRecoveryConfDir(const char *dir)
+{
+	char	   *new;
+
+	AssertArg(dir);
+
+	/* If presented path is relative, convert to absolute */
+	new = make_absolute_path(dir);
+
+	if (RecoveryConfDir)
+		free(RecoveryConfDir);
+	RecoveryConfDir = new;
+}
+
 /*
  * Change working directory to DataDir.  Most of the postmaster and backend
  * code assumes that we are in DataDir so it can use relative paths to access
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 22ba35fef93fda2d13f0058cee1085ea6f3e416b..0459dd1c09bc7bf1c981a98cc484185ee27b72aa 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -424,6 +424,7 @@ int			temp_file_limit = -1;
 int			num_temp_buffers = 1024;
 
 char	   *data_directory;
+char	   *recovery_config_directory;
 char	   *ConfigFileName;
 char	   *HbaFileName;
 char	   *IdentFileName;
@@ -2960,6 +2961,17 @@ static struct config_string ConfigureNamesString[] =
 		NULL, NULL, NULL
 	},
 
+	{
+		{"recovery_config_directory", PGC_POSTMASTER, FILE_LOCATIONS,
+			gettext_noop("Sets the server's recovery configuration directory."),
+			NULL,
+			GUC_SUPERUSER_ONLY
+		},
+		&recovery_config_directory,
+		NULL,
+		NULL, NULL, NULL
+	},
+
 	{
 		{"config_file", PGC_POSTMASTER, FILE_LOCATIONS,
 			gettext_noop("Sets the server's main configuration file."),
@@ -4181,6 +4193,18 @@ SelectConfigFiles(const char *userDoption, const char *progname)
 	 */
 	SetConfigOption("data_directory", DataDir, PGC_POSTMASTER, PGC_S_OVERRIDE);
 
+	/*
+	 * If the recovery_config_directory GUC variable has been set, use that,
+	 * otherwise use DataDir.
+	 *
+	 * Note: SetRecoveryConfDir will copy and absolute-ize its argument,
+	 * so we don't have to.
+	 */
+	if (recovery_config_directory)
+		SetRecoveryConfDir(recovery_config_directory);
+	else
+		SetRecoveryConfDir(DataDir);
+
 	/*
 	 * If timezone_abbreviations wasn't set in the configuration file, install
 	 * the default value.  We do it this way because we can't safely install a
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 99858a765f1a0e18ff32c973eccb04ba196a7dc5..2bc513045d766aa40b250dff3810a04bb7a6f5d3 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -137,6 +137,7 @@ extern bool IsBinaryUpgrade;
 extern bool ExitOnAnyError;
 
 extern PGDLLIMPORT char *DataDir;
+extern PGDLLIMPORT char *RecoveryConfDir;
 
 extern PGDLLIMPORT int NBuffers;
 extern int	MaxBackends;
@@ -301,6 +302,7 @@ extern Oid	GetCurrentRoleId(void);
 extern void SetCurrentRoleId(Oid roleid, bool is_superuser);
 
 extern void SetDataDir(const char *dir);
+extern void SetRecoveryConfDir(const char *dir);
 extern void ChangeToDataDir(void);
 extern char *make_absolute_path(const char *path);
 
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index d497b1f6546d2dd0d41a34285c066e2a11b19748..42428cbd4351cfdcf79b0844018220b530581ff9 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -220,6 +220,7 @@ extern int	temp_file_limit;
 extern int	num_temp_buffers;
 
 extern char *data_directory;
+extern char *recovery_config_directory;
 extern char *ConfigFileName;
 extern char *HbaFileName;
 extern char *IdentFileName;