diff --git a/contrib/pg_upgrade/controldata.c b/contrib/pg_upgrade/controldata.c
index 3ac2180d49bf76296c4521eb4d87f206314818c9..5ce4b95b0696be430967b35527eb818e77a0a88c 100644
--- a/contrib/pg_upgrade/controldata.c
+++ b/contrib/pg_upgrade/controldata.c
@@ -11,8 +11,6 @@
 
 #include <ctype.h>
 
-static void putenv2(const char *var, const char *val);
-
 /*
  * get_control_data()
  *
@@ -85,21 +83,21 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	if (getenv("LC_MESSAGES"))
 		lc_messages = pg_strdup(getenv("LC_MESSAGES"));
 
-	putenv2("LC_COLLATE", NULL);
-	putenv2("LC_CTYPE", NULL);
-	putenv2("LC_MONETARY", NULL);
-	putenv2("LC_NUMERIC", NULL);
-	putenv2("LC_TIME", NULL);
-	putenv2("LANG",
+	pg_putenv("LC_COLLATE", NULL);
+	pg_putenv("LC_CTYPE", NULL);
+	pg_putenv("LC_MONETARY", NULL);
+	pg_putenv("LC_NUMERIC", NULL);
+	pg_putenv("LC_TIME", NULL);
+	pg_putenv("LANG",
 #ifndef WIN32
 			NULL);
 #else
 	/* On Windows the default locale cannot be English, so force it */
 			"en");
 #endif
-	putenv2("LANGUAGE", NULL);
-	putenv2("LC_ALL", NULL);
-	putenv2("LC_MESSAGES", "C");
+	pg_putenv("LANGUAGE", NULL);
+	pg_putenv("LC_ALL", NULL);
+	pg_putenv("LC_MESSAGES", "C");
 
 	snprintf(cmd, sizeof(cmd), SYSTEMQUOTE "\"%s/%s \"%s\"" SYSTEMQUOTE,
 			 cluster->bindir,
@@ -374,15 +372,15 @@ get_control_data(ClusterInfo *cluster, bool live_check)
 	/*
 	 * Restore environment variables
 	 */
-	putenv2("LC_COLLATE", lc_collate);
-	putenv2("LC_CTYPE", lc_ctype);
-	putenv2("LC_MONETARY", lc_monetary);
-	putenv2("LC_NUMERIC", lc_numeric);
-	putenv2("LC_TIME", lc_time);
-	putenv2("LANG", lang);
-	putenv2("LANGUAGE", language);
-	putenv2("LC_ALL", lc_all);
-	putenv2("LC_MESSAGES", lc_messages);
+	pg_putenv("LC_COLLATE", lc_collate);
+	pg_putenv("LC_CTYPE", lc_ctype);
+	pg_putenv("LC_MONETARY", lc_monetary);
+	pg_putenv("LC_NUMERIC", lc_numeric);
+	pg_putenv("LC_TIME", lc_time);
+	pg_putenv("LANG", lang);
+	pg_putenv("LANGUAGE", language);
+	pg_putenv("LC_ALL", lc_all);
+	pg_putenv("LC_MESSAGES", lc_messages);
 
 	pg_free(lc_collate);
 	pg_free(lc_ctype);
@@ -529,40 +527,3 @@ rename_old_pg_control(void)
 		pg_log(PG_FATAL, "Unable to rename %s to %s.\n", old_path, new_path);
 	check_ok();
 }
