How to configure local customised repository for zypper based installation in SuSE Enterprise Linux

Earlier I had written an article with detailed list of step by step guide to create autoyast.xml file for automated scratch installation of SLES 11 and SLES 12

Step by Step Guide to create autoyast xml file for SuSE Linux (SLES) with examples

In this article I will show you detailed list of steps to create a custom repository for SuSE Linux Enterprise Linux.

Generate GPG Key

Before starting you will need a GPG key which will be used to sign the content of the repository. If you already have an existing gpg key then you can ignore this or else create a new gpg key for your custom repository

NOTE: Here the highlighted sections are the input which must be given for creating a key, you can give different input based on your requirement

# gpg --gen-key
gpg (GnuPG) 2.0.9; Copyright (C) 2008 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Please select what kind of key you want:
   (1) DSA and Elgamal (default)
   (2) DSA (sign only)
   (5) RSA (sign only)
Your selection? 5
RSA keys may be between 1024 and 4096 bits long.
What keysize do you want? (2048) 4096
Requested keysize is 4096 bits
Please specify how long the key should be valid.
         0 = key does not expire
      <n>  = key expires in n days
      <n>w = key expires in n weeks
      <n>m = key expires in n months
      <n>y = key expires in n years
Key is valid for? (0)
Key does not expire at all
Is this correct? (y/N) y

You need a user ID to identify your key; the software constructs the user ID
from the Real Name, Comment and Email Address in this form:
    "Heinrich Heine (Der Dichter) <>"

Real name: Deepak Prasad (GoLinuxHub)
Email address:
Comment: This is a test Key
You selected this USER-ID:
    "Deepak Prasad (GoLinuxHub) (This is a test Key) <>"

Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? O
You need a Passphrase to protect your secret key.
x New passphrase                                      x
x                                                     x
x                                                     x
x Passphrase ________________________________________ x
x                                                     x
x           <OK>                     <Cancel>         x

x Repeat passphrase                                   x
x                                                     x
x                                                     x
x Passphrase ________________________________________ x
x                                                     x
x           <OK>                     <Cancel>         x
can't connect to `/root/.gnupg/S.gpg-agent': No such file or directory
We need to generate a lot of random bytes. It is a good idea to perform
some other action (type on the keyboard, move the mouse, utilize the
disks) during the prime generation; this gives the random number
generator a better chance to gain enough entropy.
gpg: key 031D26CD marked as ultimately trusted
public and secret key created and signed.

gpg: checking the trustdb
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
pub   4096R/031D26CD 2018-06-21
      Key fingerprint = EF12 A620 E193 D165 AF2D  B60D 51EB 6A3E 4BF2 3A26
uid                  Deepak Prasad (GoLinuxHub) (This is a test Key) <>

Note that this key cannot be used for encryption.  You may want to use
the command "--edit-key" to generate a subkey for this purpose.

Now we have successfully generated a GPG key.

To check the details of the available keys on your node

 # gpg --list-keys
pub   1024D/9C800ACA 2000-10-19 [expired: 2018-03-17]
uid                  SuSE Package Signing Key <>

pub   1024R/307E3D54 2006-03-21 [expired: 2018-03-17]
uid                  SuSE Package Signing Key <>

pub   2048R/39DB7C82 2013-01-31 [expired: 2017-01-30]
uid                  SuSE Package Signing Key <>

pub   4096R/031D26CD 2018-06-21
uid                  Deepak Prasad (GoLinuxHub) (This is a test Key) <>

Here "031D26CD" is the GPG KEY ID which we will use for signing our contents of the repository

Create a text file with passphrase

Next create a plain text file with the passphrase which was used for the respective GPG key id

# echo "mypassw0rd" > /tmp/password

This will be used at a later stage.

Create directory structure

For this article I will create my directory structure under "/tmp/deeprasa" for the custom SuSE repository

Make sure below rpm is installed on your node


or you can download and install the same using zypper or rpm command.

Next navigate inside your home directory where you want to create the directory structure

# cd /tmp/deeprasa

# .
Creating ./updates..
/EXTRA_PROV not found, trying to find it elsewhere...
INFO:    datadirs       : ./updates/
INFO:    languages      : english
INFO:    output dir     : ./updates/
WARNING: extra_provides : file ./updates//EXTRA_PROV not found!
INFO:    processed 0 packages in 1 volumes
INFO:    now recoding to UTF-8: packages packages.DU packages.en

