diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index cfdb33a1fd6508aac31b0a87b8bb3ed73432c618..679c40a64ec64fbc0623e878954287a5e6081dc4 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -838,7 +838,7 @@ omicron         bryanh                  guest1
     <varname>unix_socket_permissions</varname> (and possibly
     <varname>unix_socket_group</varname>) configuration parameters as
     described in <xref linkend="runtime-config-connection">.  Or you
-    could set the <varname>unix_socket_directory</varname>
+    could set the <varname>unix_socket_directories</varname>
     configuration parameter to place the socket file in a suitably
     restricted directory.
    </para>
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index e30c5a0cf4e8f1d31c93907bd6894a8b38189005..7727ea7f89cf76e9fe7dbd0ced57f18dde387278 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -445,17 +445,24 @@ SET ENABLE_SEQSCAN TO OFF;
       </listitem>
      </varlistentry>
 
-     <varlistentry id="guc-unix-socket-directory" xreflabel="unix_socket_directory">
-      <term><varname>unix_socket_directory</varname> (<type>string</type>)</term>
+     <varlistentry id="guc-unix-socket-directories" xreflabel="unix_socket_directories">
+      <term><varname>unix_socket_directories</varname> (<type>string</type>)</term>
       <indexterm>
-       <primary><varname>unix_socket_directory</> configuration parameter</primary>
+       <primary><varname>unix_socket_directories</> configuration parameter</primary>
       </indexterm>
       <listitem>
        <para>
-        Specifies the directory of the Unix-domain socket on which the
-        server is to listen for
-        connections from client applications.  The default is normally
-        <filename>/tmp</filename>, but can be changed at build time.
+        Specifies the directory of the Unix-domain socket(s) on which the
+        server is to listen for connections from client applications.
+        Multiple sockets can be created by listing multiple directories
+        separated by commas.  Whitespace between entries is
+        ignored; surround a directory name with double quotes if you need
+        to include whitespace or commas in the name.
+        An empty value
+        specifies not listening on any Unix-domain sockets, in which case
+        only TCP/IP sockets can be used to connect to the server.
+        The default value is normally
+        <filename>/tmp</filename>, but that can be changed at build time.
         This parameter can only be set at server start.
        </para>
 
@@ -464,8 +471,8 @@ SET ENABLE_SEQSCAN TO OFF;
         <literal>.s.PGSQL.<replaceable>nnnn</></literal> where
         <replaceable>nnnn</> is the server's port number, an ordinary file
         named <literal>.s.PGSQL.<replaceable>nnnn</>.lock</literal> will be
-        created in the <varname>unix_socket_directory</> directory.  Neither
-        file should ever be removed manually.
+        created in each of the <varname>unix_socket_directories</> directories.
+        Neither file should ever be removed manually.
        </para>
 
        <para>
@@ -482,8 +489,8 @@ SET ENABLE_SEQSCAN TO OFF;
       </indexterm>
       <listitem>
        <para>
-        Sets the owning group of the Unix-domain socket.  (The owning
-        user of the socket is always the user that starts the
+        Sets the owning group of the Unix-domain socket(s).  (The owning
+        user of the sockets is always the user that starts the
         server.)  In combination with the parameter
         <varname>unix_socket_permissions</varname> this can be used as
         an additional access control mechanism for Unix-domain connections.
@@ -506,7 +513,7 @@ SET ENABLE_SEQSCAN TO OFF;
       </indexterm>
       <listitem>
        <para>
-        Sets the access permissions of the Unix-domain socket.  Unix-domain
+        Sets the access permissions of the Unix-domain socket(s).  Unix-domain
         sockets use the usual Unix file system permission set.
         The parameter value is expected to be a numeric mode
         specified in the format accepted by the
@@ -1852,7 +1859,7 @@ SET ENABLE_SEQSCAN TO OFF;
         <varname>commit_delay</varname> behaved differently and was much
         less effective: it affected only commits, rather than all WAL flushes,
         and waited for the entire configured delay even if the WAL flush
-        was completed sooner.  Beginning in <productname>PostgreSQL</> 9.3, 
+        was completed sooner.  Beginning in <productname>PostgreSQL</> 9.3,
         the first process that becomes ready to flush waits for the configured
         interval, while subsequent processes wait only until the leader
         completes the flush.  The default <varname>commit_delay</> is zero
@@ -6556,7 +6563,7 @@ LOG:  CleanUpLock: deleting: lock(0xb7acd844) id(24688,24696,0,0,0,1)
        </row>
        <row>
         <entry><option>-k <replaceable>x</replaceable></option></entry>
-        <entry><literal>unix_socket_directory = <replaceable>x</replaceable></></entry>
+        <entry><literal>unix_socket_directories = <replaceable>x</replaceable></></entry>
        </row>
        <row>
         <entry><option>-l</option></entry>
diff --git a/doc/src/sgml/ref/postgres-ref.sgml b/doc/src/sgml/ref/postgres-ref.sgml
index 4e5cd02589c8ebdcdfb68afdb06a492f24e06de6..a1f36e1836cc040ff2e862da0696429e766d81f5 100644
--- a/doc/src/sgml/ref/postgres-ref.sgml
+++ b/doc/src/sgml/ref/postgres-ref.sgml
@@ -254,8 +254,14 @@ PostgreSQL documentation
        <para>
         Specifies the directory of the Unix-domain socket on which
         <command>postgres</command> is to listen for
-        connections from client applications.  The default is normally
-        <filename>/tmp</filename>, but can be changed at build time.
+        connections from client applications.  The value can also be a
+        comma-separated list of directories.  An empty value
+        specifies not listening on any Unix-domain sockets, in which case
+        only TCP/IP sockets can be used to connect to the server.
+        The default value is normally
+        <filename>/tmp</filename>, but that can be changed at build time.
+        Specifying this option is equivalent to setting the <xref
+        linkend="guc-unix-socket-directories"> configuration parameter.
        </para>
       </listitem>
      </varlistentry>
diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml
index 87177989d76f2140ccd34175472c87869b66c320..9cc9d4260a9f69063b14034a6c4a88f740a007d3 100644
--- a/doc/src/sgml/runtime.sgml
+++ b/doc/src/sgml/runtime.sgml
@@ -1718,7 +1718,7 @@ pg_dumpall -p 5432 | psql -d postgres -p 5433
   <para>
    The simplest way to prevent spoofing for <literal>local</>
    connections is to use a Unix domain socket directory (<xref
-   linkend="guc-unix-socket-directory">) that has write permission only
+   linkend="guc-unix-socket-directories">) that has write permission only
    for a trusted local user.  This prevents a malicious user from creating
    their own socket file in that directory.  If you are concerned that
    some applications might still reference <filename>/tmp</> for the
