diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 4ec13f3311..bc904a09bd 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -3385,21 +3385,19 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"'  # Windows
 
      <variablelist>
      <varlistentry id="guc-recovery-target" xreflabel="recovery_target">
-      <term><varname>recovery_target</varname><literal> = 'immediate'</literal>
+      <term><varname>recovery_target</varname> (<type>string</type>)
       <indexterm>
         <primary><varname>recovery_target</varname> configuration parameter</primary>
       </indexterm>
       </term>
       <listitem>
        <para>
-        This parameter specifies that recovery should end as soon as a
-        consistent state is reached, i.e. as early as possible. When restoring
-        from an online backup, this means the point where taking the backup
-        ended.
-       </para>
-       <para>
-        Technically, this is a string parameter, but <literal>'immediate'</literal>
-        is currently the only allowed value.
+        This parameter determines how far recovery should proceed. The value
+        <literal>immediate</literal> means that recovery should end as soon as a consistent
+        state is reached, i.e. as early as possible. When restoring from an online
+        backup, this means the point where taking the backup ended.
+        The second possible value <literal>latest</literal> means that recovery
+        should proceed to the end of the available WAL log.
        </para>
       </listitem>
      </varlistentry>
diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c
index 5f0ee50092..db75587f1e 100644
--- a/src/backend/access/transam/xlog.c
+++ b/src/backend/access/transam/xlog.c
@@ -808,6 +808,7 @@ typedef struct XLogPageReadPrivate
 	int			emode;
 	bool		fetching_ckpt;	/* are we fetching a checkpoint record? */
 	bool		randAccess;
+	bool		return_on_eow;  /* returns when reaching End of WAL */
 } XLogPageReadPrivate;
 
 /*
@@ -843,6 +844,9 @@ static bool updateMinRecoveryPoint = true;
  */
 bool		reachedConsistency = false;
 
+/* Have we reached End of WAL? Used only in case of RECOVERY_TARGET_LATEST */
+bool		reachedEndOfWal = false;
+
 static bool InRedo = false;
 
 /* Have we launched bgwriter during recovery? */
@@ -887,7 +891,9 @@ static int	XLogFileReadAnyTLI(XLogSegNo segno, int emode, int source);
 static int	XLogPageRead(XLogReaderState *xlogreader, XLogRecPtr targetPagePtr,
 						 int reqLen, XLogRecPtr targetRecPtr, char *readBuf);
 static bool WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
-										bool fetching_ckpt, XLogRecPtr tliRecPtr);
+										bool fetching_ckpt,
+										XLogRecPtr tliRecPtr,
+										bool return_on_eow);
 static int	emode_for_corrupt_record(int emode, XLogRecPtr RecPtr);
 static void XLogFileClose(void);
 static void PreallocXlogFiles(XLogRecPtr endptr);
@@ -4253,6 +4259,7 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 	private->fetching_ckpt = fetching_ckpt;
 	private->emode = emode;
 	private->randAccess = (RecPtr != InvalidXLogRecPtr);
+	private->return_on_eow = (recoveryTarget == RECOVERY_TARGET_LATEST);
 
 	/* This is the first attempt to read this page. */
 	lastSourceFailed = false;
@@ -4371,8 +4378,12 @@ ReadRecord(XLogReaderState *xlogreader, XLogRecPtr RecPtr, int emode,
 				continue;
 			}
 
-			/* In standby mode, loop back to retry. Otherwise, give up. */
-			if (StandbyMode && !CheckForStandbyTrigger())
+			/*
+			 * In standby mode, loop back to retry. Otherwise, give up.
+			 * If we told to return on end of WAL, also give up.
+			 */
+			if (StandbyMode && !CheckForStandbyTrigger() &&
+				!private->return_on_eow)
 				continue;
 			else
 				return NULL;
@@ -6342,6 +6353,9 @@ StartupXLOG(void)
 		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
 			ereport(LOG,
 					(errmsg("starting point-in-time recovery to earliest consistent point")));
+		else if (recoveryTarget == RECOVERY_TARGET_LATEST)
+			ereport(LOG,
+					(errmsg("starting point-in-time recovery until all available WAL is applied")));
 		else
 			ereport(LOG,
 					(errmsg("starting archive recovery")));
@@ -7254,6 +7268,13 @@ StartupXLOG(void)
 			 * end of main redo apply loop
 			 */
 
+			if (recoveryTarget == RECOVERY_TARGET_LATEST && reachedEndOfWal)
+			{
+				ereport(LOG,
+						(errmsg("recovery stopping after reaching the end of available WAL")));
+				reachedStopPoint = true;
+			}
+
 			if (reachedStopPoint)
 			{
 				if (!reachedConsistency)
@@ -7456,6 +7477,8 @@ StartupXLOG(void)
 					 recoveryStopName);
 		else if (recoveryTarget == RECOVERY_TARGET_IMMEDIATE)
 			snprintf(reason, sizeof(reason), "reached consistency");
