Dnf offline upgrade skips GPG checks

I have a VM which I use for testing stuff that runs Fedora, and so does the host OS. The host OS has a local repo set up, shared via virtiofs to the guest OS, both using the dnf local repo plugin.

When I do a regular dnf upgrade on the host, the GPG checks are done and it writes the packages to the regular local repo as one would expect.

When I do a dnf upgrade --offline on the host, the offline upgrade downloads the packages as expected, although when rebooting with dnf offline reboot it skips the GPG check (comes with a warning at the end that GPG checks were skipped) and writes its contents to the local non-GPG-check repo.

So I would love some answers to the following:

  1. Is it possible to have GPG checks during the offline upgrade?
  2. Following from 1, write to the ordinary local repo so I don’t need two repos (one for GPG check and one for non-GPG check)?
  3. How do I tell dnf local plugin if a repo is read-only (eg. shared virtiofs as a read-only mount) so it doesn’t try to copy a package to a read-only mount?
  4. Is it possible to just download a package to be copied into the local repo without having to install it first? Would it not be better for the packages to be copied to the local repo before the transaction even starts?

Why does the default configuration skip GPG checks on offline upgrades?
It is arguably the most important time to check this as anything (even corruption) could happen from the time the offline files were downloaded to the time they were applied in next reboot. Even if it is a somewhat far-fetched scenario from a security perspective, I don’t see the logic behind checking GPG signatures when doing an ordinary dnf upgrade but skipping them in the offline one.

Can you please post the upgrade output? Did it skip GPG checks for all packages/repositories, or for a few? From what I know, each repo can configure whether GPG checks are required or not. For example, the fedora.repo file says:

cat /etc/yum.repos.d/fedora.repo
[fedora]
name=Fedora $releasever - $basearch
#baseurl=http://download.example/pub/fedora/linux/releases/$releasever/Everything/$basearch/os/
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-$releasever&arch=$basearch
enabled=1
countme=1
metadata_expire=7d
repo_gpgcheck=0
type=rpm
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
skip_if_unavailable=False

[fedora-debuginfo]
name=Fedora $releasever - $basearch - Debug
#baseurl=http://download.example/pub/fedora/linux/releases/$releasever/Everything/$basearch/debug/tree/
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-debug-$releasever&arch=$basearch
enabled=0
metadata_expire=7d
repo_gpgcheck=0
type=rpm
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
skip_if_unavailable=False

[fedora-source]
name=Fedora $releasever - Source
#baseurl=http://download.example/pub/fedora/linux/releases/$releasever/Everything/source/tree/
metalink=https://mirrors.fedoraproject.org/metalink?repo=fedora-source-$releasever&arch=$basearch
enabled=0
metadata_expire=7d
repo_gpgcheck=0
type=rpm
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-fedora-$releasever-$basearch
skip_if_unavailable=False

So, gpg checks should not be skipped for packages from this repository.

It behaves differently depending on whether it’s an online or an offline upgrade. If it’s a regular online upgrade, there is no sign the GPG checks are skipped. However, in an offline upgrade, within the reboot that installs the updates, just before rebooting again it warns about skipped GPG checks.

Here is what I get from dnf upgrade --offline:
$ sudo dnf offline reboot The system will now reboot to perform the offline transaction initiated by the following command: dnf upgrade --offline Is this ok [y/N]: y
I’m not sure how to catch the messages at boot time when it upgrades, they’re very quick but I do get a glance of a warning about skipping GPG checks.

I created a test install, with two local repos, gpg and nogpg:

/etc/dnf/libdnf5-plugins/local.conf:

[main]
name = local
enabled = host-only

# Path to the local repository for packages from repos with pkg_gpgcheck enabled.
repodir = /repo/gpg

# Path to the local repository for packages from repos with pkg_gpgcheck disabled.
repodir_nogpgcheck = /repo/nogpg

# Createrepo options. See man createrepo_c
[createrepo]
# This option lets you disable createrepo command. This could be useful
# for large repositories where metadata is periodically generated by cron
# for example. This also has the side effect of only copying the packages
# to the local repo directory.
enabled = true