diff --git a/src/backend/libpq/pqcomm.c b/src/backend/libpq/pqcomm.c
index 5272811cc0af190803666cd32dc7ac7167c12c77..5e86987f221fee01c5049f925492e0f9c441d372 100644
--- a/src/backend/libpq/pqcomm.c
+++ b/src/backend/libpq/pqcomm.c
@@ -42,7 +42,7 @@
  *		StreamServerPort	- Open postmaster's server port
  *		StreamConnection	- Create new connection with client
  *		StreamClose			- Close a client/backend connection
- *		TouchSocketFile		- Protect socket file against /tmp cleaners
+ *		TouchSocketFiles	- Protect socket files against /tmp cleaners
  *		pq_init			- initialize libpq at backend startup
  *		pq_comm_reset	- reset libpq during error recovery
  *		pq_close		- shutdown libpq at backend exit
@@ -103,8 +103,8 @@ int			Unix_socket_permissions;
 char	   *Unix_socket_group;
 
 
-/* Where the Unix socket file is */
-static char sock_path[MAXPGPATH];
+/* Where the Unix socket files are (list of palloc'd strings) */
+static List *sock_paths = NIL;
 
 
 /*
@@ -140,8 +140,8 @@ static int	internal_flush(void);
 static void pq_set_nonblocking(bool nonblocking);
 
 #ifdef HAVE_UNIX_SOCKETS
-static int	Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName);
-static int	Setup_AF_UNIX(void);
+static int	Lock_AF_UNIX(char *unixSocketDir, char *unixSocketPath);
+static int	Setup_AF_UNIX(char *sock_path);
 #endif   /* HAVE_UNIX_SOCKETS */
 
 
@@ -234,29 +234,43 @@ pq_close(int code, Datum arg)
 
 /* StreamDoUnlink()
  * Shutdown routine for backend connection
- * If a Unix socket is used for communication, explicitly close it.
+ * If any Unix sockets are used for communication, explicitly close them.
  */
 #ifdef HAVE_UNIX_SOCKETS
 static void
 StreamDoUnlink(int code, Datum arg)
 {
-	Assert(sock_path[0]);
-	unlink(sock_path);
+	ListCell   *l;
+
+	/* Loop through all created sockets... */
+	foreach(l, sock_paths)
+	{
+		char	   *sock_path = (char *) lfirst(l);
+
+		unlink(sock_path);
+	}
+	/* Since we're about to exit, no need to reclaim storage */
+	sock_paths = NIL;
 }
 #endif   /* HAVE_UNIX_SOCKETS */
 
 /*
  * StreamServerPort -- open a "listening" port to accept connections.
  *
- * Successfully opened sockets are added to the ListenSocket[] array,
- * at the first position that isn't PGINVALID_SOCKET.
+ * family should be AF_UNIX or AF_UNSPEC; portNumber is the port number.
+ * For AF_UNIX ports, hostName should be NULL and unixSocketDir must be
+ * specified.  For TCP ports, hostName is either NULL for all interfaces or
+ * the interface to listen on, and unixSocketDir is ignored (can be NULL).
+ *
+ * Successfully opened sockets are added to the ListenSocket[] array (of
+ * length MaxListen), at the first position that isn't PGINVALID_SOCKET.
  *
  * RETURNS: STATUS_OK or STATUS_ERROR
  */
 
 int
 StreamServerPort(int family, char *hostName, unsigned short portNumber,
-				 char *unixSocketName,
+				 char *unixSocketDir,
 				 pgsocket ListenSocket[], int MaxListen)
 {
 	pgsocket	fd;
@@ -273,6 +287,9 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 	int			listen_index = 0;
 	int			added = 0;
 
+#ifdef HAVE_UNIX_SOCKETS
+	char		unixSocketPath[MAXPGPATH];
+#endif
 #if !defined(WIN32) || defined(IPV6_V6ONLY)
 	int			one = 1;
 #endif
@@ -286,10 +303,14 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 #ifdef HAVE_UNIX_SOCKETS
 	if (family == AF_UNIX)
 	{
-		/* Lock_AF_UNIX will also fill in sock_path. */
-		if (Lock_AF_UNIX(portNumber, unixSocketName) != STATUS_OK)
+		/*
+		 * Create unixSocketPath from portNumber and unixSocketDir and lock
+		 * that file path
+		 */
+		UNIXSOCK_PATH(unixSocketPath, portNumber, unixSocketDir);
+		if (Lock_AF_UNIX(unixSocketDir, unixSocketPath) != STATUS_OK)
 			return STATUS_ERROR;
-		service = sock_path;
+		service = unixSocketPath;
 	}
 	else
 #endif   /* HAVE_UNIX_SOCKETS */
@@ -432,7 +453,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 					 (IS_AF_UNIX(addr->ai_family)) ?
 				  errhint("Is another postmaster already running on port %d?"
 						  " If not, remove socket file \"%s\" and retry.",
-						  (int) portNumber, sock_path) :
+						  (int) portNumber, service) :
 				  errhint("Is another postmaster already running on port %d?"
 						  " If not, wait a few seconds and retry.",
 						  (int) portNumber)));
