diff --git a/doc/src/sgml/client-auth.sgml b/doc/src/sgml/client-auth.sgml
index 231fc40..f1ecf95 100644
--- a/doc/src/sgml/client-auth.sgml
+++ b/doc/src/sgml/client-auth.sgml
@@ -1589,23 +1589,35 @@ host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
    </para>
 
    <para>
+    Multiple RADIUS servers can be specified, in which case they will
+    be tried sequentially. If a negative response is received from
+    a server, the authentication will fail. If no response is received,
+    the next server in the list will be tried. To specify multiple
+    servers, put the names within quotes and separate the server names
+    with a comma. If multiple servers are specified, all other RADIUS
+    options can also be given as a comma separate list, to apply
+    individual values to each server. They can also be specified as
+    a single value, in which case this value will apply to all servers.
+   </para>
+
+   <para>
     The following configuration options are supported for RADIUS:
      <variablelist>
       <varlistentry>
-       <term><literal>radiusserver</literal></term>
+       <term><literal>radiusservers</literal></term>
        <listitem>
         <para>
-         The name or IP address of the RADIUS server to connect to.
+         The name or IP addresses of the RADIUS servers to connect to.
          This parameter is required.
         </para>
        </listitem>
       </varlistentry>
 
       <varlistentry>
-       <term><literal>radiussecret</literal></term>
+       <term><literal>radiussecrets</literal></term>
        <listitem>
         <para>
-         The shared secret used when talking securely to the RADIUS
+         The shared secrets used when talking securely to the RADIUS
          server. This must have exactly the same value on the PostgreSQL
          and RADIUS servers. It is recommended that this be a string of
          at least 16 characters. This parameter is required.
@@ -1623,17 +1635,17 @@ host ... ldap ldapurl="ldap://ldap.example.net/dc=example,dc=net?uid?sub"
       </varlistentry>
 
       <varlistentry>
-       <term><literal>radiusport</literal></term>
+       <term><literal>radiusports</literal></term>
        <listitem>
         <para>
-         The port number on the RADIUS server to connect to. If no port
+         The port number on the RADIUS servers to connect to. If no port
          is specified, the default port <literal>1812</> will be used.
         </para>
        </listitem>
       </varlistentry>
 
       <varlistentry>
-       <term><literal>radiusidentifier</literal></term>
+       <term><literal>radiusidentifiers</literal></term>
        <listitem>
         <para>
          The string used as <literal>NAS Identifier</> in the RADIUS