+		else if (recoveryTarget == RECOVERY_TARGET_LATEST)
+			snprintf(reason, sizeof(reason), "end of WAL");
 		else
 			snprintf(reason, sizeof(reason), "no recovery target specified");
 
@@ -11588,7 +11611,8 @@ retry:
 		if (!WaitForWALToBecomeAvailable(targetPagePtr + reqLen,
 										 private->randAccess,
 										 private->fetching_ckpt,
-										 targetRecPtr))
+										 targetRecPtr,
+										 private->return_on_eow))
 		{
 			if (readFile >= 0)
 				close(readFile);
@@ -11728,7 +11752,9 @@ next_record_is_invalid:
  *
  * If the record is not immediately available, the function returns false
  * if we're not in standby mode. In standby mode, waits for it to become
- * available.
+ * available looping over all sources if return_on_eow is false. Otherwise the
+ * function returns false if the record is not immediately available on all
+ * sources.
  *
  * When the requested record becomes available, the function opens the file
  * containing it (if not open already), and returns true. When end of standby
@@ -11737,7 +11763,8 @@ next_record_is_invalid:
  */
 static bool
 WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
-							bool fetching_ckpt, XLogRecPtr tliRecPtr)
+							bool fetching_ckpt, XLogRecPtr tliRecPtr,
+							bool return_on_eow)
 {
 	static TimestampTz last_fail_time = 0;
 	TimestampTz now;
@@ -11845,6 +11872,15 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 						receivedUpto = 0;
 					}
 
+					/*
+					 * If we don't get into streaming, this is the end of WAL
+					 */
+					else if (return_on_eow)
+					{
+						reachedEndOfWal = true;
+						return false;
+					}
+
 					/*
 					 * Move to XLOG_FROM_STREAM state in either case. We'll
 					 * get immediate failure if we didn't launch walreceiver,
@@ -12107,6 +12143,18 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess,
 									 WL_EXIT_ON_PM_DEATH,
 									 5000L, WAIT_EVENT_RECOVERY_WAL_ALL);
 					ResetLatch(&XLogCtl->recoveryWakeupLatch);
+
+					/*
+					 * This is not exactly the end of WAL, but assume the
+					 * primary has no more record to send.
+					 */
+					if (return_on_eow)
+					{
+						reachedEndOfWal = true;
+						currentSource = XLOG_FROM_PG_WAL;
+						return false;
+					}
+
 					break;
 				}
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 5fccc9683e..3238e4ce98 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -3545,7 +3545,10 @@ static struct config_string ConfigureNamesString[] =
 
 	{
 		{"recovery_target", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
-			gettext_noop("Set to \"immediate\" to end recovery as soon as a consistent state is reached."),
+			gettext_noop("Set to \"immediate\" to end recovery as "
+						 "soon as a consistent state is reached or "
+						 " to \"latest\" to end recovery after "
+						 "replaying all available WAL in the archive."),
 			NULL
 		},
 		&recovery_target_string,
@@ -11650,9 +11653,10 @@ error_multiple_recovery_targets(void)
 static bool
 check_recovery_target(char **newval, void **extra, GucSource source)
 {
-	if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "") != 0)
+	if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "latest") != 0 &&
+		strcmp(*newval, "") != 0)
 	{
-		GUC_check_errdetail("The only allowed value is \"immediate\".");
+		GUC_check_errdetail("The only allowed values are \"immediate\" and \"latest\".");
 		return false;
 	}
 	return true;
@@ -11662,11 +11666,17 @@ static void
 assign_recovery_target(const char *newval, void *extra)
 {
 	if (recoveryTarget != RECOVERY_TARGET_UNSET &&
-		recoveryTarget != RECOVERY_TARGET_IMMEDIATE)
+		recoveryTarget != RECOVERY_TARGET_IMMEDIATE &&
+		recoveryTarget != RECOVERY_TARGET_LATEST)
 		error_multiple_recovery_targets();
 
 	if (newval && strcmp(newval, "") != 0)
-		recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
+	{
+		if (strcmp(newval, "immediate") == 0)
+			recoveryTarget = RECOVERY_TARGET_IMMEDIATE;
+		else if (strcmp(newval, "latest") == 0)
+			recoveryTarget = RECOVERY_TARGET_LATEST;
+	}
 	else
 		recoveryTarget = RECOVERY_TARGET_UNSET;
 }
diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h
index d519252aad..55e7e4bd2c 100644
--- a/src/include/access/xlog.h
+++ b/src/include/access/xlog.h
@@ -84,7 +84,8 @@ typedef enum
 	RECOVERY_TARGET_TIME,
 	RECOVERY_TARGET_NAME,
 	RECOVERY_TARGET_LSN,
-	RECOVERY_TARGET_IMMEDIATE
+	RECOVERY_TARGET_IMMEDIATE,
+	RECOVERY_TARGET_LATEST
 } RecoveryTargetType;
 
 /*
diff --git a/src/test/recovery/t/003_recovery_targets.pl b/src/test/recovery/t/003_recovery_targets.pl
index d8fbd50011..70fb79a8fc 100644
--- a/src/test/recovery/t/003_recovery_targets.pl
+++ b/src/test/recovery/t/003_recovery_targets.pl
@@ -3,7 +3,8 @@ use strict;
 use warnings;
 use PostgresNode;
 use TestLib;
-use Test::More tests => 8;
+use Test::More tests => 15;
+#use Time::HiRes qw(usleep);
 
 # Create and test a standby from given backup, with a certain recovery target.
 # Choose $until_lsn later than the transaction commit that causes the row
@@ -26,6 +27,8 @@ sub test_recovery_standby
 		$node_standby->append_conf('postgresql.conf', qq($param_item));
 	}
 
+	$node_standby->append_conf('postgresql.conf', "recovery_target_action = promote");
+
 	$node_standby->start;
 
 	# Wait until standby has replayed enough data
@@ -39,6 +42,15 @@ sub test_recovery_standby
 	  $node_standby->safe_psql('postgres', "SELECT count(*) FROM tab_int");
 	is($result, qq($num_rows), "check standby content for $test_name");
 
+	# check that mode was promoted
+	if ($test_name ne 'multiple overriding settings')
+	{
+		$node_standby->safe_psql('postgres', "SELECT pg_sleep(1)");
+		my $is_in_recovery =
+		  $node_standby->safe_psql('postgres', "SELECT pg_is_in_recovery()");
+		is($is_in_recovery, 'f', "check that standby was promoted for $test_name");
+	}
+
 	# Stop standby node
 	$node_standby->teardown_node;
 
@@ -77,7 +89,7 @@ my $lsn3 =
   $node_master->safe_psql('postgres', "SELECT pg_current_wal_lsn();");
 my $recovery_time = $node_master->safe_psql('postgres', "SELECT now()");
 
-# Even more data, this time with a recovery target name
+# More data, with a recovery target name
 $node_master->safe_psql('postgres',
 	"INSERT INTO tab_int VALUES (generate_series(3001,4000))");
 my $recovery_name = "my_target";
@@ -86,14 +98,17 @@ my $lsn4 =
 $node_master->safe_psql('postgres',
 	"SELECT pg_create_restore_point('$recovery_name');");
 
-# And now for a recovery target LSN
+# Even more data, this time with a recovery target LSN
 $node_master->safe_psql('postgres',
 	"INSERT INTO tab_int VALUES (generate_series(4001,5000))");
 my $lsn5 = my $recovery_lsn =
   $node_master->safe_psql('postgres', "SELECT pg_current_wal_lsn()");
 
+# And now for a recovery target 'latest'
 $node_master->safe_psql('postgres',
 	"INSERT INTO tab_int VALUES (generate_series(5001,6000))");
+my $lsn6 =
+  $node_master->safe_psql('postgres', "SELECT pg_current_wal_lsn();");
 
 # Force archiving of WAL file
 $node_master->safe_psql('postgres', "SELECT pg_switch_wal()");
@@ -114,6 +129,9 @@ test_recovery_standby('name', 'standby_4', $node_master, \@recovery_params,
 @recovery_params = ("recovery_target_lsn = '$recovery_lsn'");
 test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
 	"5000", $lsn5);
+@recovery_params = ("recovery_target = 'latest'");
+test_recovery_standby('latest target', 'standby_6', $node_master, \@recovery_params,
+	"6000", $lsn6);
 
 # Multiple targets
 #
@@ -126,9 +144,9 @@ test_recovery_standby('LSN', 'standby_5', $node_master, \@recovery_params,
 	"recovery_target_name = ''",
 	"recovery_target_time = '$recovery_time'");
 test_recovery_standby('multiple overriding settings',
-	'standby_6', $node_master, \@recovery_params, "3000", $lsn3);
+	'standby_7', $node_master, \@recovery_params, "3000", $lsn3);
 
-my $node_standby = get_new_node('standby_7');
+my $node_standby = get_new_node('standby_8');
 $node_standby->init_from_backup($node_master, 'my_backup',
 	has_restoring => 1);
 $node_standby->append_conf(
