This the multi-page printable view of this section. Click here to print.

Return to the regular view of this page.

Package Management in Photon OS with 'tdnf'

Photon OS manages packages with an open source, yum-compatible package manager called tdnf, for Tiny Dandified Yum. Tdnf keeps the operating system as small as possible while preserving yum’s robust package-management capabilities.

1 - Introduction to 'tdnf'

On Photon OS, tdnf is the default package manager for installing new packages. It is a C implementation of the DNF package manager without Python dependencies.

Tdnf appears in the minimal and full versions of Photon OS.

Tdnf implements a subset of the dnf commands as listed in the dnf guide.

2 - Configuration Files and Repositories

The main configuration files reside in /etc/tdnf/tdnf.conf. The configuration file appears as follows:

cat /etc/tdnf/tdnf.conf
[main]
gpgcheck=1
installonly_limit=3
clean_requirements_on_remove=true
repodir=/etc/yum.repos.d
cachedir=/var/cache/tdnf

The cache files for data and metadata reside in /var/cache/tdnf.

The following repositories appear in /etc/yum.repos.d/ with .repo file extensions:

ls /etc/yum.repos.d/
photon-extras.repo
photon-iso.repo
photon-updates.repo
photon.repo 

You can list the the repositories by using the tdnf repolist command. Tdnf filters the results with enabled, disabled, and all. Running the command without specifying an argument returns the enabled repositories:

tdnf repolist
repo id             repo name                               status
photon-updates      VMware Photon Linux 2.0(x86_64)Updates  enabled
photon-extras       VMware Photon Extras 2.0(x86_64)        enabled
photon              VMware Photon Linux 2.0(x86_64)         enabled

The photon-iso.repo, however, does not appear in the list of repositories because it is unavailable on the virtual machine from which these examples are taken. The photon-iso.repo is the default repository and it points to /media/cdrom. The photon-iso.repo appears as follows:

cat /etc/yum.repos.d/photon-iso.repo
[photon-iso]
name=VMWare Photon Linux 2.0(x86_64)
baseurl=file:///mnt/cdrom/RPMS
gpgkey=file:///etc/pki/rpm-gpg/VMWARE-RPM-GPG-KEY
gpgcheck=1
enabled=0
skip_if_unavailable=True

The local cache is populated with data from the repository:

ls -l /var/cache/tdnf/photon
total 8
drwxr-xr-x 2 root root 4096 May 18 22:52 repodata
d-wxr----t 3 root root 4096 May  3 22:51 rpms

You can clear the cache to help troubleshoot a problem, but doing so might slow the performance of tdnf until the cache becomes repopulated with data. To clear the cache, use the following command:

tdnf clean all
Cleaning repos: photon photon-extras photon-updates lightwave
Cleaning up everything

The command purges the repository data from the cache:

ls -l /var/cache/tdnf/photon
total 4
d-wxr----t 3 root root 4096 May  3 22:51 rpms

3 - Adding a New Repository

On Photon OS, you can add a new repository from which tdnf installs packages. To add a new repository, you create a repository configuration file with a .repo extension and place it in /etc/yum.repos.d. The repository can be on either the Internet or a local server containing your in-house applications.

Be careful if you add a repository that is on the Internet. Installing packages from untrusted or unverified sources might put the security, stability, or compatibility of your system at risk. It might also make your system harder to maintain.

On Photon OS, the existing repositories appear in the /etc/yum.repos.d directory:

ls /etc/yum.repos.d/
photon-extras.repo
photon-iso.repo
photon-updates.repo
photon.repo 

To view the the format and information that a new repository configuration file should contain, see one of the .repo files. The following is an example:

baseurl=https://https://packages.vmware.com/photon/
metalink=http://example.com/*username*/metalink/metalink
gpgkey=file:///etc/pki/rpm-gpg/VMWARE-RPM-GPG-KEY
gpgcheck=1
enabled=1
skip_if_unavailable=True