diff --git a/src/backend/libpq/auth.c b/src/backend/libpq/auth.c
index 824e408..e033b24 100644
--- a/src/backend/libpq/auth.c
+++ b/src/backend/libpq/auth.c
@@ -195,6 +195,7 @@ static int pg_SSPI_make_upn(char *accountname,
  *----------------------------------------------------------------
  */
 static int	CheckRADIUSAuth(Port *port);
+static int	PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd);
 
 
 /*
@@ -2455,7 +2456,97 @@ static int
 CheckRADIUSAuth(Port *port)
 {
 	char	   *passwd;
-	char	   *identifier = "postgresql";
+	ListCell   *server,
+			   *secrets,
+			   *radiusports,
+			   *identifiers;
+
+	/* Make sure struct alignment is correct */
+	Assert(offsetof(radius_packet, vector) == 4);
+
+	/* Verify parameters */
+	if (list_length(port->hba->radiusservers) < 1)
+	{
+		ereport(LOG,
+				(errmsg("RADIUS server not specified")));
+		return STATUS_ERROR;
+	}
+
+	if (list_length(port->hba->radiussecrets) < 1)
+	{
+		ereport(LOG,
+				(errmsg("RADIUS secret not specified")));
+		return STATUS_ERROR;
+	}
+
+	/* Send regular password request to client, and get the response */
+	sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
+
+	passwd = recv_password_packet(port);
+	if (passwd == NULL)
+		return STATUS_EOF;		/* client wouldn't send password */
+
+	if (strlen(passwd) == 0)
+	{
+		ereport(LOG,
+				(errmsg("empty password returned by client")));
+		return STATUS_ERROR;
+	}
+
+	if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
+	{
+		ereport(LOG,
+				(errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
+		return STATUS_ERROR;
+	}
+
+	/*
+	 * Loop over and try each server in order.
+	 */
+	secrets = list_head(port->hba->radiussecrets);
+	radiusports = list_head(port->hba->radiusports);
+	identifiers = list_head(port->hba->radiusidentifiers);
+	foreach(server, port->hba->radiusservers)
+	{
+		int			ret = PerformRadiusTransaction(lfirst(server),
+												   lfirst(secrets),
+									radiusports ? lfirst(radiusports) : NULL,
+									identifiers ? lfirst(identifiers) : NULL,
+												   port->user_name,
+												   passwd);
+
+		/*------
+		 * STATUS_OK = Login OK
+		 * STATUS_ERROR = Login not OK, but try next server
+		 * STATUS_EOF = Login not OK, and don't try next server
+		 *------
+		 */
+		if (ret == STATUS_OK)
+			return STATUS_OK;
+		else if (ret == STATUS_EOF)
+			return STATUS_ERROR;
+
+		/*
+		 * secret, port and identifiers either have length 0 (use default),
+		 * length 1 (use the same everywhere) or the same length as servers.
+		 * So if the length is >1, we advance one step. In other cases, we
+		 * don't and will then reuse the correct value.
+		 */
+		if (list_length(port->hba->radiussecrets) > 1)
+			secrets = lnext(secrets);
+		if (list_length(port->hba->radiusports) > 1)
+			radiusports = lnext(radiusports);
+		if (list_length(port->hba->radiusidentifiers) > 1)
+			identifiers = lnext(identifiers);
+	}
+
+	/* No servers left to try, so give up */
+	return STATUS_ERROR;
+}
+
+static int
+PerformRadiusTransaction(char *server, char *secret, char *portstr, char *identifier, char *user_name, char *passwd)
+{
 	char		radius_buffer[RADIUS_BUFFER_SIZE];
 	char		receive_buffer[RADIUS_BUFFER_SIZE];
 	radius_packet *packet = (radius_packet *) radius_buffer;
@@ -2477,7 +2568,7 @@ CheckRADIUSAuth(Port *port)
 #endif
 	struct addrinfo hint;
 	struct addrinfo *serveraddrs;
-	char		portstr[128];
+	int			port;
 	ACCEPT_TYPE_ARG3 addrsize;
 	fd_set		fdset;
 	struct timeval endtime;
@@ -2485,69 +2576,29 @@ CheckRADIUSAuth(Port *port)
 				j,
 				r;
 
-	/* Make sure struct alignment is correct */
-	Assert(offsetof(radius_packet, vector) == 4);
-
-	/* Verify parameters */
-	if (!port->hba->radiusserver || port->hba->radiusserver[0] == '\0')
-	{
-		ereport(LOG,
-				(errmsg("RADIUS server not specified")));
-		return STATUS_ERROR;
-	}
-
-	if (!port->hba->radiussecret || port->hba->radiussecret[0] == '\0')
-	{
-		ereport(LOG,
-				(errmsg("RADIUS secret not specified")));
-		return STATUS_ERROR;
-	}
-
-	if (port->hba->radiusport == 0)
-		port->hba->radiusport = 1812;
+	/* Assign default values */
+	if (portstr == NULL)
+		portstr = "1812";
+	if (identifier == NULL)
+		identifier = "postgresql";
 
 	MemSet(&hint, 0, sizeof(hint));
 	hint.ai_socktype = SOCK_DGRAM;
 	hint.ai_family = AF_UNSPEC;
-	snprintf(portstr, sizeof(portstr), "%d", port->hba->radiusport);
+	port = atoi(portstr);
 
-	r = pg_getaddrinfo_all(port->hba->radiusserver, portstr, &hint, &serveraddrs);
+	r = pg_getaddrinfo_all(server, portstr, &hint, &serveraddrs);
 	if (r || !serveraddrs)
 	{
 		ereport(LOG,
 				(errmsg("could not translate RADIUS server name \"%s\" to address: %s",
-						port->hba->radiusserver, gai_strerror(r))));
+						server, gai_strerror(r))));
 		if (serveraddrs)
 			pg_freeaddrinfo_all(hint.ai_family, serveraddrs);
 		return STATUS_ERROR;
 	}
 	/* XXX: add support for multiple returned addresses? */
 
