diff --git a/devstack/lib/ironic b/devstack/lib/ironic
index 3612b7cb2e..a53cc6824a 100644
--- a/devstack/lib/ironic
+++ b/devstack/lib/ironic
@@ -2795,6 +2795,8 @@ function upload_baremetal_ironic_efiboot {
     echo_summary "Building and uploading EFI boot image for ironic"
 
     if [ ! -e "$IRONIC_EFIBOOT" ]; then
+        # NOTE(dtantsur): update doc/source/admin/drivers/redfish.rst when
+        # changing this procedure.
 
         local efiboot_path
         efiboot_path=$(mktemp -d --tmpdir=${DEST})/$efiboot_name
@@ -2812,16 +2814,8 @@ function upload_baremetal_ironic_efiboot {
 
         sudo mkdir -p $efiboot_mount/efi/boot
 
-        sudo grub-mkimage \
-            -C xz \
-            -O x86_64-efi \
-            -p /boot/grub \
-            -o $efiboot_mount/efi/boot/bootx64.efi \
-            boot linux linuxefi search normal configfile \
-            part_gpt btrfs ext2 fat iso9660 loopback \
-            test keystatus gfxmenu regexp probe \
-            efi_gop efi_uga all_video gfxterm font \
-            echo read ls cat png jpeg halt reboot
+        sudo cp "$IRONIC_GRUB2_SHIM_FILE" $efiboot_mount/efi/boot/bootx64.efi
+        sudo cp "$IRONIC_GRUB2_FILE" $efiboot_mount/efi/boot/grubx64.efi
 
         sudo umount $efiboot_mount
 
@@ -2839,6 +2833,21 @@ function upload_baremetal_ironic_efiboot {
     die_if_not_set $LINENO IRONIC_EFIBOOT_ID "Failed to load EFI bootloader image into glance"
 
     iniset $IRONIC_CONF_FILE conductor bootloader $IRONIC_EFIBOOT_ID
+
+    local efi_grub_path
+    if is_ubuntu; then
+        efi_grub_path=EFI/ubuntu/grub.cfg
+    elif is_fedora; then
+        if grep -qi CentOS /etc/redhat-release; then
+            efi_grub_path=EFI/centos/grub.cfg
+        else
+            efi_grub_path=EFI/fedora/grub.cfg
+        fi
+    else
+        # NOTE(dtantsur): this is likely incorrect
+        efi_grub_path=EFI/BOOT/grub.cfg
+    fi
+    iniset $IRONIC_CONF_FILE DEFAULT grub_config_path $efi_grub_path
 }
 
 # build deploy kernel+ramdisk, then upload them to glance
diff --git a/doc/source/admin/drivers/redfish.rst b/doc/source/admin/drivers/redfish.rst
index bb83e64e20..a2ef27bee5 100644
--- a/doc/source/admin/drivers/redfish.rst
+++ b/doc/source/admin/drivers/redfish.rst
@@ -104,11 +104,8 @@ a node with the ``redfish`` driver. For example:
 For more information about enrolling nodes see :ref:`enrollment`
 in the install guide.
 
-Features of the ``redfish`` hardware type
-=========================================
-
 Boot mode support
-^^^^^^^^^^^^^^^^^
+=================
 
 The ``redfish`` hardware type can read current boot mode from the
 bare metal node as well as set it to either Legacy BIOS or UEFI.
@@ -121,7 +118,7 @@ bare metal node as well as set it to either Legacy BIOS or UEFI.
    boot mode to their bare metal nodes.
 
 Out-Of-Band inspection
-^^^^^^^^^^^^^^^^^^^^^^
+======================
 
 The ``redfish`` hardware type can inspect the bare metal node by querying
 Redfish compatible BMC. This process is quick and reliable compared to the
@@ -141,7 +138,7 @@ into the introspection ramdisk.
    support the required schema. In this case the property will be set to 0.
 
 Virtual media boot
-^^^^^^^^^^^^^^^^^^
+==================
 
 The idea behind virtual media boot is that BMC gets hold of the boot image
 one way or the other (e.g. by HTTP GET, other methods are defined in the
@@ -167,12 +164,7 @@ BIOS boot mode, it suffice to set ironic boot interface to
   baremetal node set --boot-interface redfish-virtual-media node-0
 
 If UEFI boot mode is desired, the user should additionally supply EFI
-System Partition image (ESP_) via ``[driver-info]/bootloader`` ironic node
-property or ironic configuration file in form of Glance image UUID or a URL.
-
-.. code-block:: bash
-
-  baremetal node set --driver-info bootloader=<glance-uuid> node-0
+System Partition image (ESP_), see `Configuring an ESP image`_ for details.
 
 If ``[driver_info]/config_via_floppy`` boolean property of the node is set to
 ``true``, ironic will create a file with runtime configuration parameters,
@@ -185,6 +177,70 @@ property can be used to pass user-specified kernel command line parameters.
 For ramdisk kernel, ``[instance_info]/kernel_append_params`` property serves
 the same purpose.
 
+Configuring an ESP image
+~~~~~~~~~~~~~~~~~~~~~~~~~
+
+An ESP image is an image that contains the necessary bootloader to boot the ISO
+in UEFI mode. You will need a GRUB2 image file, as well as Shim for secure
+boot. See :ref:`uefi-pxe-grub` for an explanation how to get them.
+
+Then the following script can be used to build an ESP image:
+
+.. code-block:: bash
+
+   DEST=/path/to/esp.img
+   GRUB2=/path/to/grub.efi
+   SHIM=/path/to/shim.efi
+   TEMP_MOUNT=$(mktemp -d)
+
+   dd if=/dev/zero of=$DEST bs=4096 count=1024
+   mkfs.fat -s 4 -r 512 -S 4096 $DEST
+
+   sudo mount $DEST $TEMP_MOUNT
+   sudo mkdir -p $DEST/EFI/BOOT
+   sudo cp "$SHIM" $DEST/EFI/BOOT/BOOTX64.efi
+   sudo cp "$GRUB2" $DEST/EFI/BOOT/GRUBX64.efi
+   sudo umount $TEMP_MOUNT
+
+.. note::
+   If you use an architecture other than x86-64, you'll need to adjust the
+   destination paths.
+
+The resulting image should be provided via the ``driver_info/bootloader``
+ironic node property in form of an image UUID or a URL:
+
+.. code-block:: bash
+
+   baremetal node set --driver-info bootloader=<glance-uuid-or-url> node-0
+
+Alternatively, set the bootloader UUID or URL in the configuration file:
+
+.. code-block:: ini
+
+   [conductor]
+   bootloader = <glance-uuid-or-url>
+
+Finally, you need to provide the correct GRUB2 configuration path for your
+image. In most cases this path will depend on your distribution, more
+precisely, the distribution you took the GRUB2 image from. For example:
+
+CentOS:
+
+.. code-block:: ini
+
+   [DEFAULT]
+   grub_config_path = EFI/centos/grub.cfg
+
+Ubuntu:
+
+.. code-block:: ini
+
+   [DEFAULT]
+   grub_config_path = EFI/ubuntu/grub.cfg
+
+.. note::
+   Unlike in the script above, these paths are case-sensitive!
+
 .. _redfish-virtual-media-ramdisk:
 
 Virtual Media Ramdisk
@@ -217,7 +273,7 @@ setting is ignored. Configuration drives are not supported yet.
 .. _`dhcpless_booting`:
 
 Layer 3 or DHCP-less ramdisk booting
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 The DHCP used by PXE requires direct L2 connectivity between the node and the
 service since it's a User Datagram Protocol (UDP) like other protocols used by
@@ -257,8 +313,8 @@ scenario.
 
   Make sure to use add the simple-init_ element when building the IPA ramdisk.
 
-Firmware update using manual cleaning step
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+Firmware update using manual cleaning
+=====================================
 
 The ``redfish`` hardware type supports updating the firmware on nodes using a
 manual cleaning step.
diff --git a/doc/source/install/configure-pxe.rst b/doc/source/install/configure-pxe.rst
index 291b101f31..6c51a24f35 100644
--- a/doc/source/install/configure-pxe.rst
+++ b/doc/source/install/configure-pxe.rst
@@ -116,6 +116,7 @@ set up on the Bare Metal service nodes which run the ``ironic-conductor``.
     echo 're ^(^/) /tftpboot/\1' >> /tftpboot/map-file
     echo 're ^([^/]) /tftpboot/\1' >> /tftpboot/map-file
 
+.. _uefi-pxe-grub:
 
 UEFI PXE - Grub setup
 ---------------------
diff --git a/ironic/conf/default.py b/ironic/conf/default.py
index c8359b20f8..c19ea5f3f4 100644
--- a/ironic/conf/default.py
+++ b/ironic/conf/default.py
@@ -211,7 +211,11 @@ image_opts = [
     cfg.StrOpt('grub_config_path',
                default='/boot/grub/grub.cfg',
                help=_('GRUB2 configuration file location on the UEFI ISO '
-                      'images produced by ironic.')),
+                      'images produced by ironic. The default value is '
+                      'usually incorrect and should not be relied on. '
+                      'If you use a GRUB2 image from a certain distribution, '
+                      'use a distribution-specific path here, e.g. '
+                      'EFI/ubuntu/grub.cfg')),
     cfg.StrOpt('grub_config_template',
                default=os.path.join('$pybasedir',
                                     'common/grub_conf.template'),