diff --git a/django/db/backends/postgresql/base.py b/django/db/backends/postgresql/base.py
index 9eac005dd1..7926c05c09 100644
--- a/django/db/backends/postgresql/base.py
+++ b/django/db/backends/postgresql/base.py
@@ -152,10 +152,14 @@ class DatabaseWrapper(BaseDatabaseWrapper):
     def get_connection_params(self):
         settings_dict = self.settings_dict
         # None may be used to connect to the default 'postgres' db
-        if settings_dict['NAME'] == '':
+        if (
+            settings_dict['NAME'] == '' and
+            not settings_dict.get('OPTIONS', {}).get('service')
+        ):
             raise ImproperlyConfigured(
                 "settings.DATABASES is improperly configured. "
-                "Please supply the NAME value.")
+                "Please supply the NAME or OPTIONS['service'] value."
+            )
         if len(settings_dict['NAME'] or '') > self.ops.max_name_length():
             raise ImproperlyConfigured(
                 "The database name '%s' (%d characters) is longer than "
@@ -166,10 +170,19 @@ class DatabaseWrapper(BaseDatabaseWrapper):
                     self.ops.max_name_length(),
                 )
             )
-        conn_params = {
-            'database': settings_dict['NAME'] or 'postgres',
-            **settings_dict['OPTIONS'],
-        }
+        conn_params = {}
+        if settings_dict['NAME']:
+            conn_params = {
+                'database': settings_dict['NAME'],
+                **settings_dict['OPTIONS'],
+            }
+        elif settings_dict['NAME'] is None:
+            # Connect to the default 'postgres' db.
+            settings_dict.get('OPTIONS', {}).pop('service', None)
+            conn_params = {'database': 'postgres', **settings_dict['OPTIONS']}
+        else:
+            conn_params = {**settings_dict['OPTIONS']}
+
         conn_params.pop('isolation_level', None)
         if settings_dict['USER']:
             conn_params['user'] = settings_dict['USER']
diff --git a/django/db/backends/postgresql/client.py b/django/db/backends/postgresql/client.py
index 7965401163..2339880967 100644
--- a/django/db/backends/postgresql/client.py
+++ b/django/db/backends/postgresql/client.py
@@ -16,6 +16,7 @@ class DatabaseClient(BaseDatabaseClient):
         dbname = settings_dict.get('NAME') or 'postgres'
         user = settings_dict.get('USER')
         passwd = settings_dict.get('PASSWORD')
+        service = options.get('service')
         sslmode = options.get('sslmode')
         sslrootcert = options.get('sslrootcert')
         sslcert = options.get('sslcert')
@@ -33,6 +34,8 @@ class DatabaseClient(BaseDatabaseClient):
         env = {}
         if passwd:
             env['PGPASSWORD'] = str(passwd)
+        if service:
+            env['PGSERVICE'] = str(service)
         if sslmode:
             env['PGSSLMODE'] = str(sslmode)
         if sslrootcert:
diff --git a/docs/ref/databases.txt b/docs/ref/databases.txt
index a9edf43c54..b9a82140a0 100644
--- a/docs/ref/databases.txt
+++ b/docs/ref/databases.txt
@@ -108,11 +108,43 @@ required, though the latest release is recommended.
 
 .. _psycopg2: https://www.psycopg.org/
 
+.. _postgresql-connection-settings:
+
 PostgreSQL connection settings
 -------------------------------
 
 See :setting:`HOST` for details.
 
+To connect using a service name from the `connection service file`_, you must
+specify it in the :setting:`OPTIONS` part of your database configuration in
+:setting:`DATABASES`:
+
+.. code-block:: python
+    :caption: settings.py
+
+    DATABASES = {
+        'default': {
+            'ENGINE': 'django.db.backends.postgresql',
+            'OPTIONS': {'service': 'my_service'},
+        }
+    }
+
+.. code-block:: text
+    :caption: .pg_service.conf
+
+    [my_service]
+    host=localhost
+    user=USER
+    dbname=NAME
+    password=PASSWORD
+    port=5432
+
+.. _connection service file: https://www.postgresql.org/docs/current/libpq-pgservice.html
+
+.. versionchanged:: 4.0
+
+    Support for connecting by a service name was added.
+
 Optimizing PostgreSQL's configuration
 -------------------------------------
 
diff --git a/docs/releases/4.0.txt b/docs/releases/4.0.txt
index d2684a9a2e..1e408c97bd 100644
--- a/docs/releases/4.0.txt
+++ b/docs/releases/4.0.txt
@@ -64,7 +64,8 @@ Minor features
 :mod:`django.contrib.postgres`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-* ...
+* The PostgreSQL backend now supports connecting by a service name. See
+  :ref:`postgresql-connection-settings` for more details.
 
 :mod:`django.contrib.redirects`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/backends/postgresql/tests.py b/tests/backends/postgresql/tests.py
index 8d0a801ea2..94ebfbdaf8 100644
--- a/tests/backends/postgresql/tests.py
+++ b/tests/backends/postgresql/tests.py
@@ -68,6 +68,36 @@ class Tests(TestCase):
         with self.assertRaisesMessage(ImproperlyConfigured, msg):
             DatabaseWrapper(settings).get_connection_params()
 
+    def test_database_name_empty(self):
+        from django.db.backends.postgresql.base import DatabaseWrapper
+        settings = connection.settings_dict.copy()
+        settings['NAME'] = ''
+        msg = (
+            "settings.DATABASES is improperly configured. Please supply the "
+            "NAME or OPTIONS['service'] value."
+        )
+        with self.assertRaisesMessage(ImproperlyConfigured, msg):
+            DatabaseWrapper(settings).get_connection_params()
+
+    def test_service_name(self):
+        from django.db.backends.postgresql.base import DatabaseWrapper
+        settings = connection.settings_dict.copy()
+        settings['OPTIONS'] = {'service': 'my_service'}
+        settings['NAME'] = ''
+        params = DatabaseWrapper(settings).get_connection_params()
+        self.assertEqual(params['service'], 'my_service')
+        self.assertNotIn('database', params)
+
+    def test_service_name_default_db(self):
+        # None is used to connect to the default 'postgres' db.
+        from django.db.backends.postgresql.base import DatabaseWrapper
+        settings = connection.settings_dict.copy()
+        settings['NAME'] = None
+        settings['OPTIONS'] = {'service': 'django_test'}
+        params = DatabaseWrapper(settings).get_connection_params()
+        self.assertEqual(params['database'], 'postgres')
+        self.assertNotIn('service', params)
+
     def test_connect_and_rollback(self):
         """
         PostgreSQL shouldn't roll back SET TIME ZONE, even if the first
diff --git a/tests/dbshell/test_postgresql.py b/tests/dbshell/test_postgresql.py
index ccf49d7e50..e9eb131db7 100644
--- a/tests/dbshell/test_postgresql.py
+++ b/tests/dbshell/test_postgresql.py
@@ -67,6 +67,12 @@ class PostgreSqlDbshellCommandTestCase(SimpleTestCase):
             )
         )
 
+    def test_service(self):
+        self.assertEqual(
+            self.settings_to_cmd_args_env({'OPTIONS': {'service': 'django_test'}}),
+            (['psql', 'postgres'], {'PGSERVICE': 'django_test'}),
+        )
+
     def test_column(self):
         self.assertEqual(
             self.settings_to_cmd_args_env({