This will create below structure

golinuxhub-server:/tmp/deeprasa # ls -l *
total 20
-rw-r--r-- 1 root root    0 Jun 21 11:12 content
-rw-r--r-- 1 root root   42 Jun 21 11:12 directory.yast
drwxr-xr-x 2 root root 4096 Jun 21 11:12 media.1
-rw-r--r-- 1 root root   10 Jun 21 11:12 packages
-rw-r--r-- 1 root root   10 Jun 21 11:12 packages.DU
-rw-r--r-- 1 root root   10 Jun 21 11:12 packages.en

total 8
-rw-r--r-- 1 root root 11 Jun 21 11:12 instorder
-rw-r--r-- 1 root root 20 Jun 21 11:12 order

Copy the rpms for the custom repository

Next create directory under /tmp/deeprasa/updates where you will copy all the rpms

# cd /tmp/deeprasa/updates

Here we have created three directories where we will copy the rpms based on the architecture type.

# mkdir -p suse/x86_64 suse/i686 suse/noarch

My rpms are present inside /tmp/rpms

# cp -av /tmp/rpms/ suse/x86_64/
`/tmp/rpms/' -> `suse/x86_64/rpms'
`/tmp/rpms/bash-doc-3.2-147.35.1.x86_64.rpm' -> `suse/x86_64/rpms/bash-doc-3.2-147.35.1.x86_64.rpm'
`/tmp/rpms/bash-3.2-147.35.1.x86_64.rpm' -> `suse/x86_64/rpms/bash-3.2-147.35.1.x86_64.rpm'
`/tmp/rpms/bind-9.9.6P1-0.39.1.x86_64.rpm' -> `suse/x86_64/rpms/bind-9.9.6P1-0.39.1.x86_64.rpm'
`/tmp/rpms/bind-chrootenv-9.9.6P1-0.39.1.x86_64.rpm' -> `suse/x86_64/rpms/bind-chrootenv-9.9.6P1-0.39.1.x86_64.rpm'

Create necessary files

Once all the rpms are copied, next it is time to create all other necessary files and directories which are needed for the repository

# cd /tmp/deeprasa/updates/suse

# create_package_descr -x setup/descr/EXTRA_PROV -C
INFO:    datadirs       : .
INFO:    languages      : english
INFO:    output dir     : ./setup/descr/
WARNING: extra_provides : file setup/descr/EXTRA_PROV not found!
INFO:    creating output directory ./setup/descr/
INFO:    processed 8 packages in 1 volumes
INFO:    now recoding to UTF-8: packages packages.DU packages.en

Next create MD5SUMS file which will contain md5sum value of all the available rpms

# create_md5sums ./
INFO:   created MD5SUMS in /tmp/deeprasa/updates/suse/./setup/descr
INFO:   created MD5SUMS in /tmp/deeprasa/updates/suse/./x86_64

Below is my MD5SUMS file for the list of rpms

golinuxhub-server:/tmp/deeprasa/updates/suse/x86_64 # cat MD5SUMS
8b29f664006cab0187d18647e22dea87  bash-3.2-147.35.1.x86_64.rpm
d1d426cd61af5ee8ee971ea61418d023  bash-doc-3.2-147.35.1.x86_64.rpm
26f0829b54d2b8260c1c0f5efb7ac3d1  bind-9.9.6P1-0.39.1.x86_64.rpm
a3450462b957602502b85d21bcbf38c8  bind-chrootenv-9.9.6P1-0.39.1.x86_64.rpm

Next create a file with the content of setup/descr as shown below

golinuxhub-server:/tmp/deeprasa/updates/suse/x86_64 # cd ../setup/descr/

golinuxhub-server:/tmp/deeprasa/updates/suse/setup/descr # ls > directory.yast

golinuxhub-server:/tmp/deeprasa/updates/suse/setup/descr # ls -l
total 24
-rw-r--r-- 1 root root  135 Jun 21 11:41 MD5SUMS
-rw-r--r-- 1 root root   56 Jun 21 11:44 directory.yast
-rw-r--r-- 1 root root 4927 Jun 21 11:40 packages
-rw-r--r-- 1 root root 1766 Jun 21 11:40 packages.DU
-rw-r--r-- 1 root root 1684 Jun 21 11:40 packages.en