-	if (port->hba->radiusidentifier && port->hba->radiusidentifier[0])
-		identifier = port->hba->radiusidentifier;
-
-	/* Send regular password request to client, and get the response */
-	sendAuthRequest(port, AUTH_REQ_PASSWORD, NULL, 0);
-
-	passwd = recv_password_packet(port);
-	if (passwd == NULL)
-		return STATUS_EOF;		/* client wouldn't send password */
-
-	if (strlen(passwd) == 0)
-	{
-		ereport(LOG,
-				(errmsg("empty password returned by client")));
-		return STATUS_ERROR;
-	}
-
-	if (strlen(passwd) > RADIUS_MAX_PASSWORD_LENGTH)
-	{
-		ereport(LOG,
-				(errmsg("RADIUS authentication does not support passwords longer than %d characters", RADIUS_MAX_PASSWORD_LENGTH)));
-		return STATUS_ERROR;
-	}
-
-
 	/* Construct RADIUS packet */
 	packet->code = RADIUS_ACCESS_REQUEST;
 	packet->length = RADIUS_HEADER_LENGTH;
@@ -2559,7 +2610,7 @@ CheckRADIUSAuth(Port *port)
 	}
 	packet->id = packet->vector[0];
 	radius_add_attribute(packet, RADIUS_SERVICE_TYPE, (unsigned char *) &service, sizeof(service));
-	radius_add_attribute(packet, RADIUS_USER_NAME, (unsigned char *) port->user_name, strlen(port->user_name));
+	radius_add_attribute(packet, RADIUS_USER_NAME, (unsigned char *) user_name, strlen(user_name));
 	radius_add_attribute(packet, RADIUS_NAS_IDENTIFIER, (unsigned char *) identifier, strlen(identifier));
 
 	/*
@@ -2569,14 +2620,14 @@ CheckRADIUSAuth(Port *port)
 	 * (if necessary)
 	 */
 	encryptedpasswordlen = ((strlen(passwd) + RADIUS_VECTOR_LENGTH - 1) / RADIUS_VECTOR_LENGTH) * RADIUS_VECTOR_LENGTH;
-	cryptvector = palloc(strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH);
-	memcpy(cryptvector, port->hba->radiussecret, strlen(port->hba->radiussecret));
+	cryptvector = palloc(strlen(secret) + RADIUS_VECTOR_LENGTH);
+	memcpy(cryptvector, secret, strlen(secret));
 
 	/* for the first iteration, we use the Request Authenticator vector */
 	md5trailer = packet->vector;
 	for (i = 0; i < encryptedpasswordlen; i += RADIUS_VECTOR_LENGTH)
 	{
-		memcpy(cryptvector + strlen(port->hba->radiussecret), md5trailer, RADIUS_VECTOR_LENGTH);
+		memcpy(cryptvector + strlen(secret), md5trailer, RADIUS_VECTOR_LENGTH);
 
 		/*
 		 * .. and for subsequent iterations the result of the previous XOR
@@ -2584,7 +2635,7 @@ CheckRADIUSAuth(Port *port)
 		 */
 		md5trailer = encryptedpassword + i;
 
-		if (!pg_md5_binary(cryptvector, strlen(port->hba->radiussecret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
+		if (!pg_md5_binary(cryptvector, strlen(secret) + RADIUS_VECTOR_LENGTH, encryptedpassword + i))
 		{
 			ereport(LOG,
 					(errmsg("could not perform MD5 encryption of password")));
@@ -2676,7 +2727,8 @@ CheckRADIUSAuth(Port *port)
 		if (timeoutval <= 0)
 		{
 			ereport(LOG,
-					(errmsg("timeout waiting for RADIUS response")));
+					(errmsg("timeout waiting for RADIUS response from %s",
+							server)));
 			closesocket(sock);
 			return STATUS_ERROR;
 		}
@@ -2701,7 +2753,8 @@ CheckRADIUSAuth(Port *port)
 		if (r == 0)
 		{
 			ereport(LOG,
-					(errmsg("timeout waiting for RADIUS response")));
+					(errmsg("timeout waiting for RADIUS response from %s",
+							server)));
 			closesocket(sock);
 			return STATUS_ERROR;
 		}
@@ -2728,19 +2781,19 @@ CheckRADIUSAuth(Port *port)
 		}
 
 #ifdef HAVE_IPV6
-		if (remoteaddr.sin6_port != htons(port->hba->radiusport))
+		if (remoteaddr.sin6_port != htons(port))
 #else
-		if (remoteaddr.sin_port != htons(port->hba->radiusport))
+		if (remoteaddr.sin_port != htons(port))
 #endif
 		{
 #ifdef HAVE_IPV6
 			ereport(LOG,
-				  (errmsg("RADIUS response was sent from incorrect port: %d",
-						  ntohs(remoteaddr.sin6_port))));
+					(errmsg("RADIUS response from %s was sent from incorrect port: %d",
+							server, ntohs(remoteaddr.sin6_port))));
 #else
 			ereport(LOG,
-				  (errmsg("RADIUS response was sent from incorrect port: %d",
-						  ntohs(remoteaddr.sin_port))));
+					(errmsg("RADIUS response from %s was sent from incorrect port: %d",
+							server, ntohs(remoteaddr.sin_port))));
 #endif
 			continue;
 		}