The repository settings details are as follows:

  • The minimal information needed to establish a repository is an ID and human-readable name of the repository and its base URL. The ID, which appears in square brackets, must be one word that is unique amoung the system’s repositories; `.

  • The baseurl is a URL for the repository’s repodata directory. For a repository on a local server that can be accessed directly or mounted as a file system, the base URL can be a file referenced by file://. Example:

    baseurl=file:///server/repo/

  • By using metalink, you can point to multiple URLs to download the repomd.xml file. A sample metalink file is as follows:

    cat metalink

    <?xml version="1.0" encoding="utf-8"?>
    
    <metalink version="3.0" xmlns="http://www.metalinker.org/" type="dynamic" pubdate="Wed, 05 Feb 2020 08:14:56 GMT" generator="mirrormanager" xmlns:mm0="http://fedorahosted.org/mirrormanager">
    
     <files>
    
      <file name="repomd.xml">
    
       <size>2035</size>
    
       <verification>
    
    <hash type="sha1">478437547dac9f5a73fe905d2ed2a0a5b153ef46</hash>
    
    <hash type="sha512">6c6fbfba288ec90905a8d2220a0bfd2a50e835b7faaefedb6978df6ca59c5bce25cc1ddd33023e305b20bcffc702ee2bd61d0855f4f1b2fd7c8f5109e428a764</hash>
    
       </verification>
    
       <resources maxconnections="1">
    
    <url protocol="http" type="http" location="IN" preference=“100”>https://packages.vmware.com/photon/3.0/photon_updates_3.0_x86_64/repodata/repomd.xml</url>
    
       </resources>
    
      </file>
    
     </files>
    
    </metalink>
    

    In the metalink file, provide the preference for each url, so tdnf first tries to sync the repository data from the mirror which has the highest preference. If it fails due to any reason, tdnf will sync to the next mirror url with the lower preference than before one.

    Note: Ensure that the shasum for respomd.xml in all the mirrors should be same

  • The gpgcheck setting specifies whether to check the GPG signature.

  • The repo_gpgcheck setting allows tdnf to verify the signature of a repository metadata before downloading the repository artifacts. When repo_gpgcheck is set to 1 in the tdnf.conf file, all repositories will be checked for the metadata signatures. The default value is 0. If a repository has repo_gpgcheck enabled,a repomd.xml.asc file is downloaded and the API equivalent of gpg --verify repomd.xml.asc repomd.xml is done. If repomd.xml.asc is missing, repository is disabled. If repomd.xml.asc fails to verify, the repository is disabled. The public key for verification must be manually installed for the initial implementation.

    Note: Ensure that you have installed libgcrypt for this implementation.

  • The gpgkey setting furnishes the URL for the repository’s ASCII-armored GPG key file. tdnf uses the GPG key to verify a package if its key has not been imported into the RPM database.

    The repository configuration also supports public keys that are remote for the gpgkey option. So, the URLs starting with http, https, or ftp can be used for gpgkey.

    For example: gpgkey=http://build-squid.eng.vmware.com/build/mts/release/bora-16633979/publish/packages/keys/vmware.asc

  • The enabled setting tells tdnf whether to poll the repository. If enabled is set to 1, tdnf polls it; if it is set to 0, tdnf ignores it.

  • The skip_if_unavailable setting instructs tdnf to continue running if the repository goes offline.

  • The retries setting in the repository configuration specifies the number of retries when downloading a file throws an error. The default is 10.

  • The timeout setting specifies the number of seconds that a download is allowed to take or 0 for no limit. Note that this is an absolute value and may interrupt large file downloads.

  • The minrate setting specifies the limit below which if the download rate falls, tdnf will abort the download. The default value is 0 (no limit).

  • The maxrate setting specifies the maximum download rate (throttle). The default value is 0 (no limit).

  • Other options and variables can appear in the repository file. The variables that are used with some of the options can reduce future changes to the repository configuration files. There are variables to replace the value of the version of the package and to replace the base architecture. For more information, see the man page for yum.conf on the full version of Photon OS: man yum.conf

The following is an example of how to add a new repository for a local server that tdnf polls for packages:

cat > /etc/yum.repos.d/apps.repo << "EOF"
[localapps]
name=Local In-House Applications(x86_64)
baseurl=file:///appserver/apps
enabled=1
skip_if_unavailable=True
EOF

Because this new repository resides on a local server, make sure the Photon OS machine can connect to it by mounting it.

After establishing a new repository, you must run the following command to update the cached binary metadata for the repositories that tdnf polls:

tdnf makecache
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)Updates'
Refreshing metadata for: 'VMware Photon Extras 1.0(x86_64)'
Refreshing metadata for: 'Local In-House Applications(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)'
Metadata cache created.

4 - Mount the Photon ISO Image for the Photon-ISO Repository

Photon OS comes with a preconfigured repository called photon-iso that resides in \etc\yum.repos.d. If you receive an access error message when working with the photon-iso repository, it is probably because you do not have the Photon OS ISO mounted. Mount the ISO and the run the following command to update the metadata for all known repositories, including photon-iso:

mount /dev/cdrom /media/cdrom
tdnf makecache

Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)Updates'
Refreshing metadata for: 'VMware Photon Extras 1.0(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)'
Metadata cache created.

5 - Adding the Dev Repository to Get New Packages from the GitHub Dev Branch

To try out new packages or the latest versions of existing packages as they are merged into the dev branch of the Photon OS GitHub site, add the dev repository to your repository list.

Perform th following steps:

  1. On your Photon OS machine, run the following command as root to create a repository configuration file named photon-dev.repo, place it in /etc/yum.repos.d, and concatenate the repository information into the file:
cat > /etc/yum.repos.d/photon-dev.repo << "EOF" 
    [photon-dev]
    name=VMware Photon Linux Dev(x86_64)
    baseurl=https://packages.vmware.com/photon/dev/photon_dev_$basearch
    gpgkey=file:///etc/pki/rpm-gpg/VMWARE-RPM-GPG-KEY
    gpgcheck=1
    enabled=1
    skip_if_unavailable=True
    EOF
    .
  1. After establishing a new repository, run the following command to update the cached binary metadata for the repositories that tdnf polls:
tdnf makecache

6 - tdnf-automatic

tdnf-automatic is an alternative Command Line Interface (CLI) to tdnf upgrade/tdnf update with specific features so that it is suitable to be executed automatically and regularly from systemd timers, cron jobs, and so on.

The operation of the tool is usually controlled by the configuration file or the function-specific timer units. The command only accepts a single optional argument pointing to the config file, and some control arguments intended for use by the services that back the timer units. If no configuration file is passed from the command line,then /etc/tdnf/automatic.conf is used.

The tool synchronizes package metadata as needed and then checks for the updates available for the given system and then either exits or shows available updates or downloads and installs the packages.

The outcome of the operation is then reported through stdio.

The systemd timer unit tdnf-automatic.timer behaves as the configuration file specifies whether to download and apply updates. Some other timer units are provided which override the configuration file with some standard behaviors:

* tdnf-automatic-notifyonly

* tdnf-automatic-install

Irrespective of the configuration file settings, the first only notifies of available updates. The second one downloads and installs the updates.

Run tdnf-automatic

You can select one that most closely fits your needs, customize /etc/tdnf/automatic.conf for any specific behaviors, and enable the timer unit.

For example: systemctl enable –now tdnf-automatic-notifyonly.timer

Configuration file format

The configuration file is separated into two sections. This basically gives info on what can be put in /etc/tdnf/automatic.conf. ‘automatic.conf’ is a configuration INI file.

Format

tdnf-automatic help:

tdnf-automatic [{-c|--conf config-file}(optional)] [{-i|--install}] [{-n|--notify}] [{-h|--help}] [{-v|--version}]



-c, --conftdnf-automatic configuration file (Optional argument)

-i, --installOverride automatic.conf apply_updates and install updates

-n, --notifyShow available updates

-h, --helpShow this help message

-v, --versionShow tdnf-automatic version information

Commands

To set the mode of the operation of the program:

  • apply_updates (boolean, default: no) Whether packages comprising the available updates should be applied by tdnf-automatic.timer, i.e. installed via RPM. Note that the other timer units override this setting.

  • show_updates (boolean, default: yes) To just receive updates use tdnf-automatic-notifyonly.timer

  • network_online_timeout (time in seconds, default: 60) Maximum time tdnf-automatic will wait until the system is online. 0 means that network availability detection will be skipped.

  • random_sleep (time in seconds, default: 0) Maximum random delay before downloading. Note that, by default, the systemd timers also apply a random delay of up to 1 hour.

  • upgrade_type (either one of all or security. default: all) Looks at the kind of upgrades. all signals looking for all available updates. security indicates only those with an issued security advisory.

  • tdnf_conf (string, default: /etc/tdnf/tdnf.conf) Configurations to override default tdnf configuration.

Reports

To select how the results should be reported:

  • emit_to_stdio (boolean, default: yes) Report the results through stdio. If no, no report will be shown.

  • system_name (string, default: hostname of the given system) How the system is called in the reports.

  • emit_to_file (string, absolute path of file) If we want to capture the logs in a file

7 - Install Packages from CLI

You can install the packages from the command line. The package can be a file or a URL. The dependencies are installed automatically.

For example:

  • Using a URL:

      tdnf install https://packages.vmware.com/photon/4.0/photon_release_4.0_x86_64/x86_64/open-vm-tools-11.2.5-1.ph4.x86_64.rpm
    
      open-vm-tools-11.2.5-1.ph4.x86_64.rpm 763014   100%
    
      Installing:
    
      attrx86_642.4.48-1.ph4  photon  88.65k 90778
    
      nss x86_643.57-2.ph4photon  1.69M 1768005
    
      ...
    
      open-vm-tools   x86_6411.2.5-1.ph4  @cmdline2.65M 2779392
    
    
      Total installed size:  91.57M 96019175
    
    
      Upgrading:
    
      nss-libsx86_643.57-2.ph4photon  2.48M 2601790
    
      util-linux-libs x86_642.36-2.ph4photon752.75k 770816
    
      pcre-libs   x86_648.44-2.ph4photon275.60k 282216
    
    
    
      Total installed size:   3.49M 3654822
    
      Is this ok [y/N]: 
    
  • Using a file:

      tdnf install ../lsof-4.91-1.ph4.x86_64.rpm 
    
    
      Installing:
    
      libtirpcx86_641.2.6-1.ph4   photon193.56k 198209
    
      lsofx86_644.91-1.ph4@cmdline  196.10k 200810
    
      Total installed size: 389.67k 399019

8 - SSL Options

Photon OS offers support for the SSL Options.

You can set the following SSL options in the repository configuration file:

  • sslverify When downloading using https, this option helps to verify the SSL certificate of the server. You can set it to 0 or 1. The default is 1.

  • sslcacert You can use this option to set the path to a certificate file to verify the server.

  • sslclientcert You can use this option to set the path to a client certificate file.

  • sslclientkey You can set this path to the client key file.

9 - Standard Syntax for tdnf Commands

The standard syntax for tdnf commands is the same as that for DNF and is as follows:

tdnf [options] <command> [<arguments>...]

You can view help information by using the following commands:

tdnf --help
tdnf -h

9.1 - tdnf Commands

check: Checks for problems in installed and available packages for all enabled repositories. The command has no arguments. You can use --enablerepo and --disablerepo to control the repos used. Supported in Photon OS 2.0 (only).

check-local: This command resolves dependencies by using the local RPMs to help check RPMs for quality assurance before publishing them. To check RPMs with this command, you must create a local directory and place your RPMs in it. The command, which includes no options, takes the path to the local directory containing the RPMs as its argument. The command does not recursively parse directories. It checks the RPMs only in the directory that you specify. For example, after creating a directory named /tmp/myrpms and placing your RPMs in it, you can run the following command to check them:

tdnf check-local /tmp/myrpms
Checking all packages from: /tmp/myrpms
Found 10 packages
Check completed without issues

check-update: This command checks for updates to packages. It takes no arguments. The tdnf list updates command performs the same function. Here is an example of the check update command:

tdnf check-update
rpm-devel.x86_64 	4.11.2-8.ph1 	photon
yum.noarch      	3.4.3-3.ph1 	photon

clean: This command cleans up temporary files, data, and metadata. It takes the argument all. Example:

tdnf clean all
Cleaning repos: photon photon-extras photon-updates
Cleaning up everything

distro-sync: This command synchronizes the machine’s RPMs with the latest version of all the packages in the repository. The following is an abridged example:

tdnf distro-sync

Upgrading:
zookeeper                             x86_64        3.4.8-2.ph1               3.38 M
yum                                   noarch        3.4.3-3.ph1               4.18 M

Total installed size: 113.01 M

Reinstalling:
zlib-devel                            x86_64        1.2.8-2.ph1             244.25 k
zlib                                  x86_64        1.2.8-2.ph1             103.93 k
yum-metadata-parser                   x86_64        1.1.4-1.ph1              57.10 k

Total installed size: 1.75 G

Obsoleting:
tftp                                  x86_64        5.2-3.ph1                32.99 k

Total installed size: 32.99 k
Is this ok [y/N]:

downgrade: This command downgrades the package that you specify as an argument to the next lower package version. The following is an example:

tdnf downgrade boost
Downgrading:
boost                                 x86_64        1.56.0-2.ph1              8.20 M
Total installed size: 8.20 M
Is this ok [y/N]:y
Downloading:
boost                                  2591470    100%
Testing transaction
Running transaction
Complete!

To downgrade to a version lower than the next one, you must specify it by name, epoch, version, and release, all properly hyphenated. The following is an example:

tdnf downgrade boost-1.56.0-2.ph1 

erase: This command removes the package that you specify as an argument.

To remove a package, run the following command:

tdnf erase pkgname

The following is an example:

tdnf erase vim
Removing:
vim                                   x86_64        7.4-4.ph1                 1.94 M
Total installed size: 1.94 M
Is this ok [y/N]:

You can also erase multiple packages:

tdnf erase docker cloud-init

info: This command displays information about packages. It can take the name of a package. Or it can take one of the following arguments: all, available, installed, extras, obsoletes, recent, upgrades. The following are examples:

tdnf info ruby
tdnf info obsoletes
tdnf info upgrades

install: This command takes the name of a package as its argument. It then installs the package and its dependencies.

To install a package, run the following command:

tdnf install pkgname

The following are examples:

tdnf install kubernetes

You can also install multiple packages:

tdnf install python-curses lsof audit gettext chkconfig ntsysv bindutils 
     wget gawk irqbalance lvm2 cifs-utils c-ares distrib-compat

list: This command lists the packages of the package that you specify as the argument. The command can take one of the following arguments: all, available, installed, extras, obsoletes, recent, upgrades.

tdnf list updates

The list of packages might be long. To more easily view it, you can concatenate it into a text file, and then open the text file in a text editor:

tdnf list all > pkgs.txt
vi pkgs.txt

To list enabled repositories, run the following command:

tdnf repolist

makecache: This command updates the cached binary metadata for all known repositories. The following is an example:

tdnf makecache
Refreshing metadata for: 'VMware Lightwave 1.0(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)Updates'
Refreshing metadata for: 'VMware Photon Extras 1.0(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)'
Metadata cache created.

provides: This command finds the packages that provide the package that you supply as an argument. The following is an example:

tdnf provides docker
docker-1.11.0-1.ph1.x86_64 : Docker
Repo     : photon
docker-1.11.0-1.ph1.x86_64 : Docker
Repo     : @System

reinstall: This command reinstalls the packages that you specify. If some packages are unavailable or not installed, the command fails. The following is an example:

tdnf reinstall docker kubernetes

Reinstalling:
kubernetes                            x86_64        1.1.8-1.ph1             152.95 M
docker                                x86_64        1.11.0-1.ph1             57.20 M

Total installed size: 210.15 M

remove: This command removes a package. When removing a package, tdnf by default also removes dependencies that are no longer used if they were was installed by tdnf as a dependency without being explicitly requested by a user. You can modify the dependency removal by changing the clean_requirements_on_remove option in /etc/tdnf/tdnf.conf to false.

tdnf remove packagename

search: This command searches for the attributes of packages. The argument can be the names of packages. The following is an example:

tdnf search docker kubernetes
docker : Docker
docker : Docker
docker-debuginfo : Debug information for package docker
docker : Docker
kubernetes : Kubernetes cluster management
kubernetes : Kubernetes cluster management
kubernetes-debuginfo : Debug information for package kubernetes
kubernetes : Kubernetes cluster management

The argument of the search command can also be a keyword or a combination of keywords and packages:

tdnf search terminal bash
rubygem-terminal-table : Simple, feature rich ascii table generation library
ncurses : Libraries for terminal handling of character screens
mingetty : A minimal getty program for virtual terminals
ncurses : Libraries for terminal handling of character screens
ncurses : Libraries for terminal handling of character screens
bash : Bourne-Again SHell
bash-lang : Additional language files for bash
bash-lang : Additional language files for bash
bash : Bourne-Again SHell
bash-debuginfo : Debug information for package bash
bash : Bourne-Again SHell
bash-lang : Additional language files for bash

updateinfo: This command displays security advisories about packages. The following is an example:

tdnf updateinfo info

Name : unzip-6.0-15.ph3.x86_64.rpm
Update ID : patch:PHSA-2020-3.0-0083
Type : Security
Updated : Fri Apr 24 01:15:03 2020
Needs Reboot: 0
Description : Security fixes for {'CVE-2018-1000035'}
Name : runc-1.0.0.rc9-3.ph3.x86_64.rpm
Update ID : patch:PHSA-2020-3.0-0102
Type : Security
Updated : Tue Jun  9 06:01:28 2020
Needs Reboot: 0
Description : Security fixes for {'CVE-2019-19921'}
Name : ruby-2.5.8-2.ph3.x86_64.rpm
Update ID : patch:PHSA-2020-3.0-0163
Type : Security
Updated : Thu Nov 19 17:21:29 2020
Needs Reboot: 0

upgrade: This command upgrades the package or packages that you specify to an available higher version that tdnf can resolve. If the package is already the latest version, the command returns Nothing to do. The following is an example:

tdnf upgrade boost

Upgrading:
boost                                 x86_64        1.60.0-1.ph1              8.11 M

Total installed size: 8.11 M
Is this ok [y/N]:y

Downloading:
boost                                  2785950    100%
Testing transaction
Running transaction

Complete!

You can also run the upgrade command with the refresh option to update the cached metadata with the latest information from the repositories. The following example refreshes the metadata and then checks for a new version of tdnf but does not find one, so tdnf takes no action:

tdnf upgrade tdnf --refresh
Refreshing metadata for: 'VMware Lightwave 1.0(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)Updates'
Refreshing metadata for: 'VMware Photon Extras 1.0(x86_64)'
Refreshing metadata for: 'VMware Photon Linux 1.0(x86_64)'
Nothing to do.

upgrade-to: This command upgrades to the version of the package that you specify. The following is an example:

tdnf upgrade-to ruby2.3

The commands and options of tdnf are a subset of those of dnf. For more help with tdnf commands, see the DNF documentation.

9.2 - tdnf Command Options

You can add the following options to tdnf commands. If the option to override a configuration is unavailable in a command, you can add it to the /etc/tdnf/tdnf.conf configuration file.

OPTION                     DESCRIPTION
--allowerasing             Allow erasing of installed packages to resolve dependencies
--assumeno                 Answer no for all questions
--best                     Try the best available package versions in transactions
--debugsolver              Dump data aiding in dependency solver debugging info.
--disablerepo=<repoid>     Disable specific repositories by an id or a glob.
--enablerepo=<repoid>      Enable specific repositories
-h, --help                 Display help
--refresh                  Set metadata as expired before running command
--nogpgcheck               Skip gpg check on packages
--rpmverbosity=<debug level name>
                           Debug level for rpm
--version                  Print version and exit
-y, --assumeyes            Answer yes to all questions
-q, --quiet                Quiet operation
--downloadonly             Enables you to download the packages and dependencies that are
                           not installed to the cache.
--downloaddir=dir          Downloads the packages to the specified directory 

The following is an example that adds the short form of the assumeyes option to the install command:

tdnf -y install gcc
Upgrading:
gcc 	x86_64	5.3.0-1.ph1 	91.35 M

The following is an example for the downloadonly option with the install command:

tdnf install --downloadonly less
    
Installing:
    
lessx86_64551-2.ph4 photon234.35k 239976
       
Total installed size: 234.35k 239976
  
tdnf will only download packages needed for the transaction
   
Is this ok [y/N]: y

Downloading:
   
less117650   100%
    
Complete!
   
Packages have been downloaded to cache.

The following is an example for the downloaddir=dir option with the install command:

tdnf install --downloadonly --downloaddir=/tmp less
 
Installing:

lessx86_64551-2.ph4 photon234.35k 239976
        
Total installed size: 234.35k 239976

tdnf will only download packages needed for the transaction

Is this ok [y/N]: y

Downloading:

less117650   100%

    
Complete!

Packages have been downloaded to /tmp.

root [ /build/build ]# ls -l /tmp/less-551-2.ph4.x86_64.rpm 

-rw-r--r-- 1 root root 117650 Feb 22 18:43 /tmp/less-551-2.ph4.x86_64.rpm