# If you want to speedup createrepo with the --cachedir option. Eg.
# cachedir = /tmp/createrepo-local-plugin-cachedir

# quiet is true by default
# quiet = true

# verbose is false by default
# verbose = false

Although unable to capture the output of the boot-time dnf offline install, I ran dnf upgrade *systemd* --offline and dnf offline reboot to see what would happen, and the following can be found in the local repositories:

/repo/gpg:
total 634M
 536K abrt-2.17.9-1.fc44.x86_64.rpm
 108K abrt-addon-ccpp-2.17.9-1.fc44.x86_64.rpm
  44K abrt-addon-kerneloops-2.17.9-1.fc44.x86_64.rpm
  20K abrt-addon-pstoreoops-2.17.9-1.fc44.x86_64.rpm
  32K abrt-addon-vmcore-2.17.9-1.fc44.x86_64.rpm
  36K abrt-addon-xorg-2.17.9-1.fc44.x86_64.rpm
  12K abrt-cli-2.17.9-1.fc44.x86_64.rpm
  64K abrt-dbus-2.17.9-1.fc44.x86_64.rpm
  12K abrt-desktop-2.17.9-1.fc44.x86_64.rpm
  44K abrt-gui-2.17.9-1.fc44.x86_64.rpm
  24K abrt-gui-libs-2.17.9-1.fc44.x86_64.rpm
  40K abrt-libs-2.17.9-1.fc44.x86_64.rpm
  28K abrt-plugin-bodhi-2.17.9-1.fc44.x86_64.rpm
  52K abrt-tui-2.17.9-1.fc44.noarch.rpm
  27M amd-gpu-firmware-20260810-1.fc44.noarch.rpm
 656K amd-ucode-firmware-20260810-1.fc44.noarch.rpm
  46M atheros-firmware-20260810-1.fc44.noarch.rpm
 9.7M brcmfmac-firmware-20260810-1.fc44.noarch.rpm
 3.1M cirrus-audio-firmware-20260810-1.fc44.noarch.rpm
 632K dracut-108-7.fc44.x86_64.rpm
  12K dracut-config-rescue-108-7.fc44.x86_64.rpm
  28K dracut-live-108-7.fc44.x86_64.rpm
  60K dracut-network-108-7.fc44.x86_64.rpm
 2.4M glibc-2.43-8.fc44.x86_64.rpm
  18M glibc-all-langpacks-2.43-8.fc44.x86_64.rpm
 376K glibc-common-2.43-8.fc44.x86_64.rpm
 1.7M glibc-gconv-extra-2.43-8.fc44.x86_64.rpm