@@ -2748,23 +2801,23 @@ CheckRADIUSAuth(Port *port)
 		if (packetlength < RADIUS_HEADER_LENGTH)
 		{
 			ereport(LOG,
-					(errmsg("RADIUS response too short: %d", packetlength)));
+					(errmsg("RADIUS response from %s too short: %d", server, packetlength)));
 			continue;
 		}
 
 		if (packetlength != ntohs(receivepacket->length))
 		{
 			ereport(LOG,
-					(errmsg("RADIUS response has corrupt length: %d (actual length %d)",
-							ntohs(receivepacket->length), packetlength)));
+					(errmsg("RADIUS response from %s has corrupt length: %d (actual length %d)",
+					   server, ntohs(receivepacket->length), packetlength)));
 			continue;
 		}
 
 		if (packet->id != receivepacket->id)
 		{
 			ereport(LOG,
-					(errmsg("RADIUS response is to a different request: %d (should be %d)",
-							receivepacket->id, packet->id)));
+					(errmsg("RADIUS response from %s is to a different request: %d (should be %d)",
+							server, receivepacket->id, packet->id)));
 			continue;
 		}
 
@@ -2772,7 +2825,7 @@ CheckRADIUSAuth(Port *port)
 		 * Verify the response authenticator, which is calculated as
 		 * MD5(Code+ID+Length+RequestAuthenticator+Attributes+Secret)
 		 */
-		cryptvector = palloc(packetlength + strlen(port->hba->radiussecret));
+		cryptvector = palloc(packetlength + strlen(secret));
 
 		memcpy(cryptvector, receivepacket, 4);	/* code+id+length */
 		memcpy(cryptvector + 4, packet->vector, RADIUS_VECTOR_LENGTH);	/* request
@@ -2781,10 +2834,10 @@ CheckRADIUSAuth(Port *port)
 		if (packetlength > RADIUS_HEADER_LENGTH)		/* there may be no
 														 * attributes at all */
 			memcpy(cryptvector + RADIUS_HEADER_LENGTH, receive_buffer + RADIUS_HEADER_LENGTH, packetlength - RADIUS_HEADER_LENGTH);