-
-
-/*
- *	putenv2()
- *
- *	This is like putenv(), but takes two arguments.
- *	It also does unsetenv() if val is NULL.
- */
-static void
-putenv2(const char *var, const char *val)
-{
-	if (val)
-	{
-#ifndef WIN32
-		char	   *envstr = (char *) pg_malloc(strlen(var) +
-												strlen(val) + 2);
-
-		sprintf(envstr, "%s=%s", var, val);
-		putenv(envstr);
-
-		/*
-		 * Do not free envstr because it becomes part of the environment on
-		 * some operating systems.	See port/unsetenv.c::unsetenv.
-		 */
-#else
-		SetEnvironmentVariableA(var, val);
-#endif
-	}
-	else
-	{
-#ifndef WIN32
-		unsetenv(var);
-#else
-		SetEnvironmentVariableA(var, "");
-#endif
-	}
-}
diff --git a/contrib/pg_upgrade/option.c b/contrib/pg_upgrade/option.c
index 36561b9b4ca1296599e80673f89400a43d0c45b5..e545458a75a86f4c874c57dc59aac4a0d5eb82d5 100644
--- a/contrib/pg_upgrade/option.c
+++ b/contrib/pg_upgrade/option.c
@@ -53,23 +53,24 @@ parseCommandLine(int argc, char *argv[])
 	};
 	int			option;			/* Command line option */
 	int			optindex = 0;	/* used by getopt_long */
-	int			user_id;
+	int			os_user_effective_id;
 
-	if (getenv("PGUSER"))
-	{
-		pg_free(os_info.user);
-		os_info.user = pg_strdup(getenv("PGUSER"));
-	}
+	user_opts.transfer_mode = TRANSFER_MODE_COPY;
 
 	os_info.progname = get_progname(argv[0]);
+
+	/* Process libpq env. variables; load values here for usage() output */
 	old_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
 	new_cluster.port = getenv("PGPORT") ? atoi(getenv("PGPORT")) : DEF_PGPORT;
-	/* must save value, getenv()'s pointer is not stable */
 
-	user_opts.transfer_mode = TRANSFER_MODE_COPY;
-
-	/* user lookup and 'root' test must be split because of usage() */
-	user_id = get_user_info(&os_info.user);
+	os_user_effective_id = get_user_info(&os_info.user);
+	/* we override just the database user name;  we got the OS id above */
+	if (getenv("PGUSER"))
+	{
+		pg_free(os_info.user);
+		/* must save value, getenv()'s pointer is not stable */
+		os_info.user = pg_strdup(getenv("PGUSER"));
+	}
 
 	if (argc > 1)
 	{
@@ -86,7 +87,8 @@ parseCommandLine(int argc, char *argv[])
 		}
 	}
 
-	if (user_id == 0)
+	/* Allow help and version to be run as root, so do the test here. */
+	if (os_user_effective_id == 0)
 		pg_log(PG_FATAL, "%s: cannot be run as root\n", os_info.progname);
 
 	getcwd(os_info.cwd, MAXPGPATH);
@@ -96,14 +98,6 @@ parseCommandLine(int argc, char *argv[])
 	{
 		switch (option)
 		{
-			case 'd':
-				old_cluster.pgdata = pg_strdup(optarg);
-				break;
-
-			case 'D':
-				new_cluster.pgdata = pg_strdup(optarg);
-				break;
-
 			case 'b':
 				old_cluster.bindir = pg_strdup(optarg);
 				break;
@@ -116,6 +110,14 @@ parseCommandLine(int argc, char *argv[])
 				user_opts.check = true;
 				break;
 
+			case 'd':
+				old_cluster.pgdata = pg_strdup(optarg);
+				break;
+
+			case 'D':
+				new_cluster.pgdata = pg_strdup(optarg);
+				break;
+
 			case 'g':
 				pg_log(PG_REPORT, "Running in debug mode\n");
 				log_opts.debug = true;
@@ -156,6 +158,11 @@ parseCommandLine(int argc, char *argv[])
 			case 'u':
 				pg_free(os_info.user);
 				os_info.user = pg_strdup(optarg);
+				/*
+				 * Push the user name into the environment so pre-9.1
+				 * pg_ctl/libpq uses it.
+				 */
+				pg_putenv("PGUSER", os_info.user);
 				break;
 
 			case 'v':
@@ -197,14 +204,14 @@ parseCommandLine(int argc, char *argv[])
 	}
 
 	/* Get values from env if not already set */
-	validateDirectoryOption(&old_cluster.pgdata, "OLDDATADIR", "-d",
-							"old cluster data resides");
-	validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
-							"new cluster data resides");
 	validateDirectoryOption(&old_cluster.bindir, "OLDBINDIR", "-b",
 							"old cluster binaries reside");
 	validateDirectoryOption(&new_cluster.bindir, "NEWBINDIR", "-B",
 							"new cluster binaries reside");