1020K grub2-common-2.12-64.fc44.noarch.rpm
 2.1M grub2-efi-ia32-2.12-64.fc44.x86_64.rpm
 2.1M grub2-efi-ia32-cdboot-2.12-64.fc44.x86_64.rpm
 1.1M grub2-efi-ia32-modules-2.12-64.fc44.noarch.rpm
 2.2M grub2-efi-x64-2.12-64.fc44.x86_64.rpm
 2.2M grub2-efi-x64-cdboot-2.12-64.fc44.x86_64.rpm
 1.2M grub2-efi-x64-modules-2.12-64.fc44.noarch.rpm
  16K grub2-pc-2.12-64.fc44.x86_64.rpm
 1.2M grub2-pc-modules-2.12-64.fc44.noarch.rpm
 1.9M grub2-tools-2.12-64.fc44.x86_64.rpm
 556K grub2-tools-efi-2.12-64.fc44.x86_64.rpm
 852K grub2-tools-extra-2.12-64.fc44.x86_64.rpm
 696K grub2-tools-minimal-2.12-64.fc44.x86_64.rpm
 3.3M intel-audio-firmware-20260810-1.fc44.noarch.rpm
  13M intel-gpu-firmware-20260810-1.fc44.noarch.rpm
 7.8M intel-vsc-firmware-20260810-1.fc44.noarch.rpm
 172K iwlegacy-firmware-20260810-1.fc44.noarch.rpm
 1.9M iwlwifi-dvm-firmware-20260810-1.fc44.noarch.rpm
  21M iwlwifi-mld-firmware-20260810-1.fc44.noarch.rpm
  61M iwlwifi-mvm-firmware-20260810-1.fc44.noarch.rpm
 240K kernel-7.1.8-200.fc44.x86_64.rpm
  21M kernel-core-7.1.8-200.fc44.x86_64.rpm
 102M kernel-modules-7.1.8-200.fc44.x86_64.rpm
  76M kernel-modules-core-7.1.8-200.fc44.x86_64.rpm
 4.3M kernel-modules-extra-7.1.8-200.fc44.x86_64.rpm
 536K kernel-tools-7.1.8-200.fc44.x86_64.rpm
 252K kernel-tools-libs-7.1.8-200.fc44.x86_64.rpm
 1.4M libertas-firmware-20260810-1.fc44.noarch.rpm
  50M linux-firmware-20260810-1.fc44.noarch.rpm
  88K linux-firmware-whence-20260810-1.fc44.noarch.rpm
  31M mt7xxx-firmware-20260810-1.fc44.noarch.rpm
 100M nvidia-gpu-firmware-20260810-1.fc44.noarch.rpm
 1.6M nxpwireless-firmware-20260810-1.fc44.noarch.rpm
 820K PackageKit-1.3.6-1.fc44.x86_64.rpm
 164K PackageKit-glib-1.3.6-1.fc44.x86_64.rpm
  20K PackageKit-gstreamer-plugin-1.3.6-1.fc44.x86_64.rpm
  40K python3-abrt-2.17.9-1.fc44.x86_64.rpm
  20K python3-abrt-addon-2.17.9-1.fc44.noarch.rpm
  72K python3-rpm-6.0.2-1.fc44.x86_64.rpm
 772K qcom-wwan-firmware-20260810-1.fc44.noarch.rpm
 6.6M realtek-firmware-20260810-1.fc44.noarch.rpm
    0 repodata
 580K rpm-6.0.2-1.fc44.x86_64.rpm
 132K rpm-build-libs-6.0.2-1.fc44.x86_64.rpm
 416K rpm-libs-6.0.2-1.fc44.x86_64.rpm
  20K rpm-plugin-audit-6.0.2-1.fc44.x86_64.rpm
  20K rpm-plugin-selinux-6.0.2-1.fc44.x86_64.rpm
  20K rpm-plugin-systemd-inhibit-6.0.2-1.fc44.x86_64.rpm
 940K rpm-sequoia-1.10.2-4.fc44.x86_64.rpm
  28K rpm-sign-libs-6.0.2-1.fc44.x86_64.rpm
 4.7M tiwilink-firmware-20260810-1.fc44.noarch.rpm

/repo/nogpg:
total 12M
   0 repodata
4.3M systemd-259.8-1.fc44.x86_64.rpm
868K systemd-libs-259.8-1.fc44.x86_64.rpm
800K systemd-networkd-259.8-1.fc44.x86_64.rpm
 16K systemd-oomd-defaults-259.8-1.fc44.noarch.rpm
440K systemd-pam-259.8-1.fc44.x86_64.rpm
288K systemd-resolved-259.8-1.fc44.x86_64.rpm
2.1M systemd-shared-259.8-1.fc44.x86_64.rpm
 52K systemd-sysusers-259.8-1.fc44.x86_64.rpm
2.7M systemd-udev-259.8-1.fc44.x86_64.rpm

This also points to the GPG checks being skipped during offline repo installation on boot.

In the case of a shared dnf local repository, it goes through the transaction and installs the packages, but fails at this stage:

filesystem error: cannot copy: Read-only file system [/var/cache/libdnf5/fedora-cff72538bc9825a4/packages/docbook-utils-0.6.15-6.fc44.noarch.rpm] [/host-repo/gpgcheck]