-		memcpy(cryptvector + packetlength, port->hba->radiussecret, strlen(port->hba->radiussecret));
+		memcpy(cryptvector + packetlength, secret, strlen(secret));
 
 		if (!pg_md5_binary(cryptvector,
-						   packetlength + strlen(port->hba->radiussecret),
+						   packetlength + strlen(secret),
 						   encryptedpassword))
 		{
 			ereport(LOG,
@@ -2797,7 +2850,8 @@ CheckRADIUSAuth(Port *port)
 		if (memcmp(receivepacket->vector, encryptedpassword, RADIUS_VECTOR_LENGTH) != 0)
 		{
 			ereport(LOG,
-					(errmsg("RADIUS response has incorrect MD5 signature")));
+			   (errmsg("RADIUS response from %s has incorrect MD5 signature",
+					   server)));
 			continue;
 		}
 
@@ -2809,13 +2863,13 @@ CheckRADIUSAuth(Port *port)
 		else if (receivepacket->code == RADIUS_ACCESS_REJECT)
 		{
 			closesocket(sock);
-			return STATUS_ERROR;
+			return STATUS_EOF;
 		}
 		else
 		{
 			ereport(LOG,
-			 (errmsg("RADIUS response has invalid code (%d) for user \"%s\"",
-					 receivepacket->code, port->user_name)));
+					(errmsg("RADIUS response from %s has invalid code (%d) for user \"%s\"",
+							server, receivepacket->code, user_name)));
 			continue;
 		}
 	}							/* while (true) */
diff --git a/src/backend/libpq/hba.c b/src/backend/libpq/hba.c
index be63a4b..9e81099 100644
--- a/src/backend/libpq/hba.c
+++ b/src/backend/libpq/hba.c
@@ -39,6 +39,7 @@
 #include "storage/fd.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/varlena.h"
 #include "utils/guc.h"
 #include "utils/lsyscache.h"
 #include "utils/memutils.h"
@@ -145,6 +146,8 @@ static List *tokenize_inc_file(List *tokens, const char *outer_filename,
 				  const char *inc_filename, int elevel, char **err_msg);
 static bool parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
 				   int elevel, char **err_msg);
+static bool verify_option_list_length(List *options, char *optionname,
+						  List *masters, char *mastername, int line_num);
 static ArrayType *gethba_options(HbaLine *hba);
 static void fill_hba_line(Tuplestorestate *tuple_store, TupleDesc tupdesc,
 			  int lineno, HbaLine *hba, const char *err_msg);
@@ -1532,8 +1535,52 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 
 	if (parsedline->auth_method == uaRADIUS)
 	{
-		MANDATORY_AUTH_ARG(parsedline->radiusserver, "radiusserver", "radius");
-		MANDATORY_AUTH_ARG(parsedline->radiussecret, "radiussecret", "radius");
+		MANDATORY_AUTH_ARG(parsedline->radiusservers, "radiusservers", "radius");
+		MANDATORY_AUTH_ARG(parsedline->radiussecrets, "radiussecrets", "radius");
+
+		if (list_length(parsedline->radiusservers) < 1)
+		{
+			ereport(LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("list of RADIUS servers cannot be empty"),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			return NULL;
+		}
+
+		if (list_length(parsedline->radiussecrets) < 1)
+		{
+			ereport(LOG,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("list of RADIUS secrets cannot be empty"),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			return NULL;
+		}
+
+		/*
+		 * Verify length of option lists - each can be 0 (except for secrets,
+		 * but that's already checked above), 1 (use the same value
+		 * everywhere) or the same as the number of servers.
+		 */
+		if (!verify_option_list_length(parsedline->radiussecrets,
+									   "RADIUS secrets",
+									   parsedline->radiusservers,
+									   "RADIUS servers",
+									   line_num))
+			return NULL;
+		if (!verify_option_list_length(parsedline->radiusports,
+									   "RADIUS ports",
+									   parsedline->radiusservers,
+									   "RADIUS servers",
+									   line_num))
+			return NULL;
+		if (!verify_option_list_length(parsedline->radiusidentifiers,
+									   "RADIUS identifiers",
+									   parsedline->radiusservers,
+									   "RADIUS servers",
+									   line_num))
+			return NULL;
 	}
 
 	/*
@@ -1547,6 +1594,28 @@ parse_hba_line(TokenizedLine *tok_line, int elevel)
 	return parsedline;
 }
 
+
+static bool
+verify_option_list_length(List *options, char *optionname, List *masters, char *mastername, int line_num)
+{
+	if (list_length(options) == 0 ||
+		list_length(options) == 1 ||
+		list_length(options) == list_length(masters))
+		return true;
+
+	ereport(LOG,
+			(errcode(ERRCODE_CONFIG_FILE_ERROR),
+			 errmsg("the number of %s (%i) must be 1 or the same as the number of %s (%i)",
+					optionname,
+					list_length(options),
+					mastername,
+					list_length(masters)
+					),
+			 errcontext("line %d of configuration file \"%s\"",
+						line_num, HbaFileName)));
+	return false;
+}
+
 /*
  * Parse one name-value pair as an authentication option into the given
  * HbaLine.  Return true if we successfully parse the option, false if we
@@ -1766,60 +1835,137 @@ parse_hba_auth_opt(char *name, char *val, HbaLine *hbaline,
 		else
 			hbaline->upn_username = false;
 	}
-	else if (strcmp(name, "radiusserver") == 0)
+	else if (strcmp(name, "radiusservers") == 0)
 	{
 		struct addrinfo *gai_result;
 		struct addrinfo hints;
 		int			ret;
+		List	   *parsed_servers;
+		ListCell   *l;
+		char       *dupval = pstrdup(val);
 
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusserver", "radius");
-
-		MemSet(&hints, 0, sizeof(hints));
-		hints.ai_socktype = SOCK_DGRAM;
-		hints.ai_family = AF_UNSPEC;
+		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusservers", "radius");
 
-		ret = pg_getaddrinfo_all(val, NULL, &hints, &gai_result);
-		if (ret || !gai_result)
+		if (!SplitIdentifierString(dupval, ',', &parsed_servers))
 		{
+			/* syntax error in list */
 			ereport(elevel,
 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
-							val, gai_strerror(ret)),
+					 errmsg("could not parse RADIUS server list \"%s\"",
+							val),
 					 errcontext("line %d of configuration file \"%s\"",
 								line_num, HbaFileName)));