+	validateDirectoryOption(&old_cluster.pgdata, "OLDDATADIR", "-d",
+							"old cluster data resides");
+	validateDirectoryOption(&new_cluster.pgdata, "NEWDATADIR", "-D",
+							"new cluster data resides");
 
 	get_pkglibdirs();
 }
diff --git a/contrib/pg_upgrade/pg_upgrade.c b/contrib/pg_upgrade/pg_upgrade.c
index 857e82901bf53f336f82e7f0bbb36328adb2b54e..6eaaa0fb84cd557b327180a7ec23a131c824d4bc 100644
--- a/contrib/pg_upgrade/pg_upgrade.c
+++ b/contrib/pg_upgrade/pg_upgrade.c
@@ -149,7 +149,7 @@ setup(char *argv0, bool live_check)
 	 * make sure the user has a clean environment, otherwise, we may confuse
 	 * libpq when we connect to one (or both) of the servers.
 	 */
-	check_for_libpq_envvars();
+	check_pghost_envvar();
 
 	verify_directories();
 
diff --git a/contrib/pg_upgrade/pg_upgrade.h b/contrib/pg_upgrade/pg_upgrade.h
index 04f67e1e34561c968299bdfac7bd4fdb791eb1bf..1f31daecfe9b45a5ad16d29f085fb5d9c7a6e0f3 100644
--- a/contrib/pg_upgrade/pg_upgrade.h
+++ b/contrib/pg_upgrade/pg_upgrade.h
@@ -361,7 +361,7 @@ PGresult   *executeQueryOrDie(PGconn *conn, const char *fmt,...);
 void		start_postmaster(ClusterInfo *cluster);
 void		stop_postmaster(bool fast);
 uint32		get_major_server_version(ClusterInfo *cluster);
-void		check_for_libpq_envvars(void);
+void		check_pghost_envvar(void);
 
 
 /* util.c */
@@ -378,6 +378,7 @@ void	   *pg_malloc(int size);
 void		pg_free(void *ptr);
 const char *getErrorText(int errNum);
 unsigned int str2uint(const char *str);
+void 		pg_putenv(const char *var, const char *val);
 
 
 /* version.c */
diff --git a/contrib/pg_upgrade/server.c b/contrib/pg_upgrade/server.c
index 8fce305d2e20fe483b6295f5f668204c4acc613a..839f39f572fbe7504ce8ac57251e451092d1adea 100644
--- a/contrib/pg_upgrade/server.c
+++ b/contrib/pg_upgrade/server.c
@@ -145,6 +145,7 @@ start_postmaster(ClusterInfo *cluster)
 	char		cmd[MAXPGPATH];
 	PGconn	   *conn;
 	bool		exit_hook_registered = false;
+	int			pg_ctl_return = 0;
 #ifndef WIN32
 	char		*output_filename = log_opts.filename;
 #else
@@ -183,7 +184,11 @@ start_postmaster(ClusterInfo *cluster)
 				"-c autovacuum=off -c autovacuum_freeze_max_age=2000000000",
 			 log_opts.filename);
 
-	exec_prog(true, "%s", cmd);
+	/*
+	 * Don't throw an error right away, let connecting throw the error
+	 * because it might supply a reason for the failure.
+	 */
+	pg_ctl_return = exec_prog(false, "%s", cmd);
 
 	/* Check to see if we can connect to the server; if not, report it. */
 	if ((conn = get_db_conn(cluster, "template1")) == NULL ||
@@ -198,6 +203,11 @@ start_postmaster(ClusterInfo *cluster)
 	}
 	PQfinish(conn);
 
+	/* If the connection didn't fail, fail now */
+	if (pg_ctl_return != 0)
+		pg_log(PG_FATAL, "pg_ctl failed to start the %s server\n",
+				CLUSTER_NAME(cluster));
+	
 	os_info.running_cluster = cluster;
 }
 