@@ -443,7 +464,7 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
 #ifdef HAVE_UNIX_SOCKETS
 		if (addr->ai_family == AF_UNIX)
 		{
-			if (Setup_AF_UNIX() != STATUS_OK)
+			if (Setup_AF_UNIX(service) != STATUS_OK)
 			{
 				closesocket(fd);
 				break;
@@ -490,10 +511,8 @@ StreamServerPort(int family, char *hostName, unsigned short portNumber,
  * Lock_AF_UNIX -- configure unix socket file path
  */
 static int
-Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
+Lock_AF_UNIX(char *unixSocketDir, char *unixSocketPath)
 {
-	UNIXSOCK_PATH(sock_path, portNumber, unixSocketName);
-
 	/*
 	 * Grab an interlock file associated with the socket file.
 	 *
@@ -502,13 +521,23 @@ Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
 	 * more portable, and second, it lets us remove any pre-existing socket
 	 * file without race conditions.
 	 */
-	CreateSocketLockFile(sock_path, true);
+	CreateSocketLockFile(unixSocketPath, true, unixSocketDir);
 
 	/*
 	 * Once we have the interlock, we can safely delete any pre-existing
 	 * socket file to avoid failure at bind() time.
 	 */
-	unlink(sock_path);
+	unlink(unixSocketPath);
+
+	/*
+	 * Arrange to unlink the socket file(s) at proc_exit.  If this is the
+	 * first one, set up the on_proc_exit function to do it; then add this
+	 * socket file to the list of files to unlink.
+	 */
+	if (sock_paths == NIL)
+		on_proc_exit(StreamDoUnlink, 0);
+
+	sock_paths = lappend(sock_paths, pstrdup(unixSocketPath));
 
 	return STATUS_OK;
 }
@@ -518,11 +547,8 @@ Lock_AF_UNIX(unsigned short portNumber, char *unixSocketName)
  * Setup_AF_UNIX -- configure unix socket permissions
  */
 static int
-Setup_AF_UNIX(void)
+Setup_AF_UNIX(char *sock_path)
 {
-	/* Arrange to unlink the socket file at exit */
-	on_proc_exit(StreamDoUnlink, 0);
-
 	/*
 	 * Fix socket ownership/permission if requested.  Note we must do this
 	 * before we listen() to avoid a window where unwanted connections could
@@ -704,20 +730,24 @@ StreamClose(pgsocket sock)
 }
 
 /*
- * TouchSocketFile -- mark socket file as recently accessed
+ * TouchSocketFiles -- mark socket files as recently accessed
  *
  * This routine should be called every so often to ensure that the socket
- * file has a recent mod date (ordinary operations on sockets usually won't
- * change the mod date).  That saves it from being removed by
+ * files have a recent mod date (ordinary operations on sockets usually won't
+ * change the mod date).  That saves them from being removed by
  * overenthusiastic /tmp-directory-cleaner daemons.  (Another reason we should
  * never have put the socket file in /tmp...)
  */
 void
-TouchSocketFile(void)
+TouchSocketFiles(void)
 {
-	/* Do nothing if we did not create a socket... */
-	if (sock_path[0] != '\0')
+	ListCell   *l;
+
+	/* Loop through all created sockets... */
+	foreach(l, sock_paths)
 	{
+		char	   *sock_path = (char *) lfirst(l);
+
 		/*
 		 * utime() is POSIX standard, utimes() is a common alternative. If we
 		 * have neither, there's no way to affect the mod or access time of
diff --git a/src/backend/postmaster/postmaster.c b/src/backend/postmaster/postmaster.c
index fee699ea9ef2da504c5ce2b4d9b2284ea37eef06..9e26fb895f3f6c70eef42f69f4f4f283cb15ba65 100644
--- a/src/backend/postmaster/postmaster.c
+++ b/src/backend/postmaster/postmaster.c
@@ -157,7 +157,9 @@ static Backend *ShmemBackendArray;
 
 /* The socket number we are listening for connections on */
 int			PostPortNumber;
-char	   *UnixSocketDir;
+/* The directory names for Unix socket(s) */
+char	   *Unix_socket_directories;
+/* The TCP listen address(es) */
 char	   *ListenAddresses;
 
 /*
@@ -612,7 +614,7 @@ PostmasterMain(int argc, char *argv[])
 				break;
 
 			case 'k':
-				SetConfigOption("unix_socket_directory", optarg, PGC_POSTMASTER, PGC_S_ARGV);
+				SetConfigOption("unix_socket_directories", optarg, PGC_POSTMASTER, PGC_S_ARGV);
 				break;
 
 			case 'l':
@@ -856,7 +858,7 @@ PostmasterMain(int argc, char *argv[])
 		/* Need a modifiable copy of ListenAddresses */
 		rawstring = pstrdup(ListenAddresses);
 
-		/* Parse string into list of identifiers */
+		/* Parse string into list of hostnames */
 		if (!SplitIdentifierString(rawstring, ',', &elemlist))
 		{
 			/* syntax error in list */
@@ -872,12 +874,12 @@ PostmasterMain(int argc, char *argv[])
 			if (strcmp(curhost, "*") == 0)
 				status = StreamServerPort(AF_UNSPEC, NULL,
 										  (unsigned short) PostPortNumber,
-										  UnixSocketDir,
+										  NULL,
 										  ListenSocket, MAXLISTEN);
 			else
 				status = StreamServerPort(AF_UNSPEC, curhost,
 										  (unsigned short) PostPortNumber,
-										  UnixSocketDir,
+										  NULL,
 										  ListenSocket, MAXLISTEN);
 
 			if (status == STATUS_OK)
@@ -896,7 +898,7 @@ PostmasterMain(int argc, char *argv[])
 								curhost)));
 		}
 
-		if (!success && list_length(elemlist))
+		if (!success && elemlist != NIL)
 			ereport(FATAL,
 					(errmsg("could not create any TCP/IP sockets")));
 
@@ -943,13 +945,54 @@ PostmasterMain(int argc, char *argv[])
 #endif
 
 #ifdef HAVE_UNIX_SOCKETS
-	status = StreamServerPort(AF_UNIX, NULL,
-							  (unsigned short) PostPortNumber,
-							  UnixSocketDir,
-							  ListenSocket, MAXLISTEN);
-	if (status != STATUS_OK)
-		ereport(WARNING,
-				(errmsg("could not create Unix-domain socket")));
+	if (Unix_socket_directories)
+	{
+		char	   *rawstring;
+		List	   *elemlist;
+		ListCell   *l;
+		int			success = 0;
+
+		/* Need a modifiable copy of Unix_socket_directories */
+		rawstring = pstrdup(Unix_socket_directories);
+
+		/* Parse string into list of directories */
+		if (!SplitDirectoriesString(rawstring, ',', &elemlist))
+		{
+			/* syntax error in list */
+			ereport(FATAL,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("invalid list syntax for \"unix_socket_directories\"")));
+		}
+
+		foreach(l, elemlist)
+		{
+			char	   *socketdir = (char *) lfirst(l);
+
+			status = StreamServerPort(AF_UNIX, NULL,
+									  (unsigned short) PostPortNumber,
+									  socketdir,
+									  ListenSocket, MAXLISTEN);
+
+			if (status == STATUS_OK)
+			{
+				success++;
+				/* record the first successful Unix socket in lockfile */
+				if (success == 1)
+					AddToDataDirLockFile(LOCK_FILE_LINE_SOCKET_DIR, socketdir);
+			}
+			else
+				ereport(WARNING,
+						(errmsg("could not create Unix-domain socket in directory \"%s\"",
+								socketdir)));
+		}
+
+		if (!success && elemlist != NIL)
+			ereport(FATAL,
+					(errmsg("could not create any Unix-domain sockets")));
+
+		list_free_deep(elemlist);
+		pfree(rawstring);
+	}
 #endif
 
 	/*
@@ -1439,15 +1482,15 @@ ServerLoop(void)
 		}
 
 		/*
-		 * Touch the socket and lock file every 58 minutes, to ensure that
+		 * Touch Unix socket and lock files every 58 minutes, to ensure that
 		 * they are not removed by overzealous /tmp-cleaning tasks.  We assume
 		 * no one runs cleaners with cutoff times of less than an hour ...
 		 */
 		now = time(NULL);
 		if (now - last_touch_time >= 58 * SECS_PER_MINUTE)
 		{
-			TouchSocketFile();
-			TouchSocketLockFile();
+			TouchSocketFiles();
+			TouchSocketLockFiles();
 			last_touch_time = now;
 		}
 	}
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 37dfa18c1d0b793d096f662597b4b0a2687a1fd6..f1248a851bf90188da8d3a7e8b61ac99bf78ebbd 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -3344,7 +3344,7 @@ process_postgres_switches(int argc, char *argv[], GucContext ctx)
 				break;
 
 			case 'k':
-				SetConfigOption("unix_socket_directory", optarg, ctx, gucsource);
+				SetConfigOption("unix_socket_directories", optarg, ctx, gucsource);
 				break;
 
 			case 'l':
diff --git a/src/backend/utils/adt/varlena.c b/src/backend/utils/adt/varlena.c
index e1b57ba3fc71fa2cb3898503fa380ff4bc21861b..d9e6bc4338b06dd2d2a1a48ffaaeefe32f4f294d 100644
--- a/src/backend/utils/adt/varlena.c
+++ b/src/backend/utils/adt/varlena.c
@@ -2446,6 +2446,116 @@ SplitIdentifierString(char *rawstring, char separator,
 }
 
 
+/*
+ * SplitDirectoriesString --- parse a string containing directory names
+ *
+ * This is similar to SplitIdentifierString, except that the parsing
+ * rules are meant to handle pathnames instead of identifiers: there is
+ * no downcasing, the max length is MAXPGPATH-1, and we apply
+ * canonicalize_path() to each extracted string.  Because of the last,
+ * the returned strings are separately palloc'd rather than being
+ * pointers into rawstring --- but we still scribble on rawstring.
+ *
+ * Inputs:
+ *	rawstring: the input string; must be modifiable!
+ *	separator: the separator punctuation expected between directories
+ *			   (typically ',' or ';').	Whitespace may also appear around
+ *			   directories.
+ * Outputs:
+ *	namelist: filled with a palloc'd list of directory names.
+ *			  Caller should list_free_deep() this even on error return.
+ *
+ * Returns TRUE if okay, FALSE if there is a syntax error in the string.
+ *
+ * Note that an empty string is considered okay here.
+ */
+bool
+SplitDirectoriesString(char *rawstring, char separator,
+					   List **namelist)
+{
+	char	   *nextp = rawstring;
+	bool		done = false;
+
+	*namelist = NIL;
+
+	while (isspace((unsigned char) *nextp))
+		nextp++;				/* skip leading whitespace */
+
+	if (*nextp == '\0')
+		return true;			/* allow empty string */
+
+	/* At the top of the loop, we are at start of a new directory. */
+	do
+	{
+		char	   *curname;
+		char	   *endp;
+
+		if (*nextp == '\"')
+		{
+			/* Quoted name --- collapse quote-quote pairs */
+			curname = nextp + 1;
+			for (;;)
+			{
+				endp = strchr(nextp + 1, '\"');
+				if (endp == NULL)
+					return false;		/* mismatched quotes */
+				if (endp[1] != '\"')
+					break;		/* found end of quoted name */
+				/* Collapse adjacent quotes into one quote, and look again */
+				memmove(endp, endp + 1, strlen(endp));
+				nextp = endp;
+			}
+			/* endp now points at the terminating quote */
+			nextp = endp + 1;
+		}
+		else
+		{
+			/* Unquoted name --- extends to separator or whitespace */
+			curname = nextp;
+			while (*nextp && *nextp != separator &&
+				   !isspace((unsigned char) *nextp))
+				nextp++;
+			endp = nextp;
+			if (curname == nextp)
+				return false;	/* empty unquoted name not allowed */
+		}
+
+		while (isspace((unsigned char) *nextp))
+			nextp++;			/* skip trailing whitespace */
+
+		if (*nextp == separator)
+		{
+			nextp++;
+			while (isspace((unsigned char) *nextp))
+				nextp++;		/* skip leading whitespace for next */
+			/* we expect another name, so done remains false */
+		}
+		else if (*nextp == '\0')
+			done = true;
+		else
+			return false;		/* invalid syntax */
+
+		/* Now safe to overwrite separator with a null */
+		*endp = '\0';
+
+		/* Truncate path if it's overlength */
+		if (strlen(curname) >= MAXPGPATH)
+			curname[MAXPGPATH - 1] = '\0';
+
+		/*
+		 * Finished isolating current name --- add it to list
+		 */
+		curname = pstrdup(curname);
+		canonicalize_path(curname);
+		*namelist = lappend(*namelist, curname);
+
+		/* Loop back if we didn't reach end of string */
+	} while (!done);
+
+	return true;
+}
+
+
 /*****************************************************************************
  *	Comparison Functions used for bytea
  *
diff --git a/src/backend/utils/init/miscinit.c b/src/backend/utils/init/miscinit.c
index fb376a0d271730f2813bb1d1f2e15562e82ff865..775d71f56c57f4c954054653b454270427fc5395 100644
--- a/src/backend/utils/init/miscinit.c
+++ b/src/backend/utils/init/miscinit.c
@@ -49,8 +49,8 @@
 
 ProcessingMode Mode = InitProcessing;
 
-/* Note: we rely on this to initialize as zeroes */
-static char socketLockFile[MAXPGPATH];
+/* List of lock files to be removed at proc exit */
+static List *lock_files = NIL;
 
 
 /* ----------------------------------------------------------------
@@ -628,7 +628,7 @@ GetUserNameFromId(Oid roleid)
  *				Interlock-file support
  *
  * These routines are used to create both a data-directory lockfile
- * ($DATADIR/postmaster.pid) and a Unix-socket-file lockfile ($SOCKFILE.lock).
+ * ($DATADIR/postmaster.pid) and Unix-socket-file lockfiles ($SOCKFILE.lock).
  * Both kinds of files contain the same info initially, although we can add
  * more information to a data-directory lockfile after it's created, using
  * AddToDataDirLockFile().	See miscadmin.h for documentation of the contents
@@ -640,32 +640,35 @@ GetUserNameFromId(Oid roleid)
  */
 
 /*
- * proc_exit callback to remove a lockfile.
+ * proc_exit callback to remove lockfiles.
  */
 static void
-UnlinkLockFile(int status, Datum filename)
+UnlinkLockFiles(int status, Datum arg)
 {
-	char	   *fname = (char *) DatumGetPointer(filename);
+	ListCell   *l;
 
-	if (fname != NULL)
+	foreach(l, lock_files)
 	{
-		if (unlink(fname) != 0)
-		{
-			/* Should we complain if the unlink fails? */
-		}
-		free(fname);
+		char	   *curfile = (char *) lfirst(l);
+
+		unlink(curfile);
+		/* Should we complain if the unlink fails? */
 	}
+	/* Since we're about to exit, no need to reclaim storage */
+	lock_files = NIL;
 }
 
 /*
  * Create a lockfile.
  *
- * filename is the name of the lockfile to create.
+ * filename is the path name of the lockfile to create.
  * amPostmaster is used to determine how to encode the output PID.
+ * socketDir is the Unix socket directory path to include (possibly empty).
  * isDDLock and refName are used to determine what error message to produce.
  */
 static void
 CreateLockFile(const char *filename, bool amPostmaster,
+			   const char *socketDir,
 			   bool isDDLock, const char *refName)
 {
 	int			fd;
@@ -891,12 +894,7 @@ CreateLockFile(const char *filename, bool amPostmaster,
 			 DataDir,
 			 (long) MyStartTime,
 			 PostPortNumber,
-#ifdef HAVE_UNIX_SOCKETS
-			 (*UnixSocketDir != '\0') ? UnixSocketDir : DEFAULT_PGSOCKET_DIR
-#else
-			 ""
-#endif
-		);
+			 socketDir);
 
 	errno = 0;
 	if (write(fd, buffer, strlen(buffer)) != strlen(buffer))
@@ -934,9 +932,14 @@ CreateLockFile(const char *filename, bool amPostmaster,
 	}
 
 	/*
-	 * Arrange for automatic removal of lockfile at proc_exit.
+	 * Arrange to unlink the lock file(s) at proc_exit.  If this is the
+	 * first one, set up the on_proc_exit function to do it; then add this
+	 * lock file to the list of files to unlink.
 	 */
-	on_proc_exit(UnlinkLockFile, PointerGetDatum(strdup(filename)));
+	if (lock_files == NIL)
+		on_proc_exit(UnlinkLockFiles, 0);
+
+	lock_files = lappend(lock_files, pstrdup(filename));
 }
 
 /*
@@ -945,41 +948,50 @@ CreateLockFile(const char *filename, bool amPostmaster,
  * When this is called, we must have already switched the working
  * directory to DataDir, so we can just use a relative path.  This
  * helps ensure that we are locking the directory we should be.
+ *
+ * Note that the socket directory path line is initially written as empty.
+ * postmaster.c will rewrite it upon creating the first Unix socket.
  */
 void
 CreateDataDirLockFile(bool amPostmaster)
 {
-	CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, true, DataDir);
+	CreateLockFile(DIRECTORY_LOCK_FILE, amPostmaster, "", true, DataDir);
 }
 
 /*
  * Create a lockfile for the specified Unix socket file.
  */
 void