Note, i’m doing this through a virtual machine with the local repo shared using a virtiofs as a read-only file system. Because it’s a test VM I don’t want it to write to that repository as it is one less issue for the host, and it is shared alongside it and some other VMs, with minor differences between them.

This also results in the offline upgrade failing in the VM. Is there a way to tell DNF/RPM not to try to copy across to the local repo?

It looks like it skips openpgp check for all 7 packages that was upgraded.

Aug 16 06:37:30 xfce-43 dnf5[642]: [ 1/16] Verify package files            100% |  72.0   B/s |   7.0   B |  00m00s
Aug 16 06:37:30 xfce-43 dnf5[642]: [ 2/16] Prepare transaction             100% |  66.0   B/s |  14.0   B |  00m00s
Aug 16 06:37:31 xfce-43 dnf5[642]: [ 3/16] Upgrading grub2-common-1:2.12-6 100% |  78.1 MiB/s |   6.2 MiB |  00m00s
Aug 16 06:37:31 xfce-43 dnf5[642]: [ 4/16] Upgrading grub2-tools-minimal-1 100% |  55.9 MiB/s |   4.1 MiB |  00m00s
Aug 16 06:37:31 xfce-43 dnf5[642]: [ 5/16] Upgrading grub2-tools-1:2.12-64 100% |  82.0 MiB/s |   7.8 MiB |  00m00s
Aug 16 06:37:35 xfce-43 dnf5[642]: [ 6/16] Upgrading selinux-policy-0:44.6 100% |   8.0 KiB/s |  34.5 KiB |  00m04s
Aug 16 06:37:35 xfce-43 dnf5[642]: [ 7/16] Upgrading selinux-policy-target 100% |  74.9 MiB/s |  14.9 MiB |  00m00s
Aug 16 06:37:35 xfce-43 dnf5[642]: [ 8/16] Upgrading grub2-efi-x64-1:2.12- 100% |  97.5 MiB/s |   6.2 MiB |  00m00s
Aug 16 06:37:35 xfce-43 dnf5[642]: [ 9/16] Upgrading exfatprogs-0:1.4.3-1. 100% |   6.6 MiB/s | 382.7 KiB |  00m00s
Aug 16 06:37:35 xfce-43 dnf5[642]: [10/16] Removing grub2-efi-x64-1:2.12-6 100% | 259.0   B/s |  14.0   B |  00m00s
Aug 16 06:37:36 xfce-43 dnf5[642]: [11/16] Removing selinux-policy-0:44.5- 100% | 225.0   B/s |  18.0   B |  00m00s
Aug 16 06:37:36 xfce-43 dnf5[642]: [12/16] Removing grub2-tools-1:2.12-60. 100% |   1.0 KiB/s |  78.0   B |  00m00s
Aug 16 06:37:36 xfce-43 dnf5[642]: [13/16] Removing grub2-tools-minimal-1: 100% | 520.0   B/s |  26.0   B |  00m00s
Aug 16 06:37:36 xfce-43 dnf5[642]: [14/16] Removing grub2-common-1:2.12-60 100% |   1.1 KiB/s |  57.0   B |  00m00s
Aug 16 06:37:36 xfce-43 dnf5[642]: [15/16] Removing selinux-policy-targete 100% |  20.6 KiB/s |   1.7 KiB |  00m00s
Aug 16 06:37:49 xfce-43 dnf5[642]: [16/16] Removing exfatprogs-0:1.4.2-2.f 100% |   3.0   B/s |  41.0   B |  00m14s
Aug 16 06:37:49 xfce-43 dnf5[642]: Warning: skipped OpenPGP checks for 7 packages from repository: updates
Aug 16 06:37:49 xfce-43 dnf5[642]: Transaction complete! Cleaning up and rebooting...
Aug 16 06:37:49 xfce-43 dnf5[642]: Complete!

In the settings you have repo_gpgcheck=0 and gpgcheck=1. Which checks are actuall spipped? repo_gpgcheck or gpgcheck?