@@ -241,20 +251,15 @@ stop_postmaster(bool fast)
 
 
 /*
- * check_for_libpq_envvars()
+ * check_pghost_envvar()
  *
- * tests whether any libpq environment variables are set.
- * Since pg_upgrade connects to both the old and the new server,
- * it is potentially dangerous to have any of these set.
- *
- * If any are found, will log them and cancel.
+ * Tests that PGHOST does not point to a non-local server
  */
 void
-check_for_libpq_envvars(void)
+check_pghost_envvar(void)
 {
 	PQconninfoOption *option;
 	PQconninfoOption *start;
-	bool		found = false;
 
 	/* Get valid libpq env vars from the PQconndefaults function */
 
@@ -262,29 +267,21 @@ check_for_libpq_envvars(void)
 
 	for (option = start; option->keyword != NULL; option++)
 	{
-		if (option->envvar)
+		if (option->envvar && (strcmp(option->envvar, "PGHOST") == 0 ||
+			strcmp(option->envvar, "PGHOSTADDR") == 0))
 		{
-			const char *value;
-
-			/* This allows us to see error messages in the local encoding */
-			if (strcmp(option->envvar, "PGCLIENTENCODING") == 0)
-				continue;
-
-			value = getenv(option->envvar);
-			if (value && strlen(value) > 0)
-			{
-				found = true;
-
-				pg_log(PG_WARNING,
-					   "libpq env var %-20s is currently set to: %s\n", option->envvar, value);
-			}
+			const char *value = getenv(option->envvar);
+
+			if (value && strlen(value) > 0 &&
+				/* check for 'local' host values */
+				(strcmp(value, "localhost") != 0 && strcmp(value, "127.0.0.1") != 0 &&
+				 strcmp(value, "::1") != 0 && value[0] != '/'))
+				pg_log(PG_FATAL,
+					"libpq environment variable %s has a non-local server value: %s\n",
+					option->envvar, value);
 		}
 	}
 
 	/* Free the memory that libpq allocated on our behalf */
 	PQconninfoFree(start);
-
-	if (found)
-		pg_log(PG_FATAL,
-			   "libpq env vars have been found and listed above, please unset them for pg_upgrade\n");
 }
diff --git a/contrib/pg_upgrade/util.c b/contrib/pg_upgrade/util.c
index 9b0bf0f82a7380dc7188bf5d5b198dccd06c5c28..4094895f46c28632f0672679aea6ca41d3859996 100644
--- a/contrib/pg_upgrade/util.c
+++ b/contrib/pg_upgrade/util.c
@@ -244,3 +244,41 @@ str2uint(const char *str)
 {
 	return strtoul(str, NULL, 10);
 }
+
+
+/*
+ *	pg_putenv()
+ *
+ *	This is like putenv(), but takes two arguments.
+ *	It also does unsetenv() if val is NULL.
+ */
+void
+pg_putenv(const char *var, const char *val)
+{
+	if (val)
+	{
+#ifndef WIN32
+		char	   *envstr = (char *) pg_malloc(strlen(var) +
+												strlen(val) + 2);
+
+		sprintf(envstr, "%s=%s", var, val);
+		putenv(envstr);
+
+		/*
+		 * Do not free envstr because it becomes part of the environment on
+		 * some operating systems.	See port/unsetenv.c::unsetenv.
+		 */
+#else
+		SetEnvironmentVariableA(var, val);
+#endif
+	}
+	else
+	{
+#ifndef WIN32
+		unsetenv(var);
+#else
+		SetEnvironmentVariableA(var, "");
+#endif
+	}
+}
+
diff --git a/doc/src/sgml/pgupgrade.sgml b/doc/src/sgml/pgupgrade.sgml
index 1713bf0272913a70f959b7ed9db334405769252e..8bd5f74178e8d70e3b60093900c1223796b92257 100644
--- a/doc/src/sgml/pgupgrade.sgml
+++ b/doc/src/sgml/pgupgrade.sgml
@@ -58,14 +58,16 @@
 
      <varlistentry>
       <term><option>-b</option> <replaceable>old_bindir</></term>