-			*err_msg = psprintf("could not translate RADIUS server name \"%s\" to address: %s",
-								val, gai_strerror(ret));
-			if (gai_result)
-				pg_freeaddrinfo_all(hints.ai_family, gai_result);
 			return false;
 		}
-		pg_freeaddrinfo_all(hints.ai_family, gai_result);
-		hbaline->radiusserver = pstrdup(val);
+
+		/* For each entry in the list, translate it */
+		foreach(l, parsed_servers)
+		{
+			MemSet(&hints, 0, sizeof(hints));
+			hints.ai_socktype = SOCK_DGRAM;
+			hints.ai_family = AF_UNSPEC;
+
+			ret = pg_getaddrinfo_all((char *) lfirst(l), NULL, &hints, &gai_result);
+			if (ret || !gai_result)
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("could not translate RADIUS server name \"%s\" to address: %s",
+								(char *) lfirst(l), gai_strerror(ret)),
+						 errcontext("line %d of configuration file \"%s\"",
+									line_num, HbaFileName)));
+				if (gai_result)
+					pg_freeaddrinfo_all(hints.ai_family, gai_result);
+
+				list_free(parsed_servers);
+				return false;
+			}
+			pg_freeaddrinfo_all(hints.ai_family, gai_result);
+		}
+
+		/* All entries are OK, so store them */
+		hbaline->radiusservers = parsed_servers;
+		hbaline->radiusservers_s = pstrdup(val);
 	}
-	else if (strcmp(name, "radiusport") == 0)
+	else if (strcmp(name, "radiusports") == 0)
 	{
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusport", "radius");
-		hbaline->radiusport = atoi(val);
-		if (hbaline->radiusport == 0)
+		List	   *parsed_ports;
+		ListCell   *l;
+		char       *dupval = pstrdup(val);
+
+		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusports", "radius");
+
+		if (!SplitIdentifierString(dupval, ',', &parsed_ports))
 		{
 			ereport(elevel,
 					(errcode(ERRCODE_CONFIG_FILE_ERROR),
-					 errmsg("invalid RADIUS port number: \"%s\"", val),
+					 errmsg("could not parse RADIUS port list \"%s\"",
+							val),
 					 errcontext("line %d of configuration file \"%s\"",
 								line_num, HbaFileName)));
 			*err_msg = psprintf("invalid RADIUS port number: \"%s\"", val);
 			return false;
 		}
+
+		foreach(l, parsed_ports)
+		{
+			if (atoi(lfirst(l)) == 0)
+			{
+				ereport(elevel,
+						(errcode(ERRCODE_CONFIG_FILE_ERROR),
+						 errmsg("invalid RADIUS port number: \"%s\"", val),
+						 errcontext("line %d of configuration file \"%s\"",
+									line_num, HbaFileName)));
+
+				return false;
+			}
+		}
+		hbaline->radiusports = parsed_ports;
+		hbaline->radiusports_s = pstrdup(val);
 	}