Next create sha1

# cd /tmp/deeprasa/updates/

golinuxhub-server:/tmp/deeprasa/updates/ # create_sha1sums -x -n .

I will use the default header as I had in my DVD for the columns under content

golinuxhub-server:/tmp/deeprasa/updates/ # sed -i '1iVENDOR        SUSE LINUX Products GmbH, Nuernberg, Germany' /tmp/deeprasa/updates/content

This will add populate your content file as shown below

# cat content
VENDOR        SUSE LINUX Products GmbH, Nuernberg, Germany
META SHA1 1206b18fb0b70c36ef39a1b2e9f105488836e42a  packages
META SHA1 1206b18fb0b70c36ef39a1b2e9f105488836e42a  packages.DU
META SHA1 1206b18fb0b70c36ef39a1b2e9f105488836e42a  packages.en

Assign GPG

Here our GPG KEY ID is "031D26CD" which we created at the first stage on this article.

# cd /tmp/deeprasa/updates/media.1
# gpg --local-user 031D26CD -b --sign --armor --passphrase-file /tmp/password --batch products
# gpg --local-user 031D26CD  --export --armor  > products.key
# ls > directory.yast

NOTE: Here /tmp/password contains the passphrase assigned to the GPG key.

Next repeat the same for contents

# cd /tmp/deeprasa/updates/
# gpg --local-user 031D26CD -b --sign --armor --passphrase-file /tmp/password  --batch content
# gpg --local-user 031D26CD --export --armor  > content.key

These will create

-rw-r--r-- 1 root root  197 Jun 21 14:36 products.asc
-rw-r--r-- 1 root root 5541 Jun 21 14:36 products.key


-rw-r--r-- 1 root root  197 Jun 21 14:36 content.asc
-rw-r--r-- 1 root root 5541 Jun 21 14:37 content.key


Create archive of the repo

I will navigate to the directory where my repo exists and create a "test_repo.tgz"

# cd /tmp/deeprasa/updates

# tar -czvf ../test_repo.tgz *

So here our repo structure is complete. You can archive this and use it for installation via zypper.

Validate the repo

I will copy the archive I created above to my test setup where we will validate the repository along with zypper

Below is my setup detail

So I will be creating a repo on my NFS client while the archive will be extracted on the server

On Server

# mkdir /tmp/repo && cd /tmp/repo
# tar -xzvf test_repo.tgz

Below is the extracted content

# ls -l
total 1108
-rw-r--r-- 1 root root     248 Jun 21  2018 content
-rw-r--r-- 1 root root     197 Jun 21  2018 content.asc
-rw-r--r-- 1 root root    5541 Jun 21  2018 content.key
-rw-r--r-- 1 root root      42 Jun 21  2018 directory.yast
drwxr-xr-x 2 root root    4096 Jun 21  2018 media.1
-rw-r--r-- 1 root root      10 Jun 21  2018 packages
-rw-r--r-- 1 root root      10 Jun 21  2018 packages.DU
-rw-r--r-- 1 root root      10 Jun 21  2018 packages.en
drwxr-xr-x 4 root root    4096 Jun 21  2018 suse

On Client
Now on the client side I will create a repo with an alias "test_repo"

# zypper addrepo nfs:// test_repo
Adding repository 'test_repo' [done]
Repository 'test_repo' successfully added
Enabled: Yes
Autorefresh: No
GPG check: Yes
URI: nfs://

Our repo is successfully created

Let us check the available packages

# zypper pa
Building repository 'test_repo' cache [done]
Loading repository data...
Reading installed packages...
S | Repository | Name           | Version        | Arch
v | test_repo  | bash           | 3.2-147.35.1   | x86_64
v | test_repo  | bash-doc       | 3.2-147.35.1   | x86_64
  | test_repo  | bind           | 9.9.6P1-0.39.1 | x86_64
  | test_repo  | bind-chrootenv | 9.9.6P1-0.39.1 | x86_64

So all looks good.

I hope the article was useful.