-      <term><option>--old-bindir=</option><replaceable>OLDBINDIR</></term>
-      <listitem><para>specify the old cluster executable directory</para></listitem>
+      <term><option>--old-bindir=</option><replaceable>old_bindir</></term>
+      <listitem><para>the old cluster executable directory;
+      environment variable <envar>OLDBINDIR</></para></listitem>
      </varlistentry>
 
      <varlistentry>
       <term><option>-B</option> <replaceable>new_bindir</></term>
-      <term><option>--new-bindir=</option><replaceable>NEWBINDIR</></term>
-      <listitem><para>specify the new cluster executable directory</para></listitem>
+      <term><option>--new-bindir=</option><replaceable>new_bindir</></term>
+      <listitem><para>the new cluster executable directory;
+      environment variable <envar>NEWBINDIR</></para></listitem>
      </varlistentry>
 
      <varlistentry>
@@ -76,14 +78,16 @@
 
      <varlistentry>
       <term><option>-d</option> <replaceable>old_datadir</></term>
-      <term><option>--old-datadir=</option><replaceable>OLDDATADIR</></term>
-      <listitem><para>specify the old cluster data directory</para></listitem>
+      <term><option>--old-datadir=</option><replaceable>old_datadir</></term>
+      <listitem><para>the old cluster data directory; environment
+      variable <envar>OLDDATADIR</></para></listitem>
      </varlistentry>
 
      <varlistentry>
       <term><option>-D</option> <replaceable>new_datadir</></term>
-      <term><option>--new-datadir=</option><replaceable>NEWDATADIR</></term>
-      <listitem><para>specify the new cluster data directory</para></listitem>
+      <term><option>--new-datadir=</option><replaceable>new_datadir</></term>
+      <listitem><para>the new cluster data directory; environment
+      variable <envar>NEWDATADIR</></para></listitem>
      </varlistentry>
 
      <varlistentry>
@@ -94,7 +98,7 @@
 
      <varlistentry>
       <term><option>-G</option> <replaceable>debug_filename</></term>
-      <term><option>--debugfile=</option><replaceable>DEBUGFILENAME</></term>
+      <term><option>--debugfile=</option><replaceable>debug_filename</></term>
       <listitem><para>output debugging activity to file</para></listitem>
      </varlistentry>
 
@@ -106,26 +110,29 @@
 
      <varlistentry>
       <term><option>-l</option> <replaceable>log_filename</></term>
-      <term><option>--logfile=</option><replaceable>LOGFILENAME</></term>
+      <term><option>--logfile=</option><replaceable>log_filename</></term>
       <listitem><para>log session activity to file</para></listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><option>-p</option> <replaceable>old_portnum</></term>
-      <term><option>--old-port=</option><replaceable>portnum</></term>
-      <listitem><para>specify the old cluster port number</para></listitem>
+      <term><option>-p</option> <replaceable>old_port_number</></term>
+      <term><option>--old-port=</option><replaceable>old_portnum</></term>
+      <listitem><para>the old cluster port number; environment
+      variable <envar>PGPORT</></para></listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><option>-P</option> <replaceable>new_portnum</></term>
-      <term><option>--new-port=</option><replaceable>portnum</></term>
-      <listitem><para>specify the new cluster port number</para></listitem>
+      <term><option>-P</option> <replaceable>new_port_number</></term>
+      <term><option>--new-port=</option><replaceable>new_portnum</></term>
+      <listitem><para>the new cluster port number; environment
+      variable <envar>PGPORT</></para></listitem>
      </varlistentry>
 
      <varlistentry>
-      <term><option>-u</option> <replaceable>username</></term>
-      <term><option>--user=</option><replaceable>username</></term>
-      <listitem><para>clusters superuser</para></listitem>
+      <term><option>-u</option> <replaceable>user_name</></term>
+      <term><option>--user=</option><replaceable>user_name</></term>
+      <listitem><para>cluster's super user name; environment
+      variable <envar>PGUSER</></para></listitem>
      </varlistentry>
 
      <varlistentry>