-	else if (strcmp(name, "radiussecret") == 0)
+	else if (strcmp(name, "radiussecrets") == 0)
 	{
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecret", "radius");
-		hbaline->radiussecret = pstrdup(val);
+		List	   *parsed_secrets;
+		char       *dupval = pstrdup(val);
+
+		REQUIRE_AUTH_OPTION(uaRADIUS, "radiussecrets", "radius");
+
+		if (!SplitIdentifierString(dupval, ',', &parsed_secrets))
+		{
+			/* syntax error in list */
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not parse RADIUS secret list \"%s\"",
+							val),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			return false;
+		}
+
+		hbaline->radiussecrets = parsed_secrets;
+		hbaline->radiussecrets_s = pstrdup(val);
 	}
-	else if (strcmp(name, "radiusidentifier") == 0)
+	else if (strcmp(name, "radiusidentifiers") == 0)
 	{
-		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifier", "radius");
-		hbaline->radiusidentifier = pstrdup(val);
+		List	   *parsed_identifiers;
+		char       *dupval = pstrdup(val);
+
+		REQUIRE_AUTH_OPTION(uaRADIUS, "radiusidentifiers", "radius");
+
+		if (!SplitIdentifierString(dupval, ',', &parsed_identifiers))
+		{
+			/* syntax error in list */
+			ereport(elevel,
+					(errcode(ERRCODE_CONFIG_FILE_ERROR),
+					 errmsg("could not parse RADIUS identifiers list \"%s\"",
+							val),
+					 errcontext("line %d of configuration file \"%s\"",
+								line_num, HbaFileName)));
+			return false;
+		}
+
+		hbaline->radiusidentifiers = parsed_identifiers;
+		hbaline->radiusidentifiers_s = pstrdup(val);
 	}
 	else
 	{
@@ -2124,21 +2270,21 @@ gethba_options(HbaLine *hba)
 
 	if (hba->auth_method == uaRADIUS)
 	{
-		if (hba->radiusserver)
+		if (hba->radiusservers_s)
 			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusserver=%s", hba->radiusserver));
+				CStringGetTextDatum(psprintf("radiusservers=%s", hba->radiusservers_s));
 
-		if (hba->radiussecret)
+		if (hba->radiussecrets_s)
 			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiussecret=%s", hba->radiussecret));
+				CStringGetTextDatum(psprintf("radiussecrets=%s", hba->radiussecrets_s));
 
-		if (hba->radiusidentifier)
+		if (hba->radiusidentifiers_s)
 			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusidentifier=%s", hba->radiusidentifier));
+				CStringGetTextDatum(psprintf("radiusidentifiers=%s", hba->radiusidentifiers_s));
 
-		if (hba->radiusport)
+		if (hba->radiusports_s)
 			options[noptions++] =
-				CStringGetTextDatum(psprintf("radiusport=%d", hba->radiusport));
+				CStringGetTextDatum(psprintf("radiusports=%s", hba->radiusports_s));
 	}
 
 	Assert(noptions <= MAX_HBA_OPTIONS);
diff --git a/src/include/libpq/hba.h b/src/include/libpq/hba.h
index 748a072..13706ff 100644
--- a/src/include/libpq/hba.h
+++ b/src/include/libpq/hba.h
@@ -88,10 +88,14 @@ typedef struct HbaLine
 	bool		include_realm;
 	bool		compat_realm;
 	bool		upn_username;
-	char	   *radiusserver;
-	char	   *radiussecret;
-	char	   *radiusidentifier;
-	int			radiusport;
+	List	   *radiusservers;
+	char       *radiusservers_s;
+	List	   *radiussecrets;
+	char       *radiussecrets_s;
+	List	   *radiusidentifiers;
+	char       *radiusidentifiers_s;
+	List	   *radiusports;
+	char       *radiusports_s;
 } HbaLine;
 
 typedef struct IdentLine
