Creating apt repository with reprepro on Ubuntu

If you create deb packages and would like to distribute them in a more centralized way instead of asking your users to download them and install them manually, then setting up an apt repository is the way to go.
The following walk-through sets up signed Ubuntu repository.

1. Install GnuPG and generate a GPG key.

In order to create a secure APT repository you might want to  sign your debian packages. You'll need a GPG key in order to do that.

[root@host1 ~]# apt-get install gnupg
[root@host1 ~]# apt-get install dpkg-sig
[root@host1 ~]# gpg --gen-key
[root@host1 ~]# dpkg-sig --sign builder some-package_1.12.9_ubuntu.deb
view raw gistfile1.sh hosted with ❤ by GitHub

The “builder” name mentioned is a debian convention, indicating that the builder of the package signed it. The GPG key used will be the one you set up above, providing you’re running the command as the same user you set up the key with.

For more information on how to create a deb package read this article.

2. Install and Configure reprepro.

root@host1 ~]# apt-get install reprepro
view raw gistfile1.sh hosted with ❤ by GitHub

3. Create the file system layout for the repository.

If you would like to support multiple platforms besides Ubuntu, then you need to create similar directory structure specifying the distribution name as outlined bellow:
[root@host1 ~]# mkdir -p /var/packages/ubuntu/
[root@host1 ~]# mkdir /var/packages/ubuntu/conf
[root@host1 ~]# mkdir -p /var/packages/debian/
[root@host1 ~]# mkdir /var/packages/debian/conf
view raw gistfile1.sh hosted with ❤ by GitHub

4. Configuring the repository.

The reprepo utility that will create the repository uses four config files, which are searched in the directory specified with --confdir or in the conf/ subdirectory of the basedir.

If a file options exists, it is parsed line by line. Each line can be the long name of a command line option (without the --) plus an argument, where possible. Those are handled as if they were command line options given before (and thus lower priority than) any other command line option. (and also lower priority than any environment variable).

To allow command line options to override options file options, most boolean options also have a corresponding form starting with --no.

The file distributions is always needed and describes what distributions to manage, while updates is only needed when syncing with external repositories and pulls is only needed when syncing with repositories in the same reprepro database.

The last three are in the format control files in Debian are in, i.e. paragraphs separated by empty lines consisting of fields. Each field consists of a fieldname, followed by a colon, possible whitespace and the data. A field ends with a newline not followed by a space or tab.

[root@host1 ~]# cd /var/packages/ubuntu/conf
[root@host1:/var/packages/ubuntu/conf]# cat distributions
Origin: linux-admins.net
Label: linux-admins.net
Codename: precise
Architectures: i386 amd64 source
Components: main
Description: Konstantins APT Repository
SignWith: yes
DebOverride: override.precise
DscOverride: override.precise
[root@host1:/var/packages/ubuntu/conf]]# cat options
verbose
ask-passphrase
basedir.
view raw gistfile1.sh hosted with ❤ by GitHub

The other two files "updates" and "pulls" can be omitted if not needed.

The important options in the distribution configuration files are:
Codename - this required field is the unique identifier of a distribution and used as directory name within dists/ It is also copied into the Release files. Note that this name is not supposed to change. You most likely never ever want a name like testing or stable here (those are suite names and supposed to point to another distribution later).

Architectures - this required field lists the binary architectures within this distribution and if it contains source (i.e. if there is an item source in this line this Distribution has source. All other items specify things to be put after "binary-" to form directory names and be checked against "Architecture:" fields). This will also be copied into the Release files. (With exception of the source item, which will not occur in the topmost Release file whether it is present here or not).

SignWith - when this field is present, a Release.gpg file will be generated. If the value is "yes" or "default", the default key of gpg is used. Otherwise the value will be given to libgpgme to determine to key to use. If there are problems with signing, you can try gpg --list-secret-keys value to see how gpg could interprete the value. If that command does not list any keys or multiple ones, try to find some other value (like the keyid), that gpg can more easily associate with a unique key. If this key has a passphrase, you need to use gpg-agent or the insecure option --ask-passphrase.

DscOverride - when this field is present, it describes the override file used when including .dsc files.

For the purpose of this tutorial the override.precise file can be left empty:
[root@host1:/var/packages/ubuntu/conf]# touch override.precise
view raw gistfile1.sh hosted with ❤ by GitHub

5. Building the repository and adding packages to it.

To add a deb package to the repository run:

[root@host1 ~]# cd /var/packages/ubuntu
[root@host1:/var/packages/ubuntu]# reprepro includedeb precise /root/some-package_1.12.9_ubuntu.deb
view raw gistfile1.sh hosted with ❤ by GitHub

To list the available deb packages in the repository execute:

[root@host1:/var/packages/ubuntu]# reprepro list precise
precise|main|i386: some-package 1.12.9
precise|main|amd64: some-package 1.12.9
view raw gistfile1.sh hosted with ❤ by GitHub

To remove the package from the repository run:

[root@host1:/var/packages/ubuntu]# reprepro remove precise some-package
view raw gistfile1.sh hosted with ❤ by GitHub

6. Configuring repository access. 

The easiest way to make the repository accessible is by configuring a virtual host in Apache.

[root@host1 ~]# cat /etc/apache2/sites-available/repo
<VirtualHost *>
DocumentRoot /var/packages
ServerName packages.linux-admins.net
ErrorLog /var/log/apache2/error.log
LogLevel warn
CustomLog /var/log/apache2/access.log combined
ServerSignature On
# Allow directory listings so that people can browse the repository from their browser too
<Directory "/var/packages">
Options Indexes FollowSymLinks MultiViews
DirectoryIndex index.html
AllowOverride Options
Order allow,deny
allow from all
</Directory>
# Hide the conf/ directory for all repositories
<Directory "/var/packages/*/conf">
Order allow,deny
Deny from all
Satisfy all
</Directory>
# Hide the db/ directory for all repositories
<Directory "/var/packages/*/db">
Order allow,deny
Deny from all
Satisfy all
</Directory>
</VirtualHost>
[root@host1 ~]# a2ensite repo
[root@host1 ~]# service apache2 reload

Copy the pgp key to the root of your web server so customers can download it and install it on their systems:

[root@host1 ~]# cd /var/www/
[root@host1:/var/www/]# gpg --armor --output packages.linux-admins.key --export Konstantin
view raw gistfile1.sh hosted with ❤ by GitHub

Now on one of your customers systems download the repository key and add it to the keyring:

[root@client ~]# curl -H GET packages.linux-admins.net/packages.linux-admins.key > packages.linux-admins.key
[root@client ~]# apt-key add packages.linux-admins.key
view raw gistfile1.sh hosted with ❤ by GitHub

Then add the new repositry source in /etc/apt/sources.list or in a separate file in /etc/apt/sources.list.d/

[root@client ~]#cat /etc/apt/sources.list
deb http://packages.linux-admins.net/ubuntu precise main
deb-src http://packages.linux-admins.net/ubuntu precise main
[root@client ~]#apt-get update
[root@client ~]#apt-get install some-package
view raw gistfile1.sh hosted with ❤ by GitHub

One drawback of using reprepro is that you can't have multiple versions of the same package. Once you add the latest version of a package the previous one will be automatically deleted.