-CreateSocketLockFile(const char *socketfile, bool amPostmaster)
+CreateSocketLockFile(const char *socketfile, bool amPostmaster,
+					 const char *socketDir)
 {
 	char		lockfile[MAXPGPATH];
 
 	snprintf(lockfile, sizeof(lockfile), "%s.lock", socketfile);
-	CreateLockFile(lockfile, amPostmaster, false, socketfile);
-	/* Save name of lockfile for TouchSocketLockFile */
-	strcpy(socketLockFile, lockfile);
+	CreateLockFile(lockfile, amPostmaster, socketDir, false, socketfile);
 }
 
 /*
- * TouchSocketLockFile -- mark socket lock file as recently accessed
+ * TouchSocketLockFiles -- mark socket lock files as recently accessed
  *
- * This routine should be called every so often to ensure that the lock file
- * has a recent mod or access date.  That saves it
+ * This routine should be called every so often to ensure that the socket
+ * lock files have a recent mod or access date.  That saves them
  * from being removed by overenthusiastic /tmp-directory-cleaner daemons.
  * (Another reason we should never have put the socket file in /tmp...)
  */
 void
-TouchSocketLockFile(void)
+TouchSocketLockFiles(void)
 {
-	/* Do nothing if we did not create a socket... */
-	if (socketLockFile[0] != '\0')
+	ListCell   *l;
+
+	foreach(l, lock_files)
 	{
+		char	   *socketLockFile = (char *) lfirst(l);
+
+		/* No need to touch the data directory lock file, we trust */
+		if (strcmp(socketLockFile, DIRECTORY_LOCK_FILE) == 0)
+			continue;
+
 		/*
 		 * utime() is POSIX standard, utimes() is a common alternative; if we
 		 * have neither, fall back to actually reading the file (which only
@@ -1011,8 +1023,10 @@ TouchSocketLockFile(void)
  * Add (or replace) a line in the data directory lock file.
  * The given string should not include a trailing newline.
  *
- * Caution: this erases all following lines.  In current usage that is OK
- * because lines are added in order.  We could improve it if needed.
+ * Note: because we don't truncate the file, if we were to rewrite a line
+ * with less data than it had before, there would be garbage after the last
+ * line.  We don't ever actually do that, so not worth adding another kernel
+ * call to cover the possibility.
  */
 void
 AddToDataDirLockFile(int target_line, const char *str)
@@ -1020,8 +1034,10 @@ AddToDataDirLockFile(int target_line, const char *str)
 	int			fd;
 	int			len;
 	int			lineno;
-	char	   *ptr;
-	char		buffer[BLCKSZ];
+	char	   *srcptr;
+	char	   *destptr;
+	char		srcbuffer[BLCKSZ];
+	char		destbuffer[BLCKSZ];
 
 	fd = open(DIRECTORY_LOCK_FILE, O_RDWR | PG_BINARY, 0);
 	if (fd < 0)
@@ -1032,7 +1048,7 @@ AddToDataDirLockFile(int target_line, const char *str)
 						DIRECTORY_LOCK_FILE)));
 		return;
 	}
-	len = read(fd, buffer, sizeof(buffer) - 1);
+	len = read(fd, srcbuffer, sizeof(srcbuffer) - 1);
 	if (len < 0)
 	{
 		ereport(LOG,
@@ -1042,36 +1058,50 @@ AddToDataDirLockFile(int target_line, const char *str)
 		close(fd);
 		return;
 	}
-	buffer[len] = '\0';
+	srcbuffer[len] = '\0';
 
 	/*
-	 * Skip over lines we are not supposed to rewrite.
+	 * Advance over lines we are not supposed to rewrite, then copy them
+	 * to destbuffer.
 	 */
-	ptr = buffer;
+	srcptr = srcbuffer;
 	for (lineno = 1; lineno < target_line; lineno++)
 	{
-		if ((ptr = strchr(ptr, '\n')) == NULL)
+		if ((srcptr = strchr(srcptr, '\n')) == NULL)
 		{
 			elog(LOG, "bogus data in \"%s\"", DIRECTORY_LOCK_FILE);
 			close(fd);
 			return;
 		}
-		ptr++;
+		srcptr++;
 	}
+	memcpy(destbuffer, srcbuffer, srcptr - srcbuffer);
+	destptr = destbuffer + (srcptr - srcbuffer);
 
 	/*
 	 * Write or rewrite the target line.
 	 */
-	snprintf(ptr, buffer + sizeof(buffer) - ptr, "%s\n", str);
+	snprintf(destptr, destbuffer + sizeof(destbuffer) - destptr, "%s\n", str);
+	destptr += strlen(destptr);
+
+	/*
+	 * If there are more lines in the old file, append them to destbuffer.
+	 */
+	if ((srcptr = strchr(srcptr, '\n')) != NULL)
+	{
+		srcptr++;
+		snprintf(destptr, destbuffer + sizeof(destbuffer) - destptr, "%s",
+				 srcptr);
+	}
 
 	/*
 	 * And rewrite the data.  Since we write in a single kernel call, this
 	 * update should appear atomic to onlookers.
 	 */
-	len = strlen(buffer);
+	len = strlen(destbuffer);
 	errno = 0;
 	if (lseek(fd, (off_t) 0, SEEK_SET) != 0 ||
-		(int) write(fd, buffer, len) != len)
+		(int) write(fd, destbuffer, len) != len)
 	{
 		/* if write didn't set errno, assume problem is no disk space */
 		if (errno == 0)
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 087aaf9b0bb4f09c449c346f4ec7524f7c675472..80e5aa1816f272d526543c9c6267e5353cc6f401 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -2895,14 +2895,18 @@ static struct config_string ConfigureNamesString[] =
 	},
 
 	{
-		{"unix_socket_directory", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
-			gettext_noop("Sets the directory where the Unix-domain socket will be created."),
+		{"unix_socket_directories", PGC_POSTMASTER, CONN_AUTH_SETTINGS,
+			gettext_noop("Sets the directories where Unix-domain sockets will be created."),
 			NULL,
 			GUC_SUPERUSER_ONLY
 		},
-		&UnixSocketDir,
+		&Unix_socket_directories,
+#ifdef HAVE_UNIX_SOCKETS
+		DEFAULT_PGSOCKET_DIR,
+#else
 		"",
-		check_canonical_path, NULL, NULL
+#endif
+		NULL, NULL, NULL
 	},
 
 	{
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index fa75d00f70872bbe7b48192861baf89ec236db98..c24afb043436e46fe0d959ec048aa8b0c7784f0e 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -65,7 +65,8 @@
 # Note:  Increasing max_connections costs ~400 bytes of shared memory per
 # connection slot, plus lock space (see max_locks_per_transaction).
 #superuser_reserved_connections = 3	# (change requires restart)
-#unix_socket_directory = ''		# (change requires restart)
+#unix_socket_directories = '/tmp'	# comma-separated list of directories
+					# (change requires restart)
 #unix_socket_group = ''			# (change requires restart)
 #unix_socket_permissions = 0777		# begin with 0 to use octal notation
 					# (change requires restart)
diff --git a/src/bin/initdb/initdb.c b/src/bin/initdb/initdb.c
index 4292231d0d59f3caa31879e6c0b804587dbfa721..132ad0fa4cdb9b59f052f2d97494910c27b1f219 100644
--- a/src/bin/initdb/initdb.c
+++ b/src/bin/initdb/initdb.c
@@ -1156,7 +1156,7 @@ static void
 setup_config(void)
 {
 	char	  **conflines;
-	char		repltok[TZ_STRLEN_MAX + 100];
+	char		repltok[MAXPGPATH];
 	char		path[MAXPGPATH];
 	const char *default_timezone;
 
@@ -1178,6 +1178,15 @@ setup_config(void)
 				 n_buffers * (BLCKSZ / 1024));
 	conflines = replace_token(conflines, "#shared_buffers = 32MB", repltok);
 
+#ifdef HAVE_UNIX_SOCKETS
+	snprintf(repltok, sizeof(repltok), "#unix_socket_directories = '%s'",
+			 DEFAULT_PGSOCKET_DIR);
+#else
+	snprintf(repltok, sizeof(repltok), "#unix_socket_directories = ''");
+#endif
+	conflines = replace_token(conflines, "#unix_socket_directories = '/tmp'",
+							  repltok);
+
 #if DEF_PGPORT != 5432
 	snprintf(repltok, sizeof(repltok), "#port = %d", DEF_PGPORT);
 	conflines = replace_token(conflines, "#port = 5432", repltok);
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 72fc4c1abf6ae80b92aafea727c9f07d36d9efb3..af8d8b28e690ce37aa3a8565bcbe4f3e4ffa62a9 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -521,7 +521,7 @@ test_postmaster_connection(bool do_checkpoint)
 						hostaddr = optlines[LOCK_FILE_LINE_LISTEN_ADDR - 1];
 
 						/*
-						 * While unix_socket_directory can accept relative
+						 * While unix_socket_directories can accept relative
 						 * directories, libpq's host parameter must have a
 						 * leading slash to indicate a socket directory.  So,
 						 * ignore sockdir if it's relative, and try to use TCP
diff --git a/src/include/libpq/libpq.h b/src/include/libpq/libpq.h
index 7083cd866b68ea906ab66b4f59a9e3b6dfd47885..41e52f2a7c68c9b9d4761d1b0e6e925c9cb7637b 100644
--- a/src/include/libpq/libpq.h
+++ b/src/include/libpq/libpq.h
@@ -44,12 +44,12 @@ typedef struct
 /*
  * prototypes for functions in pqcomm.c
  */
-extern int StreamServerPort(int family, char *hostName,
-	unsigned short portNumber, char *unixSocketName, pgsocket ListenSocket[],
-				 int MaxListen);
+extern int	StreamServerPort(int family, char *hostName,
+				 unsigned short portNumber, char *unixSocketDir,
+				 pgsocket ListenSocket[], int MaxListen);
 extern int	StreamConnection(pgsocket server_fd, Port *port);
 extern void StreamClose(pgsocket sock);
-extern void TouchSocketFile(void);
+extern void TouchSocketFiles(void);
 extern void pq_init(void);
 extern void pq_comm_reset(void);
 extern int	pq_getbytes(char *s, size_t len);
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index 8df2a28126a1f24e7a72513fb253da5c3f87773f..9f57989f8056344a239f9da6e99f6f83f717062e 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -411,7 +411,7 @@ extern char *local_preload_libraries_string;
  *		2	data directory path
  *		3	postmaster start timestamp (time_t representation)
  *		4	port number
- *		5	socket directory path (empty on Windows)
+ *		5	first Unix socket directory path (empty if none)
  *		6	first listen_address (IP address or "*"; empty if no TCP port)
  *		7	shared memory key (not present on Windows)
  *
@@ -429,8 +429,9 @@ extern char *local_preload_libraries_string;
 #define LOCK_FILE_LINE_SHMEM_KEY	7
 
 extern void CreateDataDirLockFile(bool amPostmaster);
-extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster);
-extern void TouchSocketLockFile(void);
+extern void CreateSocketLockFile(const char *socketfile, bool amPostmaster,
+					 const char *socketDir);
+extern void TouchSocketLockFiles(void);
 extern void AddToDataDirLockFile(int target_line, const char *str);
 extern void ValidatePgVersion(const char *path);
 extern void process_shared_preload_libraries(void);
diff --git a/src/include/postmaster/postmaster.h b/src/include/postmaster/postmaster.h
index 7d01d3d6248610a0aecee9bcdbd85d4cb54ca8fa..0fe7ec26db7646472fca248dee6982dc5c595c6c 100644
--- a/src/include/postmaster/postmaster.h
+++ b/src/include/postmaster/postmaster.h
@@ -19,7 +19,7 @@ extern int	ReservedBackends;
 extern int	PostPortNumber;
 extern int	Unix_socket_permissions;
 extern char *Unix_socket_group;
-extern char *UnixSocketDir;
+extern char *Unix_socket_directories;
 extern char *ListenAddresses;
 extern bool ClientAuthInProgress;
 extern int	PreAuthDelay;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index f706ba705231e05891b0dc3b3dce1f97bf9e7487..c9c665dae09c08a726c04ed58591d63e3b1b3347 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -756,6 +756,8 @@ extern int	varstr_cmp(char *arg1, int len1, char *arg2, int len2, Oid collid);
 extern List *textToQualifiedNameList(text *textval);
 extern bool SplitIdentifierString(char *rawstring, char separator,
 					  List **namelist);
+extern bool SplitDirectoriesString(char *rawstring, char separator,
+					   List **namelist);
 extern Datum replace_text(PG_FUNCTION_ARGS);
 extern text *replace_text_regexp(text *src_text, void *regexp,
 					text *replace_text, bool glob);