<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en" xml:base="https://vincent.bernat.ch">
  <title>Vincent Bernat — Distribution &gt; Debian</title>
  <link href="https://vincent.bernat.ch/en/blog/atom-distribution-debian.xml" rel="self"/>
  <link href="https://vincent.bernat.ch/en/blog#tag-distribution-debian" rel="alternate"/>
  <id>http://www.luffy.cx/en/blog/atom-distribution-debian.xml/</id>
  <updated>2026-09-08T05:27:44Z</updated>

  <entry>
    <title type="html">Downgrading all Debian packages to a specific date</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2020-downgrade-debian" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2020-downgrade-debian#isso-thread" rel="replies" type="text/html"/>
    <updated>2020-01-30T20:58:39Z</updated>
    <id>http://www.luffy.cx/en/blog/2020-downgrade-debian.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<p>Unlike <a href="https://nixos.org/" title="Linux distribution with a unique approach to package and configuration management">NixOS</a>, Debian does not have a built-in mechanism to roll back
an installation to a specific point in time. However, thanks to
<a href="http://snapshot.debian.org/">snapshot.debian.org</a>, a wayback machine for Debian packages, it is
possible to downgrade all packages to the versions from a chosen
date.</p>
<p>Let’s suppose we want to go back to January 20<sup>th</sup>, 2020. In
<code>/etc/apt/sources.list.d/snapshot.list</code>, we add a date-specific
snapshot as a source:</p>
<div class="language-debian-sourcelist codehilite"><pre><span/><code><span class="k">deb</span><span class="w"> </span><span class="sx">[check-valid-until=no]</span><span class="w"> </span><span class="kp">https://snapshot.debian.org/archive/debian/20200120T111800Z/</span><span class="w"> </span><span class="kp">unstable</span><span class="w"> </span><span class="kp">main</span><span class="w"> </span><span class="kp">contrib</span><span class="w"> </span><span class="kp">non-free</span>
</code></pre></div>

<p>In <code>/etc/apt/preferences.d/snapshot.pref</code>, we set the priority of all
packages from this source to 1001. This is above the default priority
of 500 and over 1000 to allow downgrade. See <a href="https://manpages.debian.org/apt_preferences.5.html#How_APT_Interprets_Priorities" title="Manual page for apt_preferences(5)"><code>apt_preferences(5)</code>
manual page</a> for more details.</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">*</span>
<span class="k">Pin</span><span class="p">:</span><span class="w"> </span><span class="s">origin snapshot.debian.org</span>
<span class="k">Pin-Priority</span><span class="p">:</span><span class="w"> </span><span class="s">1001</span>
</code></pre></div>

<p>After running <code>apt update</code>, we can check the result with <code>apt policy</code>:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>apt<span class="w"> </span>policy
<span class="go">Package files:</span>
<span class="go"> 100 /var/lib/dpkg/status</span>
<span class="go">     release a=now</span>
<span class="go">1001 https://snapshot.debian.org/archive/debian/20200120T111800Z unstable/non-free amd64 Packages</span>
<span class="go">     release o=Debian,a=unstable,n=sid,l=Debian,c=non-free,b=amd64</span>
<span class="go">     origin snapshot.debian.org</span>
<span class="go">1001 https://snapshot.debian.org/archive/debian/20200120T111800Z unstable/contrib amd64 Packages</span>
<span class="go">     release o=Debian,a=unstable,n=sid,l=Debian,c=contrib,b=amd64</span>
<span class="go">     origin snapshot.debian.org</span>
<span class="go">1001 https://snapshot.debian.org/archive/debian/20200120T111800Z unstable/main amd64 Packages</span>
<span class="go">     release o=Debian,a=unstable,n=sid,l=Debian,c=main,b=amd64</span>
<span class="go">     origin snapshot.debian.org</span>
<span class="go">[…]</span>
</code></pre></div>

<p>When requesting an <em>upgrade</em>, we will effectively be proposed to
downgrade everything to the versions in the snapshot:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>sudo<span class="w"> </span>apt<span class="w"> </span>upgrade
<span class="go">Reading package lists... Done</span>
<span class="go">Building dependency tree</span>
<span class="go">Reading state information... Done</span>
<span class="go">Calculating upgrade... Done</span>
<span class="go">The following NEW packages will be installed:</span>
<span class="go">  libdns-export1104 libgspell-1-1 libiptc0 libisc-export1100</span>
<span class="hll"><span class="go">The following packages will be DOWNGRADED:</span>
</span><span class="go">  bash-completion bear binutils binutils-common binutils-dev binutils-doc</span>
<span class="go">  binutils-x86-64-linux-gnu bison blackbird-gtk-theme blender blender-data</span>
<span class="go">  clang-6.0 clang-7 clang-8 clang-tools-8 cpp-9 cppcheck cpuid cups</span>
<span class="go">  […]</span>
<span class="go">0 upgraded, 4 newly installed, 451 downgraded, 0 to remove and 0 not upgraded.</span>
<span class="go">Need to get 1,053 MB/1,060 MB of archives.</span>
<span class="go">After this operation, 9,282 kB disk space will be freed.</span>
<span class="go">Do you want to continue? [Y/n]</span>
</code></pre></div>
      </div>
    </content>
  </entry>
  <entry>
    <title type="html">Pragmatic Debian packaging</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2019-pragmatic-debian-packaging" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2019-pragmatic-debian-packaging#isso-thread" rel="replies" type="text/html"/>
    <updated>2019-05-27T18:12:54Z</updated>
    <id>http://www.luffy.cx/en/blog/2019-pragmatic-debian-packaging.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<div class="admonition">
<p class="admonition-title">Notice</p>
<p>This guide is an updated version of a <a href="/en/blog/2016-pragmatic-debian-packaging">previous
edition</a>. If you need to
target distributions older than Debian Stretch and Ubuntu Bionic, have a
look at the older version instead.</p>
</div>
<p>While the creation of Debian packages is <a href="https://www.debian.org/doc/manuals/maint-guide/" title="New Maintainer's Guide">abundantly</a>
<a href="https://www.debian.org/doc/manuals/maint-guide/" title="New Maintainer's Guide">documented</a>, most <a href="https://www.debian.org/doc/manuals/packaging-tutorial/packaging-tutorial" title="Lucas Nussbaum's packaging tutorial">tutorials</a> are targeted to
packages implementing the <a href="https://www.debian.org/doc/debian-policy/" title="The Debian Policy Manual">Debian policy</a>. Moreover, Debian
packaging has <strong>a reputation for being unnecessarily
difficult</strong><sup id="fnref:difficulty"><a class="footnote-ref" href="#fn:difficulty">1</a></sup> and many people prefer to use less
constrained tools,<sup id="fnref:fpm"><a class="footnote-ref" href="#fn:fpm">2</a></sup> like <a href="https://github.com/jordansissel/fpm" title="Effing package management">fpm</a> or <a href="https://web.archive.org/web/2016/http://checkinstall.izto.org/" title="CheckInstall">CheckInstall</a>.</p>
<p>However, building Debian packages with the official tools can become
straightforward if you bend some rules:</p>
<ol>
<li>
<p><strong>No source package will be generated</strong>.  Packages will be built
    directly from a checkout of a <abbr title="Version Control System">VCS</abbr> repository.</p>
</li>
<li>
<p><strong>Additional dependencies can be downloaded during the
    build</strong>. Packaging individually each dependency is painstaking
    work, notably when you have to deal with some fast-paced
    ecosystems like Java, JavaScript, and Go.</p>
</li>
<li>
<p>The produced packages may <strong>bundle dependencies</strong>. This is likely
    to raise some concerns about security and long-term maintenance,
    but this is a common trade-off in many ecosystems, notably Java,
    JavaScript and Go.</p>
</li>
</ol>
<div class="toc">
<ul>
<li><a href="#pragmatic-packages-101">Pragmatic packages 101</a><ul>
<li><a href="#the-control-file">The control file</a></li>
<li><a href="#the-build-recipe">The build recipe</a></li>
</ul>
</li>
<li><a href="#pragmatic-packages-102">Pragmatic packages 102</a><ul>
<li><a href="#build-dependencies">Build dependencies</a></li>
<li><a href="#runtime-dependencies">Runtime dependencies</a></li>
<li><a href="#integration-with-the-init-system">Integration with the init system</a></li>
</ul>
</li>
<li><a href="#pragmatic-packages-103">Pragmatic packages 103</a><ul>
<li><a href="#the-big-picture">The big picture</a></li>
<li><a href="#automatic-builds">Automatic builds</a></li>
<li><a href="#install-additional-files">Install additional files</a></li>
</ul>
</li>
<li><a href="#other-examples">Other examples</a></li>
</ul>
</div>
<h1 id="pragmatic-packages-101">Pragmatic packages 101</h1>
<p>In the Debian archive, you have two kinds of packages: the <strong>source
packages</strong> and the <strong>binary packages</strong>. Each binary package is built
from a source package. You need a name for each package.</p>
<p>As stated in the introduction, we won’t generate a source package but
we will work with its unpacked form which is any source tree
containing a <code>debian/</code> directory. In our examples, we will start with
a source tree containing only a <code>debian/</code> directory but you are free
to include this <code>debian/</code> directory into an existing project.</p>
<p>As an example, we will package <a href="https://memcached.org/" title="memcached - a distributed memory object caching system">memcached</a>, a distributed memory
cache. You need to create four files:</p>
<ul>
<li><code>debian/compat</code>;</li>
<li><code>debian/changelog</code>;</li>
<li><code>debian/control</code>; and</li>
<li><code>debian/rules</code>.</li>
</ul>
<p>The first one is easy. Put <code>11</code> in it:<sup id="fnref:compat"><a class="footnote-ref" href="#fn:compat">3</a></sup></p>
<div class="language-bash codehilite"><pre><span/><code><span class="nb">echo</span><span class="w"> </span><span class="m">11</span><span class="w"> </span>&gt;<span class="w"> </span>debian/compat
</code></pre></div>

<p>The second one has the following content:</p>
<div class="language-text-only codehilite"><pre><span/><code>memcached (0.0-0) UNRELEASED; urgency=medium

  * Fake entry

 -- Happy Packager &lt;happy@example.com&gt;  Tue, 19 Apr 2016 22:27:05 +0200
</code></pre></div>

<p>The only important information is the name of the source package,
<code>memcached</code>, on the first line. Everything else can be left as is as
it won’t influence the generated binary packages.</p>
<h2 id="the-control-file">The control file</h2>
<p><code>debian/control</code> describes the metadata of both the source package and
the generated binary packages. We have to write a block for each of
them.</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Maintainer</span><span class="p">:</span><span class="w"> </span>Vincent Bernat <span class="gs">&lt;bernat@debian.org&gt;</span>

<span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Architecture</span><span class="p">:</span><span class="w"> </span><span class="s">any</span>
<span class="k">Description</span><span class="gs">: high-performance memory object caching system</span>
</code></pre></div>

<p>The source package is called <code>memcached</code>. We have to use the same name
as in <code>debian/changelog</code>.</p>
<p>We generate only one binary package: <code>memcached</code>. In the remainder of
the example, when you see <code>memcached</code>, this is the name of a binary
package. The <code>Architecture</code> field should be set to either <code>any</code> or
<code>all</code>. Use <code>all</code> exclusively if the package contains only
arch-independent files. In doubt, just stick to <code>any</code>.</p>
<p>The <code>Description</code> field contains a short description of the binary package.</p>
<h2 id="the-build-recipe">The build recipe</h2>
<p>The last mandatory file is <code>debian/rules</code>. It is the <strong>recipe</strong> for the
package. We need to retrieve <em>memcached</em>, build it, and install its
file tree in <code>debian/memcached/</code>. It looks like this:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>

<span class="nv">DISTRIBUTION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>shell<span class="w"> </span>sed<span class="w"> </span>-n<span class="w"> </span><span class="s2">"s/^VERSION_CODENAME=//p"</span><span class="w"> </span>/etc/os-release<span class="k">)</span>
<span class="nv">VERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span>.6.6
<span class="nv">PACKAGEVERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>VERSION<span class="k">)</span>-0~<span class="k">$(</span>DISTRIBUTION<span class="k">)</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>memcached-<span class="k">$(</span>VERSION<span class="k">)</span>.tar.gz
<span class="nv">URL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>http://www.memcached.org/files/<span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span>

<span class="nf">override_dh_auto_clean</span><span class="o">:</span>
<span class="nf">override_dh_auto_test</span><span class="o">:</span>
<span class="nf">override_dh_auto_build</span><span class="o">:</span>
<span class="nf">override_dh_auto_install</span><span class="o">:</span>
<span class="w">    </span>wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="k">$(</span>URL<span class="k">)</span>
<span class="w">    </span>tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="k">$(</span>TARBALL<span class="k">)</span>
<span class="w">    </span>./configure<span class="w"> </span>--prefix<span class="o">=</span>/usr
<span class="w">    </span>make
<span class="w">    </span>make<span class="w"> </span>install<span class="w"> </span><span class="nv">DESTDIR</span><span class="o">=</span>debian/memcached

<span class="nf">override_dh_gencontrol</span><span class="o">:</span>
<span class="w">    </span>dh_gencontrol<span class="w"> </span>--<span class="w"> </span>-v<span class="k">$(</span>PACKAGEVERSION<span class="k">)</span>
</code></pre></div>

<p>The empty targets <code>override_dh_auto_clean</code>, <code>override_dh_auto_test</code>
and <code>override_dh_auto_build</code> keep <em>debhelper</em> from being too
smart. The <code>override_dh_gencontrol</code> target sets the package
version<sup id="fnref:version"><a class="footnote-ref" href="#fn:version">4</a></sup> without updating <code>debian/changelog</code>. If you ignore
the slight boilerplate, the recipe is quite similar to what you would
have done with <code>fpm</code>:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="nv">DISTRIBUTION</span><span class="o">=</span><span class="k">$(</span>sed<span class="w"> </span>-n<span class="w"> </span><span class="s2">"s/^VERSION_CODENAME=//p"</span><span class="w"> </span>/etc/os-release<span class="k">)</span>
<span class="nv">VERSION</span><span class="o">=</span><span class="m">1</span>.6.6
<span class="nv">PACKAGEVERSION</span><span class="o">=</span><span class="si">${</span><span class="nv">VERSION</span><span class="si">}</span>-0~<span class="si">${</span><span class="nv">DISTRIBUTION</span><span class="si">}</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="o">=</span>memcached-<span class="si">${</span><span class="nv">VERSION</span><span class="si">}</span>.tar.gz
<span class="nv">URL</span><span class="o">=</span>http://www.memcached.org/files/<span class="si">${</span><span class="nv">TARBALL</span><span class="si">}</span>

wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="si">${</span><span class="nv">URL</span><span class="si">}</span>
tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="si">${</span><span class="nv">TARBALL</span><span class="si">}</span>
./configure<span class="w"> </span>--prefix<span class="o">=</span>/usr
make
make<span class="w"> </span>install<span class="w"> </span><span class="nv">DESTDIR</span><span class="o">=</span>/tmp/installdir

<span class="c1"># Build the final package</span>
fpm<span class="w"> </span>-s<span class="w"> </span>dir<span class="w"> </span>-t<span class="w"> </span>deb<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-n<span class="w"> </span>memcached<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-v<span class="w"> </span><span class="si">${</span><span class="nv">PACKAGEVERSION</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-C<span class="w"> </span>/tmp/installdir<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>--description<span class="w"> </span><span class="s2">"high-performance memory object caching system"</span>
</code></pre></div>

<p>You can review the whole package tree on <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/memcached-basic/debian">GitHub</a>
and build it with the <code>dpkg-buildpackage -us -uc -b</code> command or with
<code>GIT_PBUILDER_OPTIONS=--use-network=yes DIST=bionic git-pbuilder -us
-uc -b</code> if you already set up a tool like <code>pbuilder</code>.</p>
<h1 id="pragmatic-packages-102">Pragmatic packages 102</h1>
<p>At this point, we can iterate and add several improvements to our
<em>memcached</em> package. <strong>None of them are mandatory</strong> but they are
usually worth the additional effort.</p>
<h2 id="build-dependencies">Build dependencies</h2>
<p>Our initial build recipe only works when several packages are
installed, like <code>wget</code> and <code>libevent-dev</code>. They are not present on all
Debian systems. You can easily express that you need them by adding a
<code>Build-Depends</code> section for the source package in <code>debian/control</code>:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Build-Depends</span><span class="p">:</span><span class="w"> </span><span class="nf">debhelper</span> <span class="p">(</span><span class="o">&gt;=</span><span class="w"> </span><span class="m">11</span><span class="p">)</span>,
<span class="w"> </span>              <span class="nf">wget</span>, <span class="nf">ca-certificates</span>,
<span class="w"> </span>              <span class="nf">libevent-dev</span>
</code></pre></div>

<p>Always specify the <code>debhelper (&gt;= 11)</code> dependency as we heavily rely on
it. We don’t require <code>make</code> or a C compiler because it is assumed that
the <code>build-essential</code> meta-package is installed and it pulls
them. <code>dpkg-buildpackage</code> will complain if the dependencies are not
met. If you want to install these packages from your <abbr title="Continuous integration">CI</abbr> system, you
can use the following command:<sup id="fnref:devscripts"><a class="footnote-ref" href="#fn:devscripts">5</a></sup></p>
<div class="language-bash codehilite"><pre><span/><code>mk-build-deps<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-t<span class="w"> </span><span class="s1">'apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends -qqy'</span><span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-i<span class="w"> </span>-r<span class="w"> </span>debian/control
</code></pre></div>

<p>You may also want to investigate <a href="https://pbuilder-team.pages.debian.net/pbuilder/" title="pbuilder user's manual">pbuilder</a>, <a href="https://wiki.debian.org/sbuild" title="sbuild page on Debian Wiki">sbuild</a> or
<a href="https://www.uhoreg.ca/programming/debian/whalebuilder" title="Debian package builder using Docker">whalebuilder</a>, three tools to build Debian packages in a clean
isolated environment.</p>
<h2 id="runtime-dependencies">Runtime dependencies</h2>
<p>If the resulting package is installed on a freshly installed machine,
it won’t work because it will be missing <em>libevent</em>, a required
library for <em>memcached</em>. You can express the dependencies needed by
each binary package by adding a <code>Depends</code> field. Moreover, for dynamic
libraries, you can automatically get the right dependencies by using
some substitution variables:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Depends</span><span class="p">:</span><span class="w"> </span><span class="o">$</span><span class="p">{</span><span class="ni">misc</span><span class="p">:</span>Depends<span class="p">}</span>, <span class="o">$</span><span class="p">{</span><span class="ni">shlibs</span><span class="p">:</span>Depends<span class="p">}</span>
</code></pre></div>

<p>The resulting package will contain the following information:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>dpkg<span class="w"> </span>-I<span class="w"> </span>../memcached_1.6.6-0<span class="se">\~</span>buster0_amd64.deb<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>Depends
<span class="go"> Depends: libc6 (&gt;= 2.17), libevent-2.1-6 (&gt;= 2.1.8-stable)</span>
</code></pre></div>

<h2 id="integration-with-the-init-system">Integration with the init system</h2>
<p>Most packaged daemons come with some integration with the init system.
This integration ensures the daemon will be started on boot and
restarted on upgrade. While Debian still supports several init systems,
in my opinion, the most pragmatic one is <strong>systemd</strong>.</p>
<p>The content of a <em>systemd</em> unit should go in
<code>debian/memcached.service</code>. For example:</p>
<div class="language-systemd codehilite"><pre><span/><code><span class="k">[Unit]</span>
<span class="na">Description</span><span class="o">=</span><span class="s">memcached daemon</span>
<span class="na">After</span><span class="o">=</span><span class="s">network.target</span>

<span class="k">[Service]</span>
<span class="na">Type</span><span class="o">=</span><span class="s">forking</span>
<span class="na">Environment</span><span class="o">=</span><span class="s">PORT=11211</span>
<span class="na">Environment</span><span class="o">=</span><span class="s">MAXCONN=1024</span>
<span class="na">Environment</span><span class="o">=</span><span class="s">CACHESIZE=64</span>
<span class="na">Environment</span><span class="o">=</span><span class="s">OPTIONS=</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/memcached -d -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS</span>
<span class="na">Restart</span><span class="o">=</span><span class="s">on-failure</span>
<span class="na">User</span><span class="o">=</span><span class="s">_memcached</span>
<span class="na">DynamicUser</span><span class="o">=</span><span class="s">yes</span>

<span class="k">[Install]</span>
<span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</code></pre></div>

<p>The <code>Type</code> directive is quite important. We used <code>forking</code> as
<em>memcached</em> is started with the <code>-d</code> flag and will fork when it is
ready to accept requests. If you use a non-forking daemon, you can
either use <code>notify</code> for a daemon with some support for <em>systemd</em> or
<code>simple</code> otherwise.</p>
<p>A user can customize one of the <code>Environment</code> directives by using
<code>systemctl edit memcached.service</code> to create an override which will be
installed in <code>/etc/systemd/system/memcached.service.d/override.conf</code>.
<code>systemctl cat memcached.service</code> can be used to check the final
definition of the unit.</p>
<p>The <code>DynamicUser</code> directive is also quite interesting. <em>systemd</em>
automatically creates a <code>_memcached</code> user<sup id="fnref:naming"><a class="footnote-ref" href="#fn:naming">6</a></sup> and runs the daemon as
this user. This frees us from managing a system user ourselves!</p>
<p>You can review the whole package tree on
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/memcached-intermediate/debian">GitHub</a> and build it with
the <code>dpkg-buildpackage -us -uc -b</code> command.</p>
<h1 id="pragmatic-packages-103">Pragmatic packages 103</h1>
<p>It is possible to leverage <em>debhelper</em> to <strong>reduce the recipe size</strong>
and to make it <strong>more declarative</strong>. This section is quite optional
and it requires understanding a bit more how a Debian package is
built. Feel free to skip it.</p>
<h2 id="the-big-picture">The big picture</h2>
<p>There are <strong>four steps</strong> to build a regular Debian package:</p>
<ol>
<li>
<p><code>debian/rules clean</code> <strong>cleans</strong> the source tree to make it
    pristine.</p>
</li>
<li>
<p><code>debian/rules build</code> <strong>builds</strong> the code. For an
    autoconf-based software, like <em>memcached</em>, this step should
    execute something like <code>./configure &amp;&amp; make</code>.</p>
</li>
<li>
<p><code>debian/rules install</code> <strong>installs</strong> files at the correct location
    for each binary package. For an autoconf-based software, this step
    should execute <code>make install DESTDIR=debian/memcached</code>.</p>
</li>
<li>
<p><code>debian/rules binary</code> <strong>packs</strong> the different file trees into
    binary packages.</p>
</li>
</ol>
<p>You don’t directly write each of these targets. Instead, you let <code>dh</code>,
a component of <em>debhelper</em>, do most of the work. The following
<code>debian/rules</code> file should do almost everything correctly with many
source packages:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>
<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span>
</code></pre></div>

<p>For each of the four targets described above, you can run <code>dh</code>
with <code>--no-act</code> to see what it would do. For example:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>dh<span class="w"> </span>build<span class="w"> </span>--no-act
<span class="go">   dh_testdir</span>
<span class="go">   dh_update_autotools_config</span>
<span class="go">   dh_auto_configure</span>
<span class="go">   dh_auto_build</span>
<span class="go">   dh_auto_test</span>
</code></pre></div>

<p>Each of these helpers has a manual page. Helpers starting with
<code>dh_auto_</code> are a bit “magic.” For example, <code>dh_auto_configure</code> will
try to automatically configure a package before building: it will
detect the build system and invoke <code>./configure</code>, <code>cmake</code>, or
<code>Makefile.PL</code>.</p>
<p>If one of the helpers does not do the “right” thing, you can replace it
by using an override target:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="nf">override_dh_auto_configure</span><span class="o">:</span>
<span class="w">    </span>./configure<span class="w"> </span>--with-some-grog
</code></pre></div>

<p>These helpers are also configurable, so you can just alter a bit their
behavior by invoking them with additional options:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="nf">override_dh_auto_configure</span><span class="o">:</span>
<span class="w">    </span>dh_auto_configure<span class="w"> </span>--<span class="w"> </span>--with-some-grog
</code></pre></div>

<p>This way, <code>./configure</code> will be called with your custom flag but also
with a lot of default flags like <code>--prefix=/usr</code> for better
integration. A manual page is available for each tool.</p>
<p>In the initial <em>memcached</em> example, we overrode all these “magic”
targets. <code>dh_auto_clean</code>, <code>dh_auto_configure</code>, and <code>dh_auto_build</code> are
converted to no-ops to avoid any unexpected
behavior. <code>dh_auto_install</code> is hijacked to do all the build
process. Additionally, we modified the behavior of the <code>dh_gencontrol</code>
helper by forcing the version number instead of using the one from
<code>debian/changelog</code>.</p>
<h2 id="automatic-builds">Automatic builds</h2>
<p>As <em>memcached</em> is an autoconf-enabled package, <strong><code>dh</code> knows how
to build it</strong>: <code>./configure &amp;&amp; make &amp;&amp; make install</code>. Therefore, we
can let it handle most of the work with this <code>debian/rules</code> file:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>

<span class="nv">DISTRIBUTION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>shell<span class="w"> </span>sed<span class="w"> </span>-n<span class="w"> </span><span class="s2">"s/^VERSION_CODENAME=//p"</span><span class="w"> </span>/etc/os-release<span class="k">)</span>
<span class="nv">VERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span>.6.6
<span class="nv">PACKAGEVERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>VERSION<span class="k">)</span>-0~<span class="k">$(</span>DISTRIBUTION<span class="k">)</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>memcached-<span class="k">$(</span>VERSION<span class="k">)</span>.tar.gz
<span class="nv">URL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>http://www.memcached.org/files/<span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span><span class="w"> </span>--with<span class="w"> </span>systemd

<span class="nf">override_dh_update_autotools_config</span><span class="o">:</span>
<span class="w">    </span>wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="k">$(</span>URL<span class="k">)</span>
<span class="w">    </span>tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">override_dh_auto_test</span><span class="o">:</span>
<span class="c">    # Don't run the whitespace test</span>
<span class="w">    </span>rm<span class="w"> </span>t/whitespace.t
<span class="w">    </span>dh_auto_test

<span class="nf">override_dh_gencontrol</span><span class="o">:</span>
<span class="w">    </span>dh_gencontrol<span class="w"> </span>--<span class="w"> </span>-v<span class="k">$(</span>PACKAGEVERSION<span class="k">)</span>
</code></pre></div>

<p>The <code>dh_update_autotools_config</code> target is hijacked to download and
set up the source tree. We do not override the <code>dh_auto_configure</code> step,
so <code>dh</code> will execute the <code>./configure</code> script with the appropriate
options. We do not override the <code>dh_auto_build</code> step either: <code>dh</code> will
execute <code>make</code>. <code>dh_auto_test</code> is invoked after the build and it will
run the <em>memcached</em> test suite. We need to override it because one of
the tests is complaining about odd whitespaces in the <code>debian/</code>
directory. We suppress this rogue test and let <code>dh_auto_test</code> execute
the test suite. <code>dh_auto_install</code> is not overridden either, so <code>dh</code>
will execute some variant of <code>make install</code>.</p>
<p>To get a better sense of the difference, here is a diff:</p>
<div class="language-diff codehilite"><pre><span/><code><span class="gd">--- memcached-intermediate/debian/rules 2019-05-31 07:52:40.908868035 +0200</span>
<span class="gi">+++ memcached/debian/rules      2019-05-31 07:28:17.404380064 +0200</span>
<span class="gu">@@ -9,15 +9,14 @@</span>
<span class="w"> </span>%:
<span class="w"> </span>   dh $@

<span class="gd">-override_dh_auto_clean:</span>
<span class="gd">-override_dh_auto_test:</span>
<span class="gd">-override_dh_auto_build:</span>
<span class="gd">-override_dh_auto_install:</span>
<span class="gi">+override_dh_update_autotools_config:</span>
<span class="w"> </span>   wget -N --progress=dot:mega $(URL)
<span class="w"> </span>   tar --strip-components=1 -xf $(TARBALL)
<span class="gd">-   ./configure --prefix=/usr</span>
<span class="gd">-   make</span>
<span class="gd">-   make install DESTDIR=debian/memcached</span>
<span class="gi">+</span>
<span class="gi">+override_dh_auto_test:</span>
<span class="gi">+   # Don't run the whitespace test</span>
<span class="gi">+   rm t/whitespace.t</span>
<span class="gi">+   dh_auto_test</span>

<span class="w"> </span>override_dh_gencontrol:
<span class="w"> </span>   dh_gencontrol -- -v$(PACKAGEVERSION)
</code></pre></div>

<p>It is up to you to decide if <code>dh</code> can do some work for you, but
you could try to start from a minimal <code>debian/rules</code> and only override
some targets.</p>
<h2 id="install-additional-files">Install additional files</h2>
<p>While <code>make install</code> installed the essential files for <em>memcached</em>,
you may want to <strong>put additional files</strong> in the binary package. You
could use <code>cp</code> in your build recipe, but you can also declare them:</p>
<ul>
<li>files listed in <code>debian/memcached.docs</code> will be copied to
   <code>/usr/share/doc/memcached</code> by <code>dh_installdocs</code></li>
<li>files listed in <code>debian/memcached.examples</code> will be copied to
   <code>/usr/share/doc/memcached/examples</code> by <code>dh_installexamples</code></li>
<li>files listed in <code>debian/memcached.manpages</code> will be copied to the
   appropriate subdirectory of <code>/usr/share/man</code> by <code>dh_installman</code></li>
</ul>
<p>Here is an example using wildcards for <code>debian/memcached.docs</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>doc/*.txt
</code></pre></div>

<p>If you need to copy some files to an arbitrary location, you can list
them along with their destination directories in
<code>debian/memcached.install</code> and <code>dh_install</code> will take care of the
copy. Here is an example:</p>
<div class="language-text-only codehilite"><pre><span/><code>scripts/memcached-tool usr/bin
</code></pre></div>

<p>Using these files makes the build process <strong>more declarative</strong>. It is
a matter of taste, and you are free to use <code>cp</code> in <code>debian/rules</code>
instead. You can review the whole package tree on
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/memcached/debian">GitHub</a>.</p>
<h1 id="other-examples">Other examples</h1>
<p>The <a href="https://github.com/vincentbernat/pragmatic-debian-packages" title="Git repository full of pragmatic Debian packages">Git repository</a> contains some additional
examples. They all follow the same scheme:</p>
<ul>
<li><code>dh_update_autotools_config</code> is hijacked to download and set up the source tree</li>
<li><code>dh_gencontrol</code> is modified to use a computed version</li>
</ul>
<p>Notably, you will find daemons in <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/riemann/debian">Java</a>, <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/etcd/debian">Go</a>, <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/puppetboard/debian">Python</a> and
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/master/kiwiirc/debian">Node.js</a>. The goal of these examples is to demonstrate that using
Debian tools to build Debian packages can be straightforward. Hope
this helps.</p>
<div class="admonition">
<p class="admonition-title">Update (2019-05)</p>
<p>This guide has been updated to use <code>override_dh_update_autotools_config</code> to
fetch source files instead of <code>override_dh_auto_clean</code> as it interacts
better with most build tools, like <code>pbuilder</code> and <code>sbuild</code>.</p>
</div>
<div class="footnote">
<hr/>
<ol>
<li id="fn:difficulty">
<p>People may remember the time before <em>debhelper</em> 7.0.50
(circa 2009) where <code>debian/rules</code> was a daunting
beast. However, nowadays, the boilerplate is quite
reduced. <a class="footnote-backref" href="#fnref:difficulty" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:fpm">
<p>The complexity is not the only reason. These alternative tools
enable the creation of RPM packages, something that Debian
tools don’t. <a class="footnote-backref" href="#fnref:fpm" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:compat">
<p>This compatibility level is available from Debian 9 and
Ubuntu Bionic. Therefore, this covers modern distributions.
However, it suffers from a <a href="https://bugs.debian.org/887904" title="dh_installsystemd will unmask services *after* an attempt to start them">bug preventing a service to
start</a> after installing, removing, and installing again a
package. Downgrading to 10 makes the packaging a bit more complex.
If you can, you may consider upgrading to 12 instead—available
since Debian 10 and Ubuntu Focal. <a class="footnote-backref" href="#fnref:compat" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:version">
<p>There are many ways to version a package. Again, if you
want to be pragmatic, the proposed solution should be good
enough for Ubuntu. On Debian, it doesn’t cover upgrade
from one distribution version to another, but we assume
that nowadays, systems get reinstalled instead of being
upgraded. <a class="footnote-backref" href="#fnref:version" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:devscripts">
<p>You also need to install <code>devscripts</code> and <code>equivs</code> packages. <a class="footnote-backref" href="#fnref:devscripts" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
<li id="fn:naming">
<p>The Debian Policy does not provide any hint for the naming
convention of these system users. A common usage is to prefix the
daemon name with an underscore (like <code>_memcached</code>). Another common
usage is to use <code>Debian-</code> as a prefix. The main drawback of the
latter solution is that the name is likely to be replaced by the
UID in <code>ps</code> and <code>top</code> because of its length.</p>
<p>If no <code>User</code> directive is present, <em>systemd</em> will use the name
of the service as the name of the user. If you agree with this
choice, you can omit this directive. <a class="footnote-backref" href="#fnref:naming" title="Jump back to footnote 6 in the text">↩</a></p>
</li>
</ol>
</div>
      </div>
    </content>
  </entry>
  <entry>
    <title type="html">Packaging an out-of-tree module for Debian with DKMS</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2018-packaging-driver-debian-dkms" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2018-packaging-driver-debian-dkms#isso-thread" rel="replies" type="text/html"/>
    <updated>2018-03-07T16:38:52Z</updated>
    <id>http://www.luffy.cx/en/blog/2018-packaging-driver-debian-dkms.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<blockquote>
<p><a href="https://github.com/dkms-project/dkms" title="Dynamic Kernel Module Support on GitHub"><abbr title="Dynamic Kernel Module Support">DKMS</abbr></a> is a framework designed to allow individual kernel modules to
be upgraded without changing the whole kernel. It is also very easy
to rebuild modules as you upgrade kernels.</p>
</blockquote>
<p>On Debian-like systems,<sup id="fnref:rpm"><a class="footnote-ref" href="#fn:rpm">1</a></sup> <em><abbr title="Dynamic Kernel Module Support">DKMS</abbr></em> enables the installation of
various drivers, from <a href="https://packages.debian.org/sid/zfs-dkms" title="ZFS on Linux module package for Debian">ZFS on Linux</a> to <a href="https://packages.debian.org/sid/virtualbox-dkms" title="VirtualBox kernel module package for Debian">VirtualBox kernel
modules</a> or <a href="https://packages.debian.org/sid/nvidia-kernel-dkms" title="NVIDIA kernel module package for Debian">NVIDIA drivers</a>. These out-of-tree modules are not
distributed as binaries: once installed, they need to be compiled for
your current kernel. Everything is done automatically:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp"># </span>apt<span class="w"> </span>install<span class="w"> </span>zfs-dkms
<span class="go">Reading package lists... Done</span>
<span class="go">Building dependency tree</span>
<span class="go">Reading state information... Done</span>
<span class="go">The following additional packages will be installed:</span>
<span class="go">  binutils cpp cpp-6 dkms fakeroot gcc gcc-6 gcc-6-base libasan3 libatomic1 libc-dev-bin libc6-dev</span>
<span class="go">  libcc1-0 libcilkrts5 libfakeroot libgcc-6-dev libgcc1 libgomp1 libisl15 libitm1 liblsan0 libmpc3</span>
<span class="go">  libmpfr4 libmpx2 libnvpair1linux libquadmath0 libstdc++6 libtsan0 libubsan0 libuutil1linux libzfs2linux</span>
<span class="go">  libzpool2linux linux-compiler-gcc-6-x86 linux-headers-4.9.0-6-amd64 linux-headers-4.9.0-6-common</span>
<span class="go">  linux-headers-amd64 linux-kbuild-4.9 linux-libc-dev make manpages manpages-dev patch spl spl-dkms</span>
<span class="go">  zfs-zed zfsutils-linux</span>
<span class="go">[…]</span>
<span class="go">3 upgraded, 44 newly installed, 0 to remove and 3 not upgraded.</span>
<span class="go">Need to get 42.1 MB of archives.</span>
<span class="go">After this operation, 187 MB of additional disk space will be used.</span>
<span class="go">Do you want to continue? [Y/n]</span>
<span class="go">[…]</span>
<span class="gp"># </span>dkms<span class="w"> </span>status
<span class="go">spl, 0.6.5.9, 4.9.0-6-amd64, x86_64: installed</span>
<span class="go">zfs, 0.6.5.9, 4.9.0-6-amd64, x86_64: installed</span>
<span class="gp"># </span>modinfo<span class="w"> </span>zfs<span class="w"> </span><span class="p">|</span><span class="w"> </span>head
<span class="go">filename:       /lib/modules/4.9.0-6-amd64/updates/dkms/zfs.ko</span>
<span class="go">version:        0.6.5.9-5</span>
<span class="go">license:        CDDL</span>
<span class="go">author:         OpenZFS on Linux</span>
<span class="go">description:    ZFS</span>
<span class="go">srcversion:     42C4AB70887EA26A9970936</span>
<span class="go">depends:        spl,znvpair,zcommon,zunicode,zavl</span>
<span class="go">retpoline:      Y</span>
<span class="go">vermagic:       4.9.0-6-amd64 SMP mod_unload modversions</span>
<span class="go">parm:           zvol_inhibit_dev:Do not create zvol device nodes (uint)</span>
</code></pre></div>

<p>If you install a new kernel, a compilation of the module is
automatically triggered.</p>
<h1 id="building-your-own-dkms-enabled-package">Building your own <abbr title="Dynamic Kernel Module Support">DKMS</abbr>-enabled package</h1>
<p>Suppose you’ve gotten your hands on an <a href="https://www.intel.com/content/www/us/en/products/sku/95260/intel-ethernet-network-adapter-xxv710da2/specifications.html" title="Intel Ethernet Network Adapter XXV710-DA2">Intel XXV710-DA2</a> <abbr title="Network Interface Card">NIC</abbr>. This
card is handled by the <code>i40e</code> driver. Unfortunately, it only got
support from Linux 4.10 and you are using a stock 4.9 Debian Stretch
kernel. <em><abbr title="Dynamic Kernel Module Support">DKMS</abbr></em> provides here an easy solution!</p>
<p>Download the <a href="https://www.intel.com/content/www/us/en/download/18026/intel-network-adapter-driver-for-pcie-40-gigabit-ethernet-network-connections-under-linux.html" title="Intel Network Adapter Driver for PCIe 40 Gigabit Ethernet Network Connections Under Linux">driver from Intel</a>, unpack it in some directory and
add a <code>debian/</code> subdirectory with the following files:</p>
<ul>
<li>
<p><code>debian/changelog</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>i40e-dkms (2.4.6-0) stretch; urgency=medium

  * Initial package.

 -- Vincent Bernat &lt;bernat@debian.org&gt;  Tue, 27 Feb 2018 17:20:58 +0100
</code></pre></div>

</li>
<li>
<p><code>debian/control</code>:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">i40e-dkms</span>
<span class="k">Maintainer</span><span class="p">:</span><span class="w"> </span>Vincent Bernat <span class="gs">&lt;bernat@debian.org&gt;</span>
<span class="k">Build-Depends</span><span class="p">:</span><span class="w"> </span><span class="nf">debhelper</span> <span class="p">(</span><span class="o">&gt;=</span><span class="w"> </span><span class="m">9</span><span class="p">)</span>, <span class="nf">dkms</span>

<span class="nf">Package</span><span class="err">:</span> <span class="nf">i40e-dkms</span>
<span class="k">Architecture</span><span class="p">:</span><span class="w"> </span><span class="s">all</span>
<span class="k">Depends</span><span class="p">:</span><span class="w"> </span><span class="o">$</span><span class="p">{</span><span class="ni">misc</span><span class="p">:</span>Depends<span class="p">}</span>
<span class="k">Description</span><span class="gs">: DKMS source for the Intel i40e network driver</span>
</code></pre></div>

</li>
<li>
<p><code>debian/rules</code>:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>

<span class="cp">include /usr/share/dpkg/pkg-info.mk</span>

<span class="nf">%</span><span class="o">:</span>
<span class="w">        </span>dh<span class="w"> </span><span class="nv">$@</span><span class="w"> </span>--with<span class="w"> </span>dkms

<span class="nf">override_dh_install</span><span class="o">:</span>
<span class="hll"><span class="w">        </span>dh_install<span class="w"> </span>src/*<span class="w"> </span>usr/src/i40e-<span class="k">$(</span>DEB_VERSION_UPSTREAM<span class="k">)</span>/
</span>
<span class="nf">override_dh_dkms</span><span class="o">:</span>
<span class="w">        </span>dh_dkms<span class="w"> </span>-V<span class="w"> </span><span class="k">$(</span>DEB_VERSION_UPSTREAM<span class="k">)</span>

<span class="nf">override_dh_auto_configure</span><span class="o">:</span>
<span class="nf">override_dh_auto_build</span><span class="o">:</span>
<span class="nf">override_dh_auto_test</span><span class="o">:</span>
<span class="nf">override_dh_auto_install</span><span class="o">:</span>
<span class="nf">override_dh_auto_clean</span><span class="o">:</span>
</code></pre></div>

</li>
<li>
<p><code>debian/i40e-dkms.dkms</code>:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="na">PACKAGE_NAME</span><span class="o">=</span><span class="s">"i40e"</span>
<span class="na">PACKAGE_VERSION</span><span class="o">=</span><span class="s">"#MODULE_VERSION#"</span>
<span class="na">BUILT_MODULE_NAME[0]</span><span class="o">=</span><span class="s">"$PACKAGE_NAME"</span>
<span class="na">DEST_MODULE_LOCATION[0]</span><span class="o">=</span><span class="s">"/updates/dkms"</span>
<span class="na">AUTOINSTALL</span><span class="o">=</span><span class="s">"YES"</span>
<span class="na">REMAKE_INITRD</span><span class="o">=</span><span class="s">"YES"</span>
</code></pre></div>

</li>
<li>
<p><code>debian/compat</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>9
</code></pre></div>

</li>
</ul>
<p>In <code>debian/changelog</code>, pay attention to the version. The version of
the driver is 2.4.6. Therefore, we use <code>2.4.6-0</code> for the package. In
<code>debian/rules</code>, we install the source of the driver in
<code>/usr/src/i40e-2.4.6</code>—the version is extracted from
<code>debian/changelog</code>.</p>
<p>The content of <code>debian/i40e-dkms.dkms</code> is described in detail in the
<a href="https://manpages.debian.org/dkms.8.html#DKMS.CONF" title="Manual page for dkms(8)"><code>dkms(8)</code> manual page</a>. The <code>i40e</code> driver is fairly
standard and <code>dkms</code> is able to figure out how to compile it. However,
if your kernel module does not follow the usual conventions, it is the
right place to override the build command.</p>
<p>Once all the files are in place, you can turn the directory into a
Debian package with, for example, the <code>dpkg-buildpackage</code>
command.<sup id="fnref:apt"><a class="footnote-ref" href="#fn:apt">2</a></sup> At the end of this operation, you get your <abbr title="Dynamic Kernel Module Support">DKMS</abbr>-enabled
package, <code>i40e-dkms_2.4.6-0_all.deb</code>. Put it in your <a href="/en/blog/2014-local-apt-repositories" title="Local corporate APT repositories">internal
repository</a> and install it on the target.</p>
<h1 id="avoiding-compilation-on-target">Avoiding compilation on target</h1>
<div class="admonition">
<p class="admonition-title">Update (2024-03)</p>
<p><em><abbr title="Dynamic Kernel Module Support">DKMS</abbr></em> upstream removed support for the <code>dkms mkbmdeb</code> command. This impacts
versions starting from 2.8.8, including the ones shipped in Debian Bookworm
and Ubuntu Noble. See <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1009179" title="Debian bug #1009179">Debian bug#1009179</a> for more information.</p>
</div>
<div class="admonition">
<p class="admonition-title">Update (2026-09)</p>
<p>As a replacement, you can use <a href="https://github.com/uecasm/dkms-mkbmdeb" title="Standalone replacement for dkms mkbmdeb"><code>dkms-mkbmdeb</code></a>, a standalone
script with the same features. Substitute <code>dkms mkbmdeb</code> with <code>dkms-mkbmdeb</code>
in the example below. Thanks to Gavin for this!</p>
</div>
<p>If you feel uncomfortable installing compilation tools on the target
servers, there is a simple solution. Since version
2.2.0.3-5,<sup id="fnref:version"><a class="footnote-ref" href="#fn:version">3</a></sup> thanks to <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=554843#92" title="Debian bug #554843">Thijs Kinkhorst</a>, <code>dkms</code> can build
lean binary packages with only the built modules. For each kernel
version, you build such a package in your <abbr title="Continuous Integration">CI</abbr> system:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="nv">KERNEL_VERSION</span><span class="o">=</span><span class="m">4</span>.9.0-6-amd64<span class="w"> </span><span class="c1"># could be a Jenkins parameter</span>
apt<span class="w"> </span>-qyy<span class="w"> </span>install<span class="w"> </span><span class="se">\</span>
<span class="w">      </span>i40e-dkms<span class="w"> </span><span class="se">\</span>
<span class="w">      </span>linux-image-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w">      </span>linux-headers-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>

<span class="nv">DRIVER_VERSION</span><span class="o">=</span><span class="k">$(</span>dkms<span class="w"> </span>status<span class="w"> </span>i40e<span class="w"> </span><span class="p">|</span><span class="w"> </span>awk<span class="w"> </span>-F<span class="s1">', '</span><span class="w"> </span><span class="s1">'{print $2}'</span><span class="k">)</span>
<span class="hll">dkms<span class="w"> </span>mkbmdeb<span class="w"> </span>i40e/<span class="si">${</span><span class="nv">DRIVER_VERSION</span><span class="si">}</span><span class="w"> </span>-k<span class="w"> </span><span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>
</span>
<span class="nb">cd</span><span class="w"> </span>/var/lib/dkms/i40e/<span class="si">${</span><span class="nv">DRIVER_VERSION</span><span class="si">}</span>/bmdeb/
dpkg<span class="w"> </span>-c<span class="w"> </span>i40e-modules-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>_*
dpkg<span class="w"> </span>-I<span class="w"> </span>i40e-modules-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>_*
</code></pre></div>

<p>Here is the shortened output of the two last commands:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp"># </span>dpkg<span class="w"> </span>-c<span class="w"> </span>i40e-modules-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>_*
<span class="go">[…]</span>
<span class="hll"><span class="go">-rw-r--r-- root/root    551664 2018-03-01 19:16 ./lib/modules/4.9.0-6-amd64/updates/dkms/i40e.ko</span>
</span><span class="go">[…]</span>
<span class="gp"># </span>dpkg<span class="w"> </span>-I<span class="w"> </span>i40e-modules-<span class="si">${</span><span class="nv">KERNEL_VERSION</span><span class="si">}</span>_*
<span class="go"> new debian package, version 2.0.</span>
<span class="go">[…]</span>
<span class="go"> Package: i40e-modules-4.9.0-6-amd64</span>
<span class="go"> Source: i40e-dkms-bin</span>
<span class="go"> Version: 2.4.6</span>
<span class="go"> Architecture: amd64</span>
<span class="go"> Installed-Size: 555</span>
<span class="go"> Depends: linux-image-4.9.0-6-amd64</span>
<span class="hll"><span class="go"> Provides: i40e-modules</span>
</span><span class="go"> Section: misc</span>
<span class="go"> Priority: optional</span>
<span class="go"> Description: i40e binary drivers for linux-image-4.9.0-6-amd64</span>
<span class="go">  This package contains i40e drivers for the 4.9.0-6-amd64 Linux kernel,</span>
<span class="go">  built from i40e-dkms for the amd64 architecture.</span>
</code></pre></div>

<p>The generated Debian package contains the pre-compiled driver and only
depends on the associated kernel. You can safely install it without
pulling dozens of packages.</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:rpm">
<p><em><abbr title="Dynamic Kernel Module Support">DKMS</abbr></em> is also compatible with RPM-based distributions but the
content of this article is not suitable for these. <a class="footnote-backref" href="#fnref:rpm" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:apt">
<p>You may need to install some additional packages:
<code>build-essential</code>, <code>fakeroot</code> and <code>debhelper</code>. <a class="footnote-backref" href="#fnref:apt" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:version">
<p>Available in Debian Stretch and in the backports for
Debian Jessie. However, for Ubuntu Xenial, you need to
<a href="https://github.com/exoscale/pkg-dkms/tree/xenial" title="Backport of dkms for Ubuntu Xenial">backport</a> a more recent version of <code>dkms</code>. <a class="footnote-backref" href="#fnref:version" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
</ol>
</div>
      </div>
    </content>
  </entry>
  <entry>
    <title type="html">Vienna Linux Meetup: are distributions still relevant?</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2017-vienna-linux-distributions" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2017-vienna-linux-distributions#isso-thread" rel="replies" type="text/html"/>
    <updated>2017-11-07T18:00:00Z</updated>
    <id>http://www.luffy.cx/en/blog/2017-vienna-linux-distributions.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<p>Here are the slides I presented for a <a href="https://www.meetup.com/viennalinux/">Vienna Linux Meetup</a>. I was
trapped here by Sanja. I share what distributions bring to the
table for several decades and I explain why this is still partly
needed today, despite the widespread use of containers.</p>
<figure class="lf-fullbleed"><div class="lf-media-outer" style="width: 992px"><span class="lf-media-inner" style="padding-bottom: 56.250%"><object width="992" height="558" type="application/pdf" data="https://media.bernat.ch/files/slides-vienna-linux-meetup.pdf#toolbar=0&amp;navpanes=0&amp;scrollbar=0&amp;view=Fit&amp;zoom=page-fit&amp;pagemode=none" class="lf-media lf-opaque"><a href="https://media.bernat.ch/files/slides-vienna-linux-meetup.pdf">Slides in PDF</a></object></span></div></figure>
      </div>
    </content>
  </entry>
  <entry>
    <title type="html">Pragmatic Debian packaging (2016)</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2016-pragmatic-debian-packaging" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2016-pragmatic-debian-packaging#isso-thread" rel="replies" type="text/html"/>
    <updated>2016-05-02T17:25:13Z</updated>
    <id>http://www.luffy.cx/en/blog/2016-pragmatic-debian-packaging.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<div class="admonition">
<p class="admonition-title">Update (2019-05)</p>
<p>Have a look at the <a href="/en/blog/2019-pragmatic-debian-packaging">2019
edition</a> of this guide if
you target only recent distributions (Debian Stretch and Ubuntu Bionic).</p>
</div>
<p>While the creation of Debian packages is <a href="https://www.debian.org/doc/manuals/maint-guide/" title="New Maintainer's Guide">abundantly</a>
<a href="https://www.debian.org/doc/manuals/maint-guide/" title="New Maintainer's Guide">documented</a>, most <a href="https://www.debian.org/doc/manuals/packaging-tutorial/packaging-tutorial" title="Lucas Nussbaum's packaging tutorial">tutorials</a> are targeted to
packages implementing the <a href="https://www.debian.org/doc/debian-policy/" title="The Debian Policy Manual">Debian policy</a>. Moreover, Debian
packaging has <strong>a reputation of being unnecessarily
difficult</strong><sup id="fnref:difficulty"><a class="footnote-ref" href="#fn:difficulty">1</a></sup> and many people prefer to use less
constrained tools<sup id="fnref:fpm"><a class="footnote-ref" href="#fn:fpm">2</a></sup> like <a href="https://github.com/jordansissel/fpm" title="Effing package management">fpm</a> or <a href="https://web.archive.org/web/2016/http://checkinstall.izto.org/" title="CheckInstall">CheckInstall</a>.</p>
<p>However, building Debian packages with the official tools can become
straightforward if you bend some rules:</p>
<ol>
<li>
<p><strong>No source package will be generated</strong>.  Packages will be built
    directly from a checkout of a <abbr title="Version Control System">VCS</abbr> repository.</p>
</li>
<li>
<p><strong>Additional dependencies can be downloaded during
    build</strong>. Packaging individually each dependency is a painstaking
    work, notably when you have to deal with some fast-paced
    ecosystems like Java, JavaScript and Go.</p>
</li>
<li>
<p>The produced packages may <strong>bundle dependencies</strong>. This is likely
    to raise some concerns about security and long-term maintenance,
    but this is a common trade-off in many ecosystems, notably Java,
    JavaScript and Go.</p>
</li>
</ol>
<div class="toc">
<ul>
<li><a href="#pragmatic-packages-101">Pragmatic packages 101</a><ul>
<li><a href="#the-control-file">The control file</a></li>
<li><a href="#the-build-recipe">The build recipe</a></li>
</ul>
</li>
<li><a href="#pragmatic-packages-102">Pragmatic packages 102</a><ul>
<li><a href="#build-dependencies">Build dependencies</a></li>
<li><a href="#runtime-dependencies">Runtime dependencies</a></li>
<li><a href="#integration-with-init-system">Integration with init system</a><ul>
<li><a href="#system-v">System-V</a></li>
<li><a href="#upstart">Upstart</a></li>
<li><a href="#systemd">systemd</a></li>
</ul>
</li>
<li><a href="#dedicated-user">Dedicated user</a></li>
</ul>
</li>
<li><a href="#pragmatic-packages-103">Pragmatic packages 103</a><ul>
<li><a href="#the-big-picture">The big picture</a></li>
<li><a href="#automatic-builds">Automatic builds</a></li>
<li><a href="#install-additional-files">Install additional files</a></li>
</ul>
</li>
<li><a href="#other-examples">Other examples</a></li>
</ul>
</div>
<h1 id="pragmatic-packages-101">Pragmatic packages 101</h1>
<p>In the Debian archive, you have two kinds of packages: the <strong>source
packages</strong> and the <strong>binary packages</strong>. Each binary package is built
from a source package. You need a name for each package.</p>
<p>As stated in the introduction, we won’t generate a source package but
we will work with its unpacked form which is any source tree
containing a <code>debian/</code> directory. In our examples, we will start with
a source tree containing only a <code>debian/</code> directory but you are free
to include this <code>debian/</code> directory into an existing project.</p>
<p>As an example, we will package <a href="https://memcached.org/" title="memcached - a distributed memory object caching system">memcached</a>, a distributed memory
cache. There are four files to create:</p>
<ul>
<li><code>debian/compat</code>;</li>
<li><code>debian/changelog</code>;</li>
<li><code>debian/control</code>; and</li>
<li><code>debian/rules</code>.</li>
</ul>
<p>The first one is easy. Put <code>9</code> in it:<sup id="fnref:compat"><a class="footnote-ref" href="#fn:compat">3</a></sup></p>
<div class="language-bash codehilite"><pre><span/><code><span class="nb">echo</span><span class="w"> </span><span class="m">9</span><span class="w"> </span>&gt;<span class="w"> </span>debian/compat
</code></pre></div>

<p>The second one has the following content:</p>
<div class="language-text-only codehilite"><pre><span/><code>memcached (0.0-0) UNRELEASED; urgency=medium

  * Fake entry

 -- Happy Packager &lt;happy@example.com&gt;  Tue, 19 Apr 2016 22:27:05 +0200
</code></pre></div>

<p>The only important information is the name of the source package,
<code>memcached</code>, on the first line. Everything else can be left as is as
it won’t influence the generated binary packages.</p>
<h2 id="the-control-file">The control file</h2>
<p><code>debian/control</code> describes the metadata of both the source package and
the generated binary packages. We have to write a block for each of
them.</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Maintainer</span><span class="p">:</span><span class="w"> </span>Vincent Bernat <span class="gs">&lt;bernat@debian.org&gt;</span>

<span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Architecture</span><span class="p">:</span><span class="w"> </span><span class="s">any</span>
<span class="k">Description</span><span class="gs">: high-performance memory object caching system</span>
</code></pre></div>

<p>The source package is called <code>memcached</code>. We have to use the same name
as in <code>debian/changelog</code>.</p>
<p>We generate only one binary package: <code>memcached</code>. In the remaining of
the example, when you see <code>memcached</code>, this is the name of a binary
package. The <code>Architecture</code> field should be set to either <code>any</code> or
<code>all</code>. Use <code>all</code> exclusively if the package contains only
arch-independent files. In doubt, just stick to <code>any</code>.</p>
<p>The <code>Description</code> field contains a short description of the binary
package.</p>
<h2 id="the-build-recipe">The build recipe</h2>
<p>The last mandatory file is <code>debian/rules</code>. It’s the <strong>recipe</strong> of the
package. We need to retrieve <em>memcached</em>, build it and install its
file tree in <code>debian/memcached/</code>. It looks like this:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>

<span class="nv">DISTRIBUTION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>shell<span class="w"> </span>lsb_release<span class="w"> </span>-sr<span class="k">)</span>
<span class="nv">VERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span>.4.25
<span class="nv">PACKAGEVERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>VERSION<span class="k">)</span>-0~<span class="k">$(</span>DISTRIBUTION<span class="k">)</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>memcached-<span class="k">$(</span>VERSION<span class="k">)</span>.tar.gz
<span class="nv">URL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>http://www.memcached.org/files/<span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span>

<span class="nf">override_dh_auto_clean</span><span class="o">:</span>
<span class="nf">override_dh_auto_test</span><span class="o">:</span>
<span class="nf">override_dh_auto_build</span><span class="o">:</span>
<span class="nf">override_dh_auto_install</span><span class="o">:</span>
<span class="w">    </span>wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="k">$(</span>URL<span class="k">)</span>
<span class="w">    </span>tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="k">$(</span>TARBALL<span class="k">)</span>
<span class="w">    </span>./configure<span class="w"> </span>--prefix<span class="o">=</span>/usr
<span class="w">    </span>make
<span class="w">    </span>make<span class="w"> </span>install<span class="w"> </span><span class="nv">DESTDIR</span><span class="o">=</span>debian/memcached

<span class="nf">override_dh_gencontrol</span><span class="o">:</span>
<span class="w">    </span>dh_gencontrol<span class="w"> </span>--<span class="w"> </span>-v<span class="k">$(</span>PACKAGEVERSION<span class="k">)</span>
</code></pre></div>

<p>The empty targets <code>override_dh_auto_clean</code>, <code>override_dh_auto_test</code>
and <code>override_dh_auto_build</code> keep <em>debhelper</em> from being too
smart. The <code>override_dh_gencontrol</code> target sets the package
version<sup id="fnref:version"><a class="footnote-ref" href="#fn:version">4</a></sup> without updating <code>debian/changelog</code>. If you ignore
the slight boilerplate, the recipe is quite similar to what you would
have done with <code>fpm</code>:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="nv">DISTRIBUTION</span><span class="o">=</span><span class="k">$(</span>lsb_release<span class="w"> </span>-sr<span class="k">)</span>
<span class="nv">VERSION</span><span class="o">=</span><span class="m">1</span>.4.25
<span class="nv">PACKAGEVERSION</span><span class="o">=</span><span class="si">${</span><span class="nv">VERSION</span><span class="si">}</span>-0~<span class="si">${</span><span class="nv">DISTRIBUTION</span><span class="si">}</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="o">=</span>memcached-<span class="si">${</span><span class="nv">VERSION</span><span class="si">}</span>.tar.gz
<span class="nv">URL</span><span class="o">=</span>http://www.memcached.org/files/<span class="si">${</span><span class="nv">TARBALL</span><span class="si">}</span>

wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="si">${</span><span class="nv">URL</span><span class="si">}</span>
tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="si">${</span><span class="nv">TARBALL</span><span class="si">}</span>
./configure<span class="w"> </span>--prefix<span class="o">=</span>/usr
make
make<span class="w"> </span>install<span class="w"> </span><span class="nv">DESTDIR</span><span class="o">=</span>/tmp/installdir

<span class="c1"># Build the final package</span>
fpm<span class="w"> </span>-s<span class="w"> </span>dir<span class="w"> </span>-t<span class="w"> </span>deb<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-n<span class="w"> </span>memcached<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-v<span class="w"> </span><span class="si">${</span><span class="nv">PACKAGEVERSION</span><span class="si">}</span><span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-C<span class="w"> </span>/tmp/installdir<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>--description<span class="w"> </span><span class="s2">"high-performance memory object caching system"</span>
</code></pre></div>

<p>You can review the whole package tree on <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/memcached-basic/debian">GitHub</a>
and build it with <code>dpkg-buildpackage -us -uc -b</code>.</p>
<h1 id="pragmatic-packages-102">Pragmatic packages 102</h1>
<p>At this point, we can iterate and add several improvements to our
<em>memcached</em> package. <strong>None of them are mandatory</strong> but they are
usually worth the additional effort.</p>
<h2 id="build-dependencies">Build dependencies</h2>
<p>Our initial build recipe only works when several packages are
installed, like <code>wget</code> and <code>libevent-dev</code>. They are not present on all
Debian systems. You can easily express that you need them by adding a
<code>Build-Depends</code> section for the source package in <code>debian/control</code>:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Build-Depends</span><span class="p">:</span><span class="w"> </span><span class="nf">debhelper</span> <span class="p">(</span><span class="o">&gt;=</span><span class="w"> </span><span class="m">9</span><span class="p">)</span>,
<span class="w"> </span>              <span class="nf">wget</span>, <span class="nf">ca-certificates</span>, <span class="nf">lsb-release</span>,
<span class="w"> </span>              <span class="nf">libevent-dev</span>
</code></pre></div>

<p>Always specify the <code>debhelper (&gt;= 9)</code> dependency as we heavily rely on
it. We don’t require <code>make</code> or a C compiler because it is assumed that
the <code>build-essential</code> meta-package is installed and it pulls
them. <code>dpkg-buildpackage</code> will complain if the dependencies are not
met. If you want to install these packages from your <abbr title="Continuous integration">CI</abbr> system, you
can use the following command:<sup id="fnref:devscripts"><a class="footnote-ref" href="#fn:devscripts">5</a></sup></p>
<div class="language-bash codehilite"><pre><span/><code>mk-build-deps<span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-t<span class="w"> </span><span class="s1">'apt-get -o Debug::pkgProblemResolver=yes --no-install-recommends -qqy'</span><span class="w"> </span><span class="se">\</span>
<span class="w">    </span>-i<span class="w"> </span>-r<span class="w"> </span>debian/control
</code></pre></div>

<p>You may also want to investigate <a href="https://pbuilder-team.pages.debian.net/pbuilder/" title="pbuilder user's manual">pbuilder</a> or <a href="https://wiki.debian.org/sbuild" title="sbuild page on Debian Wiki">sbuild</a>, two tools
to build Debian packages in a clean isolated environment.</p>
<h2 id="runtime-dependencies">Runtime dependencies</h2>
<p>If the resulting package is installed on a freshly installed machine,
it won’t work because it will be missing <em>libevent</em>, a required
library for <em>memcached</em>. You can express the dependencies needed by
each binary package by adding a <code>Depends</code> field. Moreover, for dynamic
libraries, you can automatically get the right dependencies by using
some substitution variables:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Depends</span><span class="p">:</span><span class="w"> </span><span class="o">$</span><span class="p">{</span><span class="ni">misc</span><span class="p">:</span>Depends<span class="p">}</span>, <span class="o">$</span><span class="p">{</span><span class="ni">shlibs</span><span class="p">:</span>Depends<span class="p">}</span>
</code></pre></div>

<p>The resulting package will contain the following information:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>dpkg<span class="w"> </span>-I<span class="w"> </span>../memcached_1.4.25-0<span class="se">\~</span>unstable0_amd64.deb<span class="w"> </span><span class="p">|</span><span class="w"> </span>grep<span class="w"> </span>Depends
<span class="go"> Depends: libc6 (&gt;= 2.17), libevent-2.0-5 (&gt;= 2.0.10-stable)</span>
</code></pre></div>

<h2 id="integration-with-init-system">Integration with init system</h2>
<p>Most packaged daemons come with some integration with the init
system. This integration ensures the daemon will be started on boot
and restarted on upgrade. For Debian-based distributions, there are
several init systems available. The most prominent ones are:</p>
<ul>
<li><strong>System-V init</strong> is the historical init system. More modern
   inits are able to reuse scripts written for this init, so this is a
   safe common denominator for packaged daemons.</li>
<li><strong>Upstart</strong> is the less-historical init system for Ubuntu (used in
   Ubuntu 14.10 and previous releases).</li>
<li><strong>systemd</strong> is the default init system for Debian since Jessie and
   for Ubuntu since 15.04.</li>
</ul>
<p>Writing a correct script for the <em>System-V init</em> is
error-prone. Therefore, I usually prefer to provide a native
configuration file for the default init system of the targeted
distribution (<em>Upstart</em> and <em>systemd</em>).</p>
<h3 id="system-v">System-V</h3>
<p>If you want to provide a <em>System-V</em> init script, have a look at
<code>/etc/init.d/skeleton</code> on the most ancient distribution you want to
target and adapt it.<sup id="fnref:provided"><a class="footnote-ref" href="#fn:provided">6</a></sup> Put the result in
<code>debian/memcached.init</code>. It will be installed at the right place,
invoked on install, upgrade and removal. On Debian-based systems, many
init scripts allow user customizations by providing a
<code>/etc/default/memcached</code> file. You can ship one by putting its content
in <code>debian/memcached.default</code>.</p>
<h3 id="upstart">Upstart</h3>
<p>Providing an <em>Upstart</em> job is similar: put it in
<code>debian/memcached.upstart</code>. For example:</p>
<div class="language-text-only codehilite"><pre><span/><code>description "memcached daemon"

start on runlevel [2345]
stop on runlevel [!2345]
respawn
respawn limit 5 60
expect daemon

script
  . /etc/default/memcached
  exec memcached -d -u $USER -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS
end script
</code></pre></div>

<p>When writing an <em>Upstart</em> job, the most important directive is
<a href="https://web.archive.org/web/2013/http://upstart.ubuntu.com/cookbook/#expect" title="Upstart cookbook: expect stanza">expect</a>. Be sure to get it right. Here, we use <code>expect daemon</code> and
<em>memcached</em> is started with the <code>-d</code> flag.</p>
<h3 id="systemd">systemd</h3>
<p>Providing a <em>systemd</em> unit is a bit more complex. The content of the
file should go in <code>debian/memcached.service</code>. For example:</p>
<div class="language-systemd codehilite"><pre><span/><code><span class="k">[Unit]</span>
<span class="na">Description</span><span class="o">=</span><span class="s">memcached daemon</span>
<span class="na">After</span><span class="o">=</span><span class="s">network.target</span>

<span class="k">[Service]</span>
<span class="na">Type</span><span class="o">=</span><span class="s">forking</span>
<span class="na">EnvironmentFile</span><span class="o">=</span><span class="s">/etc/default/memcached</span>
<span class="na">ExecStart</span><span class="o">=</span><span class="s">/usr/bin/memcached -d -u $USER -p $PORT -m $CACHESIZE -c $MAXCONN $OPTIONS</span>
<span class="na">Restart</span><span class="o">=</span><span class="s">on-failure</span>

<span class="k">[Install]</span>
<span class="na">WantedBy</span><span class="o">=</span><span class="s">multi-user.target</span>
</code></pre></div>

<p>We reuse <code>/etc/default/memcached</code> even if it is not considered a good
practice with systemd.<sup id="fnref:default"><a class="footnote-ref" href="#fn:default">7</a></sup> Like for <em>Upstart</em>, the directive
<code>Type</code> is quite important. We used <code>forking</code> as <em>memcached</em> is started
with the <code>-d</code> flag.</p>
<div class="admonition">
<p class="admonition-title">Update (2017-11)</p>
<p>When targeting Debian Stretch, Ubuntu Yaketty or more recent, put <code>10</code> in
<code>debian/compat</code> and ignore the following instructions.</p>
</div>
<p>You also need to add a build-dependency to <code>dh-systemd</code> in
<code>debian/control</code>:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Source</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Build-Depends</span><span class="p">:</span><span class="w"> </span><span class="nf">debhelper</span> <span class="p">(</span><span class="o">&gt;=</span><span class="w"> </span><span class="m">9</span><span class="p">)</span>,
<span class="w"> </span>              <span class="nf">wget</span>, <span class="nf">ca-certificates</span>, <span class="nf">lsb-release</span>,
<span class="w"> </span>              <span class="nf">libevent-dev</span>,
<span class="w"> </span>              <span class="nf">dh-systemd</span>
</code></pre></div>

<p>And you need to modify the default rule in <code>debian/rules</code>:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span><span class="w"> </span>--with<span class="w"> </span>systemd
</code></pre></div>

<p>The extra complexity is a bit unfortunate but <em>systemd</em> integration
was not part of <em>debhelper</em>.<sup id="fnref:bug822670"><a class="footnote-ref" href="#fn:bug822670">8</a></sup> Without these additional
modifications, the unit will get installed but you won’t get a proper
integration and the service won’t be enabled on install or boot.</p>
<h2 id="dedicated-user">Dedicated user</h2>
<p>Many daemons don’t need to run as <em>root</em> and it is a good practice to
ship a dedicated user. In the case of <em>memcached</em>, we can provide a
<code>_memcached</code> user.<sup id="fnref:naming"><a class="footnote-ref" href="#fn:naming">9</a></sup></p>
<div class="admonition">
<p class="admonition-title">Update (2018-11)</p>
<p>If you only care about <em>systemd</em>, you may save time and energy by using its
<a href="http://0pointer.net/blog/dynamic-users-with-systemd.html" title="Dynamic Users with systemd">dynamic users feature</a> instead.</p>
</div>
<p>Add a <code>debian/memcached.postinst</code> file with the following content:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="ch">#!/bin/sh</span>

<span class="nb">set</span><span class="w"> </span>-e

<span class="k">case</span><span class="w"> </span><span class="s2">"</span><span class="nv">$1</span><span class="s2">"</span><span class="w"> </span><span class="k">in</span>
<span class="w">    </span>configure<span class="o">)</span>
<span class="w">        </span>adduser<span class="w"> </span>--system<span class="w"> </span>--disabled-password<span class="w"> </span>--disabled-login<span class="w"> </span>--home<span class="w"> </span>/var/empty<span class="w"> </span><span class="se">\</span>
<span class="w">                </span>--no-create-home<span class="w"> </span>--quiet<span class="w"> </span>--force-badname<span class="w"> </span>--group<span class="w"> </span>_memcached
<span class="w">        </span><span class="p">;;</span>
<span class="k">esac</span>

<span class="c1">#DEBHELPER#</span>

<span class="nb">exit</span><span class="w"> </span><span class="m">0</span>
</code></pre></div>

<p>There is no cleanup of the user when the package is removed for two reasons:</p>
<ol>
<li>Less stuff to write.</li>
<li>The user could still own some files.</li>
</ol>
<p>The utility <code>adduser</code> will do the right thing whatever the requested
user already exists or not. You need to add it as a dependency in
<code>debian/control</code>:</p>
<div class="language-debian-control-file codehilite"><pre><span/><code><span class="k">Package</span><span class="p">:</span><span class="w"> </span><span class="s">memcached</span>
<span class="k">Depends</span><span class="p">:</span><span class="w"> </span><span class="o">$</span><span class="p">{</span><span class="ni">misc</span><span class="p">:</span>Depends<span class="p">}</span>, <span class="o">$</span><span class="p">{</span><span class="ni">shlibs</span><span class="p">:</span>Depends<span class="p">}</span>, <span class="nf">adduser</span>
</code></pre></div>

<p>The <code>#DEBHELPER#</code> marker is important as it will be replaced by some
code to handle the service configuration files (or some other stuff).</p>
<p>You can review the whole package tree on
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/memcached-intermediate/debian">GitHub</a> and build it with
<code>dpkg-buildpackage -us -uc -b</code>.</p>
<h1 id="pragmatic-packages-103">Pragmatic packages 103</h1>
<p>It is possible to leverage <em>debhelper</em> to <strong>reduce the recipe size</strong>
and to make it <strong>more declarative</strong>. This section is quite optional
and it requires understanding a bit more how a Debian package is
built. Feel free to skip it.</p>
<h2 id="the-big-picture">The big picture</h2>
<p>There are <strong>four steps</strong> to build a regular Debian package:</p>
<ol>
<li>
<p><code>debian/rules clean</code> should <strong>clean</strong> the source tree to make it
    pristine.</p>
</li>
<li>
<p><code>debian/rules build</code> should trigger the <strong>build</strong>. For an
    autoconf-based software, like <em>memcached</em>, this step should
    execute something like <code>./configure &amp;&amp; make</code>.</p>
</li>
<li>
<p><code>debian/rules install</code> should <strong>install</strong> the file tree of each
    binary package. For an autoconf-based software, this step should
    execute <code>make install DESTDIR=debian/memcached</code>.</p>
</li>
<li>
<p><code>debian/rules binary</code> will <strong>pack</strong> the different file trees into
    binary packages.</p>
</li>
</ol>
<p>You don’t directly write each of these targets. Instead, you let <code>dh</code>,
a component of <em>debhelper</em>, do most of the work. The following
<code>debian/rules</code> file should do almost everything correctly with many
source packages:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>
<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span>
</code></pre></div>

<p>For each of the four targets described above, you can run <code>dh</code>
with <code>--no-act</code> to see what it would do. For example:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>dh<span class="w"> </span>build<span class="w"> </span>--no-act
<span class="go">   dh_testdir</span>
<span class="go">   dh_update_autotools_config</span>
<span class="go">   dh_auto_configure</span>
<span class="go">   dh_auto_build</span>
<span class="go">   dh_auto_test</span>
</code></pre></div>

<p>Each of these helpers has a manual page. Helpers starting with
<code>dh_auto_</code> are a bit “magic.” For example, <code>dh_auto_configure</code> will
try to automatically configure a package prior to building: it will
detect the build system and invoke <code>./configure</code>, <code>cmake</code> or
<code>Makefile.PL</code>.</p>
<p>If one of the helpers does not do the “right” thing, you can replace it
by using an override target:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="nf">override_dh_auto_configure</span><span class="o">:</span>
<span class="w">    </span>./configure<span class="w"> </span>--with-some-grog
</code></pre></div>

<p>These helpers are also configurable, so you can just alter a bit their
behavior by invoking them with additional options:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="nf">override_dh_auto_configure</span><span class="o">:</span>
<span class="w">    </span>dh_auto_configure<span class="w"> </span>--<span class="w"> </span>--with-some-grog
</code></pre></div>

<p>This way, <code>./configure</code> will be called with your custom flag but also
with a lot of default flags like <code>--prefix=/usr</code> for better
integration.</p>
<p>In the initial <em>memcached</em> example, we overrode all these “magic”
targets. <code>dh_auto_clean</code>, <code>dh_auto_configure</code> and <code>dh_auto_build</code> are
converted to no-ops to avoid any unexpected
behavior. <code>dh_auto_install</code> is hijacked to do all the build
process. Additionally, we modified the behavior of the <code>dh_gencontrol</code>
helper by forcing the version number instead of using the one from
<code>debian/changelog</code>.</p>
<h2 id="automatic-builds">Automatic builds</h2>
<p>As <em>memcached</em> is an autoconf-enabled package, <strong><code>dh</code> knows how
to build it</strong>: <code>./configure &amp;&amp; make &amp;&amp; make install</code>. Therefore, we
can let it handle most of the work with this <code>debian/rules</code> file:</p>
<div class="language-makefile codehilite"><pre><span/><code><span class="c">#!/usr/bin/make -f</span>

<span class="nv">DISTRIBUTION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>shell<span class="w"> </span>lsb_release<span class="w"> </span>-sr<span class="k">)</span>
<span class="nv">VERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="m">1</span>.4.25
<span class="nv">PACKAGEVERSION</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">$(</span>VERSION<span class="k">)</span>-0~<span class="k">$(</span>DISTRIBUTION<span class="k">)</span><span class="m">0</span>
<span class="nv">TARBALL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>memcached-<span class="k">$(</span>VERSION<span class="k">)</span>.tar.gz
<span class="nv">URL</span><span class="w"> </span><span class="o">=</span><span class="w"> </span>http://www.memcached.org/files/<span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">%</span><span class="o">:</span>
<span class="w">    </span>dh<span class="w"> </span><span class="nv">$@</span><span class="w"> </span>--with<span class="w"> </span>systemd

<span class="nf">override_dh_auto_clean</span><span class="o">:</span>
<span class="w">    </span>wget<span class="w"> </span>-N<span class="w"> </span>--progress<span class="o">=</span>dot:mega<span class="w"> </span><span class="k">$(</span>URL<span class="k">)</span>
<span class="w">    </span>tar<span class="w"> </span>--strip-components<span class="o">=</span><span class="m">1</span><span class="w"> </span>-xf<span class="w"> </span><span class="k">$(</span>TARBALL<span class="k">)</span>

<span class="nf">override_dh_auto_test</span><span class="o">:</span>
<span class="c">    # Don't run the whitespace test</span>
<span class="w">    </span>rm<span class="w"> </span>t/whitespace.t
<span class="w">    </span>dh_auto_test

<span class="nf">override_dh_gencontrol</span><span class="o">:</span>
<span class="w">    </span>dh_gencontrol<span class="w"> </span>--<span class="w"> </span>-v<span class="k">$(</span>PACKAGEVERSION<span class="k">)</span>
</code></pre></div>

<p>The <code>dh_auto_clean</code> target is hijacked to download and setup the
source tree.<sup id="fnref:clean"><a class="footnote-ref" href="#fn:clean">10</a></sup> We do not override the <code>dh_auto_configure</code> step,
so <code>dh</code> will execute the <code>./configure</code> script with the appropriate
options. We do not override the <code>dh_auto_build</code> step either: <code>dh</code> will
execute <code>make</code>. <code>dh_auto_test</code> is invoked after the build and it will
run the <em>memcached</em> test suite. We need to override it because one of
the tests is complaining about odd whitespaces in the <code>debian/</code>
directory. We suppress this rogue test and let <code>dh_auto_test</code> execute
the test suite. <code>dh_auto_install</code> is not overridden either, so <code>dh</code>
will execute some variant of <code>make install</code>.</p>
<p>To get a better sense of the difference, here is a diff:</p>
<div class="language-diff codehilite"><pre><span/><code><span class="gd">--- memcached-intermediate/debian/rules 2016-04-30 14:02:37.425593362 +0200</span>
<span class="gi">+++ memcached/debian/rules  2016-05-01 14:55:15.815063835 +0200</span>
<span class="gu">@@ -12,10 +12,9 @@</span>
<span class="w"> </span>override_dh_auto_clean:
<span class="gd">-override_dh_auto_test:</span>
<span class="gd">-override_dh_auto_build:</span>
<span class="gd">-override_dh_auto_install:</span>
<span class="w"> </span>   wget -N --progress=dot:mega $(URL)
<span class="w"> </span>   tar --strip-components=1 -xf $(TARBALL)
<span class="gd">-   ./configure --prefix=/usr</span>
<span class="gd">-   make</span>
<span class="gd">-   make install DESTDIR=debian/memcached</span>
<span class="gi">+</span>
<span class="gi">+override_dh_auto_test:</span>
<span class="gi">+   # Don't run the whitespace test</span>
<span class="gi">+   rm t/whitespace.t</span>
<span class="gi">+   dh_auto_test</span>
</code></pre></div>

<p>It is up to you to decide if <code>dh</code> can do some work for you, but
you could try to start from a minimal <code>debian/rules</code> and only override
some targets.</p>
<h2 id="install-additional-files">Install additional files</h2>
<p>While <code>make install</code> installed the essential files for <em>memcached</em>,
you may want to <strong>put additional files</strong> in the binary package. You
could use <code>cp</code> in your build recipe, but you can also declare them:</p>
<ul>
<li>files listed in <code>debian/memcached.docs</code> will be copied to
   <code>/usr/share/doc/memcached</code> by <code>dh_installdocs</code></li>
<li>files listed in <code>debian/memcached.examples</code> will be copied to
   <code>/usr/share/doc/memcached/examples</code> by <code>dh_installexamples</code></li>
<li>files listed in <code>debian/memcached.manpages</code> will be copied to the
   appropriate subdirectory of <code>/usr/share/man</code> by <code>dh_installman</code></li>
</ul>
<p>Here is an example using wildcards for <code>debian/memcached.docs</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>doc/*.txt
</code></pre></div>

<p>If you need to copy some files to an arbitrary location, you can list
them along with their destination directories in
<code>debian/memcached.install</code> and <code>dh_install</code> will take care of the
copy. Here is an example:</p>
<div class="language-text-only codehilite"><pre><span/><code>scripts/memcached-tool usr/bin
</code></pre></div>

<p>Using these files make the build process <strong>more declarative</strong>. It is a
matter of taste and you are free to use <code>cp</code> in <code>debian/rules</code>
instead. You can review the whole package tree on
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/memcached/debian">GitHub</a>.</p>
<h1 id="other-examples">Other examples</h1>
<p>The <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016" title="Git repository full of pragmatic Debian packages">Git repository</a> contains some additional
examples. They all follow the same scheme:</p>
<ul>
<li><code>dh_auto_clean</code> is hijacked to download and setup the source tree</li>
<li><code>dh_gencontrol</code> is modified to use a computed version</li>
</ul>
<p>Notably, you’ll find daemons in <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/riemann/debian">Java</a>, <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/etcd/debian">Go</a>, <a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/puppetboard/debian">Python</a> and
<a href="https://github.com/vincentbernat/pragmatic-debian-packages/tree/2016/kiwiirc/debian">Node.js</a>. The goal of these examples is to demonstrate that using
Debian tools to build Debian packages can be straightforward. Hope
this helps.</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:difficulty">
<p>People may remember the time before <em>debhelper</em> 7.0.50
(circa 2009) where <code>debian/rules</code> was a daunting
beast. However, nowadays, the boilerplate is quite
reduced. <a class="footnote-backref" href="#fnref:difficulty" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:fpm">
<p>The complexity is not the only reason. These alternative tools
enable the creation of RPM packages, something that Debian
tools do not. <a class="footnote-backref" href="#fnref:fpm" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:compat">
<p>This compatibility level is available from Debian 8 and
Ubuntu Precise. Therefore, this covers a wide range of
distributions. <a class="footnote-backref" href="#fnref:compat" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:version">
<p>There are many ways to version a package. Again, if you
want to be pragmatic, the proposed solution should be good
enough for Ubuntu. On Debian, it doesn’t cover upgrade
from one distribution version to another, but we assume
that nowadays, systems get reinstalled instead of being
upgraded. <a class="footnote-backref" href="#fnref:version" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:devscripts">
<p>You also need to install <code>devscripts</code> and <code>equivs</code> package. <a class="footnote-backref" href="#fnref:devscripts" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
<li id="fn:provided">
<p>It’s also possible to use a script provided by
upstream. However, there is no such thing as an init
script that works on all distributions. Compare the
proposed with the skeleton, check if it is using
<code>start-stop-daemon</code> and if it sources
<code>/lib/lsb/init-functions</code> before considering it. If it
seems to fit, you can install it yourself in
<code>debian/memcached/etc/init.d/</code>. <em>debhelper</em> will ensure
its proper integration. <a class="footnote-backref" href="#fnref:provided" title="Jump back to footnote 6 in the text">↩</a></p>
</li>
<li id="fn:default">
<p>Instead, a user wanting to customize the options is
expected to edit the unit with <code>systemctl edit</code>. <a class="footnote-backref" href="#fnref:default" title="Jump back to footnote 7 in the text">↩</a></p>
</li>
<li id="fn:bug822670">
<p>See <a href="https://bugs.debian.org/cgi-bin/bugreport.cgi?archive=yes&amp;bug=822670">#822670</a>. <a class="footnote-backref" href="#fnref:bug822670" title="Jump back to footnote 8 in the text">↩</a></p>
</li>
<li id="fn:naming">
<p>The Debian Policy doesn’t provide any hint for the naming
convention of these system users. A common usage is to
prefix the daemon name with an underscore (like
<code>_memcached</code>). Another common usage is to use <code>Debian-</code> as
a prefix. The main drawback of the latter solution is that
the name is likely to be replaced by the UID in <code>ps</code> and
<code>top</code> because of its length. <a class="footnote-backref" href="#fnref:naming" title="Jump back to footnote 9 in the text">↩</a></p>
</li>
<li id="fn:clean">
<p>We could call <code>dh_auto_clean</code> at the end of the target to
let it invoke <code>make clean</code>. However, it is assumed that a
fresh checkout is used before each build. <a class="footnote-backref" href="#fnref:clean" title="Jump back to footnote 10 in the text">↩</a></p>
</li>
</ol>
</div>
      </div>
    </content>
  </entry>
  <entry>
    <title type="html">Local corporate APT repositories</title>
    <author><name>Vincent Bernat</name></author>
    <link href="https://vincent.bernat.ch/en/blog/2014-local-apt-repositories" rel="alternate"/>
    <link href="https://vincent.bernat.ch/en/blog/2014-local-apt-repositories#isso-thread" rel="replies" type="text/html"/>
    <updated>2014-04-27T13:43:25Z</updated>
    <id>http://www.luffy.cx/en/blog/2014-local-apt-repositories.html</id>

    <content type="xhtml">
      <div xmlns="http://www.w3.org/1999/xhtml">
<p>Distributing software efficiently across your platform can be
difficult. Every distribution comes with a package manager which is
usually suited for this task. <abbr title="Advanced Packaging Tool">APT</abbr> can be relied upon when using
<a href="https://www.debian.org/" title="Debian, the universal operating system">Debian</a> or a derivative.</p>
<p>Unfortunately, the official repositories may not contain everything
you need. When you require unpackaged software or more recent versions,
it is possible to set up your own local repository.</p>
<p>Most of what is presented here was set up for <a href="https://www.dailymotion.com/" title="Watch, publish, share videos">Dailymotion</a> and was
greatly inspired by the work done by <a href="https://raphink.info/" title="Raphaël Pinson's blog">Raphaël Pinson</a> at Orange.</p>
<div class="toc">
<ul>
<li><a href="#setting-up-your-repositories">Setting up your repositories</a><ul>
<li><a href="#initial-setup">Initial setup</a></li>
<li><a href="#local-mirror-of-an-official-distribution">Local mirror of an official distribution</a></li>
<li><a href="#repository-for-local-packages">Repository for local packages</a></li>
<li><a href="#local-mirror-of-third-party-repositories">Local mirror of third-party repositories</a></li>
</ul>
</li>
<li><a href="#building-debian-packages">Building Debian packages</a><ul>
<li><a href="#builders">Builders</a></li>
<li><a href="#version-numbering">Version numbering</a></li>
<li><a href="#uploading">Uploading</a></li>
</ul>
</li>
</ul>
</div>
<h1 id="setting-up-your-repositories">Setting up your repositories</h1>
<p>There are three kinds of repositories you may want to set up:</p>
<ol>
<li>
<p>A <strong>distribution mirror</strong>. Such a mirror will save bandwidth,
    provide faster downloads and permanent access, even when someone
    <a href="https://www.youtube.com/watch?v=v2FMqtC1x9Y">types Google into Google</a>.</p>
</li>
<li>
<p>A <strong>local repository</strong> for your own packages with the ability to
    have a staging zone to test packages on some servers before
    putting them in production.</p>
</li>
<li>
<p>Mirrors for <strong>unofficial repositories</strong>, like <a href="https://web.archive.org/web/2014/https://help.launchpad.net/Packaging/PPA" title="Personal Package Archives for Ubuntu">Ubuntu PPA</a>. To
    avoid unexpected changes, such a repository will also get a
    staging and a production zone.</p>
</li>
</ol>
<p>Before going further, it is important to understand what a
repository is. Let’s illustrate with the following line from my
<code>/etc/apt/sources.list</code>:</p>
<div class="language-debian-sourcelist codehilite"><pre><span/><code><span class="k">deb</span><span class="w"> </span><span class="s">http://ftp.debian.org/debian/</span><span class="w"> </span><span class="kp">unstable</span><span class="w"> </span><span class="kp">main</span><span class="w"> </span><span class="kp">contrib</span><span class="w"> </span><span class="kp">non-free</span>
</code></pre></div>

<p>In this example, <code>http://ftp.debian.org/debian/</code> is the <strong>repository</strong>
and <code>unstable</code> is the <strong>distribution</strong>. A distribution is subdivided
into <strong>components</strong>. We have three components: <code>main</code>, <code>contrib</code>, and
<code>non-free</code>.</p>
<p>To set up repositories, we will use <a href="https://web.archive.org/web/2014/http://mirrorer.alioth.debian.org/" title="reprepro: local repositories for Debian packages">reprepro</a>. This is not the only
solution but it has a good balance between versatility and
simplicity. <em>reprepro</em> can only handle one repository. The first
choice is about how you will split your packages into repositories,
distributions, and components.</p>
<div class="admonition">
<p class="admonition-title">Update (2019-06)</p>
<p><a href="https://www.aptly.info/" title="aptly: Debian repository management">aptly</a> is an alternative to <em>reprepro</em>. It handles multiple repositories.
It features an HTTP API. It can keep older versions of a package. To mirror
complete distributions, <em>reprepro</em> is more performant but for everything
else, I advise you to use <em>aptly</em>.</p>
</div>
<p>Here is what matters:</p>
<ul>
<li>A <strong>repository</strong> cannot contain two identical packages (same name,
   version, and architecture).</li>
<li>Inside a <strong>component</strong>, you can only have one version of a package.</li>
<li>Usually, a <strong>distribution is a subset of the versions</strong> while a
   <strong>component is a subset of the packages</strong>. For example, in Debian,
   with the distribution <code>unstable</code>, you choose to get the most recent
   versions while with the component <code>main</code>, you choose to get <abbr title="Debian Free Software Guidelines">DFSG</abbr>-free
   software only.</li>
</ul>
<p>If you go for several repositories, you will have to handle several
<em>reprepro</em> instances and won’t be able to easily copy packages from
one place to another. At <a href="https://www.dailymotion.com/" title="Watch, publish, share videos">Dailymotion</a>, we put everything in the
same repository but it would also be perfectly valid to have three repositories:</p>
<ul>
<li>one to mirror the distribution;</li>
<li>one for your local packages; and</li>
<li>one to mirror unofficial repositories.</li>
</ul>
<p>Here is our target setup:</p>
<figure class="lf-fullbleed"><div class="lf-media-outer" style="width: 654px"><span class="lf-media-inner" style="padding-bottom: 63.609%"><img alt="Local APT repository" src="https://media-bernat-ch.b-cdn.net/images/apt-repository.559851520741ee.svg" width="654" height="416" class="lf-media"/></span></div><figcaption>A repository with three distributions, including a staging area.</figcaption></figure>
<h2 id="initial-setup">Initial setup</h2>
<p>First, create a system user to work with the repositories:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>adduser<span class="w"> </span>--system<span class="w"> </span>--disabled-password<span class="w"> </span>--disabled-login<span class="w"> </span><span class="se">\</span>
<span class="gp">&gt; </span><span class="w">        </span>--home<span class="w"> </span>/srv/packages<span class="w"> </span><span class="se">\</span>
<span class="gp">&gt; </span><span class="w">        </span>--group<span class="w"> </span>reprepro
</code></pre></div>

<p>All operations should be done with this user only. If you want to
set up several repositories, create a directory for each of
them. Each repository has these subdirectories:</p>
<ul>
<li><code>conf/</code> contains the configuration files</li>
<li><code>gpg/</code> contains the GPG stuff to sign the repository<sup id="fnref:sharegpg"><a class="footnote-ref" href="#fn:sharegpg">1</a></sup></li>
<li><code>logs/</code> contains the logs</li>
<li><code>www/</code> contains the repository that should be exported by the web server</li>
</ul>
<p>Here is the content of <code>conf/options</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>outdir +b/www
logdir +b/logs
gnupghome +b/gpg
</code></pre></div>

<p>Then, you need to create the GPG key to sign the repository:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span><span class="nv">GNUPGHOME</span><span class="o">=</span>gpg<span class="w"> </span>gpg<span class="w"> </span>--gen-key
<span class="go">Please select what kind of key you want:</span>
<span class="go">   (1) RSA and RSA (default)</span>
<span class="go">   (2) DSA and Elgamal</span>
<span class="go">   (3) DSA (sign only)</span>
<span class="go">   (4) RSA (sign only)</span>
<span class="go">Your selection? 1</span>
<span class="go">RSA keys may be between 1024 and 4096 bits long.</span>
<span class="go">What keysize do you want? (2048) 4096</span>
<span class="go">Requested keysize is 4096 bits</span>
<span class="go">Please specify how long the key should be valid.</span>
<span class="go">         0 = key does not expire</span>
<span class="go">      &lt;n&gt;  = key expires in n days</span>
<span class="go">      &lt;n&gt;w = key expires in n weeks</span>
<span class="go">      &lt;n&gt;m = key expires in n months</span>
<span class="go">      &lt;n&gt;y = key expires in n years</span>
<span class="go">Key is valid for? (0) 10y</span>
<span class="go">Key expires at mer. 08 nov. 2023 22:30:58 CET</span>
<span class="go">Is this correct? (y/N) y</span>

<span class="go">Real name: Dailymotion Archive Automatic Signing Key</span>
<span class="go">Email address: the-it-operations@dailymotion.com</span>
<span class="go">Comment:</span>
<span class="go">[…]</span>
</code></pre></div>

<p>By setting an empty password, you allow <em>reprepro</em> to run unattended.
You will have to distribute the public key of your new repository to
let <abbr title="Advanced Packaging Tool">APT</abbr> check the archive signature. An easy way is to ship it in some
package.</p>
<h2 id="local-mirror-of-an-official-distribution">Local mirror of an official distribution</h2>
<p>Let’s start by mirroring a distribution. We want a local mirror of
Ubuntu Precise. For this, we need to do two things:</p>
<ol>
<li>Set up a new <strong>distribution</strong> in <code>conf/distributions</code>.</li>
<li>Configure the <strong>update sources</strong> in <code>conf/updates</code>.</li>
</ol>
<p>Let’s add this block to <code>conf/distributions</code>:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="c1"># Ubuntu Precise</span>
<span class="na">Origin</span><span class="o">:</span><span class="w"> </span><span class="s">Ubuntu</span>
<span class="na">Label</span><span class="o">:</span><span class="w"> </span><span class="s">Ubuntu</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise</span>
<span class="na">Version</span><span class="o">:</span><span class="w"> </span><span class="s">12.04</span>
<span class="na">Codename</span><span class="o">:</span><span class="w"> </span><span class="s">precise</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">i386 amd64</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">UDebComponents</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">Description</span><span class="o">:</span><span class="w"> </span><span class="s">Ubuntu Precise 12.04 (with updates and security)</span>
<span class="na">Contents</span><span class="o">:</span><span class="w"> </span><span class="s">.gz .bz2</span>
<span class="na">UDebIndices</span><span class="o">:</span><span class="w"> </span><span class="s">Packages Release . .gz</span>
<span class="na">Tracking</span><span class="o">:</span><span class="w"> </span><span class="s">minimal</span>
<span class="na">Update</span><span class="o">:</span><span class="w"> </span><span class="s">- ubuntu-precise ubuntu-precise-updates ubuntu-precise-security</span>
<span class="na">SignWith</span><span class="o">:</span><span class="w"> </span><span class="s">yes</span>
</code></pre></div>

<p>This defines the <code>precise</code> distribution in our repository. It contains
four components: <code>main</code>, <code>restricted</code>, <code>universe</code>, and <code>multiverse</code>
(like the regular distribution in official repositories).</p>
<p>The <code>Update</code> line starts with a dash. This means <em>reprepro</em> will
mark everything as deleted before updating with the provided
sources. Old packages will not be kept when they are removed from
Ubuntu. In <code>conf/updates</code>, we define the sources:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="c1"># Ubuntu Precise</span>
<span class="na">Name</span><span class="o">:</span><span class="w"> </span><span class="s">ubuntu-precise</span>
<span class="na">Method</span><span class="o">:</span><span class="w"> </span><span class="s">http://fr.archive.ubuntu.com/ubuntu</span>
<span class="na">Fallback</span><span class="o">:</span><span class="w"> </span><span class="s">http://de.archive.ubuntu.com/ubuntu</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main multiverse restricted universe</span>
<span class="na">UDebComponents</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">amd64 i386</span>
<span class="na">VerifyRelease</span><span class="o">:</span><span class="w"> </span><span class="s">437D05B5</span>
<span class="na">GetInRelease</span><span class="o">:</span><span class="w"> </span><span class="s">no</span>

<span class="c1"># Ubuntu Precise Updates</span>
<span class="na">Name</span><span class="o">:</span><span class="w"> </span><span class="s">ubuntu-precise-updates</span>
<span class="na">Method</span><span class="o">:</span><span class="w"> </span><span class="s">http://fr.archive.ubuntu.com/ubuntu</span>
<span class="na">Fallback</span><span class="o">:</span><span class="w"> </span><span class="s">http://de.archive.ubuntu.com/ubuntu</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-updates</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">UDebComponents</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">amd64 i386</span>
<span class="na">VerifyRelease</span><span class="o">:</span><span class="w"> </span><span class="s">437D05B5</span>
<span class="na">GetInRelease</span><span class="o">:</span><span class="w"> </span><span class="s">no</span>

<span class="c1"># Ubuntu Precise Security</span>
<span class="na">Name</span><span class="o">:</span><span class="w"> </span><span class="s">ubuntu-precise-security</span>
<span class="na">Method</span><span class="o">:</span><span class="w"> </span><span class="s">http://fr.archive.ubuntu.com/ubuntu</span>
<span class="na">Fallback</span><span class="o">:</span><span class="w"> </span><span class="s">http://de.archive.ubuntu.com/ubuntu</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-security</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">UDebComponents</span><span class="o">:</span><span class="w"> </span><span class="s">main restricted universe multiverse</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">amd64 i386</span>
<span class="na">VerifyRelease</span><span class="o">:</span><span class="w"> </span><span class="s">437D05B5</span>
<span class="na">GetInRelease</span><span class="o">:</span><span class="w"> </span><span class="s">no</span>
</code></pre></div>

<p>The <code>VerifyRelease</code> lines are GPG key fingerprint to use to check the
remote repository. The key needs to be imported in the local keyring:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>gpg<span class="w"> </span>--keyring<span class="w"> </span>/usr/share/keyrings/ubuntu-archive-keyring.gpg<span class="w"> </span><span class="se">\</span>
<span class="gp">&gt; </span><span class="w">    </span>--export<span class="w"> </span>437D05B5<span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nv">GNUPGHOME</span><span class="o">=</span>gpg<span class="w"> </span>gpg<span class="w"> </span>--import
</code></pre></div>

<p>Another important point is that we merge three distributions
(<code>precise</code>, <code>precise-updates</code> and <code>precise-security</code>) into a single
distribution (<code>precise</code>) in our local repository. This may cause some
difficulties with tools expecting the three distributions to be
available (like the Debian Installer<sup id="fnref:installer"><a class="footnote-ref" href="#fn:installer">2</a></sup>).</p>
<p>Next, you can run <em>reprepro</em> and ask it to update your local mirror:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>reprepro<span class="w"> </span>update
</code></pre></div>

<p>This will take some time on the first run. You can execute this
command every night. <em>reprepro</em> is not the fastest mirror solution but
it is easy to set up, flexible, and reliable.</p>
<h2 id="repository-for-local-packages">Repository for local packages</h2>
<p>Let’s configure the repository to accept local packages. For each
official distribution (like <code>precise</code>), we will configure two
distributions:</p>
<ul>
<li><code>precise-staging</code> contains packages that have not been fully
   tested and are not ready to go to production.</li>
<li><code>precise-prod</code> contains production packages copied from
   <code>precise-staging</code>.</li>
</ul>
<p>In our workflow, packages are introduced in <code>precise-staging</code> where
they can be tested and will be copied to <code>precise-prod</code> when we want
them to be available for production. You can adopt a more complex
workflow if you need it. The <em>reprepro</em> part is quite easy. We add the
following blocks into <code>conf/distributions</code>:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="c1"># Dailymotion Precise packages (staging)</span>
<span class="na">Origin</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion</span><span class="w"> </span><span class="c1"># ❸</span>
<span class="na">Label</span><span class="o">:</span><span class="w"> </span><span class="s">dm-staging</span><span class="w">   </span><span class="c1"># ❸</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-staging</span>
<span class="na">Codename</span><span class="o">:</span><span class="w"> </span><span class="s">precise-staging</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">i386 amd64 source</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main role/dns role/database role/web</span><span class="w"> </span><span class="c1"># ❶</span>
<span class="na">Description</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion Precise staging repository</span>
<span class="na">Contents</span><span class="o">:</span><span class="w"> </span><span class="s">.gz .bz2</span>
<span class="na">Tracking</span><span class="o">:</span><span class="w"> </span><span class="s">keep</span>
<span class="na">SignWith</span><span class="o">:</span><span class="w"> </span><span class="s">yes</span>
<span class="na">NotAutomatic</span><span class="o">:</span><span class="w"> </span><span class="s">yes</span><span class="w"> </span><span class="c1"># ❷</span>
<span class="na">Log</span><span class="o">:</span><span class="w"> </span><span class="s">packages.dm-precise-staging.log</span>
<span class="w"> </span><span class="na">--type</span><span class="o">=</span><span class="s">dsc email-changes</span>

<span class="c1"># Dailymotion Precise packages (prod)</span>
<span class="na">Origin</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion</span><span class="w"> </span><span class="c1"># ❸</span>
<span class="na">Label</span><span class="o">:</span><span class="w"> </span><span class="s">dm-prod</span><span class="w">      </span><span class="c1"># ❸</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-prod</span>
<span class="na">Codename</span><span class="o">:</span><span class="w"> </span><span class="s">precise-prod</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">i386 amd64 source</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main role/dns role/database role/web</span><span class="w"> </span><span class="c1"># ❶</span>
<span class="na">Description</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion Precise prod repository</span>
<span class="na">Contents</span><span class="o">:</span><span class="w"> </span><span class="s">.gz .bz2</span>
<span class="na">Tracking</span><span class="o">:</span><span class="w"> </span><span class="s">keep</span>
<span class="na">SignWith</span><span class="o">:</span><span class="w"> </span><span class="s">yes</span>
<span class="na">Log</span><span class="o">:</span><span class="w"> </span><span class="s">packages.dm-precise-prod.log</span>
</code></pre></div>

<p>First notice we use several components (in ❶):</p>
<ul>
<li><code>main</code> will contain packages that are not specific to a subset of
   the platform. If you put a package in <code>main</code>, it should work
   correctly on any host.</li>
<li><code>role/*</code> are components dedicated to a subset of the platform. For
   example, in <code>role/dns</code>, we ship a custom version of <em>BIND</em>.</li>
</ul>
<p>The staging distribution has the <code>NotAutomatic</code> flag (in ❷) which
disallows the package manager to install these packages except if the
user explicitly requests it. Just below, when a new <code>dsc</code> file is
uploaded, the hook <a href="https://gist.github.com/vincentbernat/7404733" title="email-changes hook for reprepro"><code>email-changes</code></a> will be
executed. It should be in the <code>conf/</code> directory.</p>
<p>The <code>Origin</code> and <code>Label</code> lines (in ❸) are quite important to be able
to define an explicit policy of which packages should be
installed. Let’s say we use the following <code>/etc/apt/sources.list</code>
file:</p>
<div class="language-debian-sourcelist codehilite"><pre><span/><code><span class="c"># Ubuntu packages</span>
<span class="k">deb</span><span class="w"> </span><span class="s">http://packages.dm.gg/dailymotion</span><span class="w"> </span><span class="kp">precise</span><span class="w"> </span><span class="kp">main</span><span class="w"> </span><span class="kp">restricted</span><span class="w"> </span><span class="kp">universe</span><span class="w"> </span><span class="kp">multiverse</span>

<span class="c"># Dailymotion packages</span>
<span class="k">deb</span><span class="w"> </span><span class="s">http://packages.dm.gg/dailymotion</span><span class="w"> </span><span class="kp">precise-prod</span><span class="w">    </span><span class="kp">main</span><span class="w"> </span><span class="kp">role/dns</span>
<span class="k">deb</span><span class="w"> </span><span class="s">http://packages.dm.gg/dailymotion</span><span class="w"> </span><span class="kp">precise-staging</span><span class="w"> </span><span class="kp">main</span><span class="w"> </span><span class="kp">role/dns</span>
</code></pre></div>

<p>All servers have the <code>precise-staging</code> distribution. We must ensure
we won’t install these packages by mistake. The <code>NotAutomatic</code>
flag is one possible safeguard. We also use a tailored
<code>/etc/apt/preferences</code>:</p>
<div class="language-text-only codehilite"><pre><span/><code>Explanation: Dailymotion packages of a specific component should be more preferred
Package: *
Pin: release o=Dailymotion, l=dm-prod, c=role/*
Pin-Priority: 950

Explanation: Dailymotion packages should be preferred
Package: *
Pin: release o=Dailymotion, l=dm-prod
Pin-Priority: 900

Explanation: staging should never be preferred
Package: *
Pin: release o=Dailymotion, l=dm-staging
Pin-Priority: -100
</code></pre></div>

<p>By default, packages will have a priority of 500. By setting a
priority of -100 to the staging distribution, we ensure the packages
cannot be installed at all. This is stronger than <code>NotAutomatic</code> which
sets the priority to 1. When a package exists in Ubuntu and our local
repository, we ensure that, if this is a production package, we will
use ours by using a priority of 900 (or 950 if we match a specific
role component).</p>
<p>Have a look at the “<em>How <abbr title="Advanced Packaging Tool">APT</abbr> Interprets Priorities</em>” section of
<a href="https://manpages.debian.org/apt_preferences.5.html" title="Manual page for apt_preferences(5)"><code>apt_preferences(5)</code></a> manual page for additional
information. Keep in mind that version matters only when the priority
is the same. To check if everything works as you expect, use
<code>apt-cache policy</code>:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>apt-cache<span class="w"> </span>policy<span class="w"> </span>php5-memcache
<span class="go">  Installed: 3.0.8-1~precise2~dm1</span>
<span class="go">  Candidate: 3.0.8-1~precise2~dm1</span>
<span class="go">  Version table:</span>
<span class="go"> *** 3.0.8-1~precise2~dm1 0</span>
<span class="go">        950 http://packages.dm.gg/dailymotion/ precise-prod/role/web amd64 Packages</span>
<span class="go">        100 /var/lib/dpkg/status</span>
<span class="go">     3.0.8-1~precise1~dm4 0</span>
<span class="go">        900 http://packages.dm.gg/dailymotion/ precise-prod/main amd64 Packages</span>
<span class="go">       -100 http://packages.dm.gg/dailymotion/ precise-staging/main amd64 Packages</span>
<span class="go">     3.0.6-1 0</span>
<span class="go">        500 http://packages.dm.gg/dailymotion/ precise/universe amd64 Packages</span>
</code></pre></div>

<p>If we want to install a package from the staging distribution, we can
use <code>apt-get</code> with the <code>-t precise-staging</code> option to raise the
priority of this distribution to 990.</p>
<p>Once you have tested your package, you can copy it from the staging
distribution to the production distribution:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>reprepro<span class="w"> </span>-C<span class="w"> </span>main<span class="w"> </span>copysrc<span class="w"> </span>precise-prod<span class="w"> </span>precise-staging<span class="w"> </span>wackadoodle
</code></pre></div>

<h2 id="local-mirror-of-third-party-repositories">Local mirror of third-party repositories</h2>
<p>Sometimes, you want a piece of software published on some third-party
repository without repackaging it yourself. A common example is the
repositories edited by hardware vendors. As for an Ubuntu mirror,
there are two steps: defining the distribution and defining the
source.</p>
<p>We chose to put such mirrors into the same distributions as our local
packages but with a dedicated component for each mirror. This way,
these third-party packages will share the same workflow as our local
packages: they will appear in the staging distribution, we validate
them and copy them to the production distribution.</p>
<p>The first step is to add the components and an appropriate <code>Update</code>
line to <code>conf/distributions</code>:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="na">Origin</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion</span>
<span class="na">Label</span><span class="o">:</span><span class="w"> </span><span class="s">dm-staging</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-staging</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main role/dns role/database role/web vendor/hp</span>
<span class="na">Update</span><span class="o">:</span><span class="w"> </span><span class="s">hp</span>
<span class="c1"># […]</span>

<span class="na">Origin</span><span class="o">:</span><span class="w"> </span><span class="s">Dailymotion</span>
<span class="na">Label</span><span class="o">:</span><span class="w"> </span><span class="s">dm-prod</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise-prod</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">main role/dns role/database role/web vendor/hp</span>
<span class="c1"># […]</span>
</code></pre></div>

<p>We added the <code>vendor/hp</code> component to both the staging and the
production distributions. However, only the staging distribution gets
an <code>Update</code> line (remember, packages will be copied manually into the
production distribution).</p>
<p>We declare the source in <code>conf/updates</code>:</p>
<div class="language-ini codehilite"><pre><span/><code><span class="c1"># HP repository</span>
<span class="na">Name</span><span class="o">:</span><span class="w"> </span><span class="s">hp</span>
<span class="na">Method</span><span class="o">:</span><span class="w"> </span><span class="s">http://downloads.linux.hp.com/SDR/downloads/ManagementComponentPack/</span>
<span class="na">Suite</span><span class="o">:</span><span class="w"> </span><span class="s">precise/current</span>
<span class="na">Components</span><span class="o">:</span><span class="w"> </span><span class="s">non-free&gt;vendor/hp</span>
<span class="na">Architectures</span><span class="o">:</span><span class="w"> </span><span class="s">i386 amd64</span>
<span class="na">VerifyRelease</span><span class="o">:</span><span class="w"> </span><span class="s">2689B887</span>
<span class="na">GetInRelease</span><span class="o">:</span><span class="w"> </span><span class="s">no</span>
</code></pre></div>

<p>Don’t forget to add the GPG key to your local keyring. Notice an
interesting feature of <em>reprepro</em>: we copy the remote <code>non-free</code>
component to our local <code>vendor/hp</code> component.</p>
<p>Then, you can synchronize the mirror with <code>reprepro update</code>. Once the
packages have been tested, you will have to copy them in the
production distribution.</p>
<h1 id="building-debian-packages">Building Debian packages</h1>
<p>Our <em>reprepro</em> setup seems complete, but how do we put packages into
the staging distribution?</p>
<p>You have several options to build Debian packages for your local
repository. It depends on how much time you want to invest in this
activity:</p>
<ol>
<li>
<p>Build packages <strong>from source</strong> by adding a <code>debian/</code>
    directory. This is the classic way of building Debian
    packages. You can start from scratch or use an existing package as
    a base. In the latter case, the package can be from the official
    archive but for a more recent distribution or a backport or from
    an unofficial repository.</p>
</li>
<li>
<p>Use a <strong>tool that will create a binary package from a directory</strong>,
    like <a href="https://github.com/jordansissel/fpm" title="fpm: Effing package managers">fpm</a>. Such a tool will try to guess a lot of things to
    minimize your work. It can even download everything for you.</p>
</li>
</ol>
<p>There is no universal solution. If you don’t have the time budget for
building packages from source, have a look at <a href="https://github.com/jordansissel/fpm" title="fpm: Effing package managers">fpm</a>. I would advise
you to use the first approach when possible because you will get these
perks for free:</p>
<ul>
<li>
<p>You <strong>keep the sources</strong> in your repository. Whenever you need to
   rebuild something to fix an emergency bug, you won’t have to hunt
   the sources which may be unavailable when you need them the
   most. This only works if you build packages that don’t download
   stuff directly from the Internet.</p>
</li>
<li>
<p>You also <strong>keep the recipe</strong><sup id="fnref:recipe"><a class="footnote-ref" href="#fn:recipe">3</a></sup> to build the package in your
   repository. If someone enables some option and rebuilds the package,
   you won’t accidentally drop this option on the next build. These
   changes can be documented in <code>debian/changelog</code>. Moreover, you can
   use a version control software for the whole <code>debian/</code> directory.</p>
</li>
<li>
<p>You can propose your package for <strong>inclusion into Debian</strong>. This
   will help many people once the package hits the archive.</p>
</li>
</ul>
<div class="admonition">
<p class="admonition-title">Update (2017-10)</p>
<p>I present an alternate approach to create Debian packages without much
effort in “<a href="/en/blog/2016-pragmatic-debian-packaging" title="Pragmatic Debian packaging">Pragmatic Debian packaging</a>.”</p>
</div>
<h2 id="builders">Builders</h2>
<p>We chose <a href="https://pbuilder-docs.readthedocs.io/en/latest/index.html" title="pbuilder users's manual">pbuilder</a> as a builder.<sup id="fnref:vssbuild"><a class="footnote-ref" href="#fn:vssbuild">4</a></sup> Its setup is quite
straightforward. Here is our <code>/etc/pbuilderrc</code>:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="nv">DISTRIBUTION</span><span class="o">=</span><span class="nv">$DIST</span>
<span class="nv">NAME</span><span class="o">=</span><span class="s2">"</span><span class="nv">$DIST</span><span class="s2">-</span><span class="nv">$ARCH</span><span class="s2">"</span>
<span class="nv">MIRRORSITE</span><span class="o">=</span>http://packages.dm.gg/dailymotion
<span class="nv">COMPONENTS</span><span class="o">=(</span><span class="s2">"main"</span><span class="w"> </span><span class="s2">"restricted"</span><span class="w"> </span><span class="s2">"universe"</span><span class="w"> </span><span class="s2">"multiverse"</span><span class="o">)</span>
<span class="nv">OTHERMIRROR</span><span class="o">=</span><span class="s2">"deb http://packages.dm.gg/dailymotion </span><span class="si">${</span><span class="nv">DIST</span><span class="si">}</span><span class="s2">-staging main"</span>
<span class="nv">HOOKDIR</span><span class="o">=</span>/etc/pbuilder/hooks.d
<span class="nv">BASE</span><span class="o">=</span>/var/cache/pbuilder/dailymotion
<span class="nv">BASETGZ</span><span class="o">=</span><span class="nv">$BASE</span>/<span class="nv">$NAME</span>/base.tgz
<span class="nv">BUILDRESULT</span><span class="o">=</span><span class="nv">$BASE</span>/<span class="nv">$NAME</span>/results/
<span class="nv">APTCACHE</span><span class="o">=</span><span class="nv">$BASE</span>/<span class="nv">$NAME</span>/aptcache/
<span class="nv">DEBBUILDOPTS</span><span class="o">=</span><span class="s2">"-sa"</span>
<span class="nv">KEYRING</span><span class="o">=</span><span class="s2">"/usr/share/keyrings/dailymotion-archive.keyring.gpg"</span>
<span class="nv">DEBOOTSTRAPOPTS</span><span class="o">=(</span><span class="s2">"--arch"</span><span class="w"> </span><span class="s2">"</span><span class="nv">$ARCH</span><span class="s2">"</span><span class="w"> </span><span class="s2">"--variant=buildd"</span><span class="w"> </span><span class="s2">"</span><span class="si">${</span><span class="nv">DEBOOTSTRAPOPTS</span><span class="p">[@]</span><span class="si">}</span><span class="s2">"</span><span class="w"> </span><span class="s2">"--keyring=</span><span class="nv">$KEYRING</span><span class="s2">"</span><span class="o">)</span>
<span class="nv">APTKEYRINGS</span><span class="o">=(</span><span class="s2">"</span><span class="nv">$KEYRING</span><span class="s2">"</span><span class="o">)</span>
<span class="nv">EXTRAPACKAGES</span><span class="o">=(</span><span class="s2">"dailymotion-archive-keyring"</span><span class="o">)</span>
</code></pre></div>

<p><code>pbuilder</code> is expected to be invoked with <code>DIST</code>, <code>ARCH</code> and
optionally <code>ROLE</code> environment variables. Building the initial bases
can be done like this:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="k">for</span><span class="w"> </span>ARCH<span class="w"> </span><span class="k">in</span><span class="w"> </span>i386<span class="w"> </span>amd64<span class="p">;</span><span class="w"> </span><span class="k">do</span>
<span class="w">  </span><span class="k">for</span><span class="w"> </span>DIST<span class="w"> </span><span class="k">in</span><span class="w"> </span>precise<span class="p">;</span><span class="w"> </span><span class="k">do</span>
<span class="w">    </span><span class="nb">export</span><span class="w"> </span>ARCH
<span class="w">    </span><span class="nb">export</span><span class="w"> </span>DIST
<span class="w">    </span>pbuilder<span class="w"> </span>--create
<span class="w">  </span><span class="k">done</span>
<span class="k">done</span>
</code></pre></div>

<p>We don’t create a base for each role. Instead, we use a <code>D</code> hook to
add the appropriate source:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="ch">#!/bin/bash</span>
<span class="o">[</span><span class="w"> </span>-z<span class="w"> </span><span class="s2">"</span><span class="nv">$ROLE</span><span class="s2">"</span><span class="w"> </span><span class="o">]</span><span class="w"> </span><span class="o">||</span><span class="w"> </span><span class="o">{</span>
<span class="w">  </span>cat<span class="w"> </span>&gt;&gt;<span class="w"> </span>/etc/apt/sources.list<span class="w"> </span><span class="s">&lt;&lt;EOF</span>
<span class="s">deb http://packages.dm.gg/dailymotion ${DIST}-staging role/${ROLE}</span>
<span class="s">EOF</span>
<span class="o">}</span>

apt-get<span class="w"> </span>update
</code></pre></div>

<p>We ensure packages from our staging distribution are preferred
over other packages by adding an <code>/etc/apt/preferences</code> file
in an <code>E</code> hook:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="ch">#!/bin/bash</span>

cat<span class="w"> </span>&gt;<span class="w"> </span>/etc/apt/preferences<span class="w"> </span><span class="s">&lt;&lt;EOF</span>
<span class="s">Explanation: Dailymotion packages are of higher priority</span>
<span class="s">Package: *</span>
<span class="s">Pin: release o=Dailymotion</span>
<span class="s">Pin-Priority: 900</span>
<span class="s">EOF</span>
</code></pre></div>

<p>We also use a <code>C</code> hook to get a shell in case there is an error. This
is convenient to debug a problem:</p>
<div class="language-bash codehilite"><pre><span/><code><span class="ch">#!/bin/bash</span>
apt-get<span class="w"> </span>install<span class="w"> </span>-y<span class="w"> </span>--force-yes<span class="w"> </span>vim<span class="w"> </span>less
<span class="nb">cd</span><span class="w"> </span>/tmp/buildd/*/debian/..
/bin/bash<span class="w"> </span>&lt;<span class="w"> </span>/dev/tty<span class="w"> </span>&gt;<span class="w"> </span>/dev/tty<span class="w"> </span><span class="m">2</span>&gt;<span class="w"> </span>/dev/tty
</code></pre></div>

<p>A manual build can be run with:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span><span class="nv">ARCH</span><span class="o">=</span>amd64<span class="w"> </span><span class="nv">DIST</span><span class="o">=</span>precise<span class="w"> </span><span class="nv">ROLE</span><span class="o">=</span>web<span class="w"> </span>pbuilder<span class="w"> </span><span class="se">\</span>
<span class="gp">&gt; </span><span class="w">        </span>--build<span class="w"> </span>somepackage.dsc
</code></pre></div>

<h2 id="version-numbering">Version numbering</h2>
<p>To avoid applying complex rules to choose a version number for a package,
we chose to treat everything as a backport, even in-house software. We
use the following scheme: <code>X-Y~preciseZ+dmW</code>.</p>
<ul>
<li>
<p><code>X</code> is the upstream version.<sup id="fnref:snapshot"><a class="footnote-ref" href="#fn:snapshot">5</a></sup></p>
</li>
<li>
<p><code>Y</code> is the Debian version. If there is no Debian version, use 0.</p>
</li>
<li>
<p><code>Z</code> is the Ubuntu backport version. Again, if such a version
   doesn’t exist, use 0.</p>
</li>
<li>
<p><code>W</code> is our version of the package. We increment it when we make a
   change to the packaging. This is the only number we are
   allowed to control. All the others are set by an upstream entity
   unless it doesn’t exist and, in this case, you use 0.</p>
</li>
</ul>
<p>Let’s suppose you need to backport <code>wackadoodle</code>. It is available in a
more recent version of Ubuntu as <code>1.4-3</code>. Your first backport will be
<code>1.4-3~precise0+dm1</code>. After a change to the packaging, the version
will be <code>1.4-3~precise0+dm2</code>. A new upstream version <code>1.5</code>
is available and you need it. You will use <code>1.5-0~precise0+dm1</code>.</p>
<p>Later, this new upstream version will be available in some version of
Ubuntu as <code>1.5-3ubuntu1</code>. You will rebase your changes on this version
and get <code>1.5-3ubuntu1~precise0+dm1</code>.</p>
<p>When using Debian instead of Ubuntu, a compatible convention could
be: <code>X-Y~bpo70+Z~dm+W</code>.</p>
<h2 id="uploading">Uploading</h2>
<p>To upload a package, a common setup is the following workflow:</p>
<ol>
<li>Upload the source package to an incoming directory.</li>
<li><em>reprepro</em> will notice the <em>source</em> package, check its
    correctness (signature, distribution), and put it in the archive.</li>
<li>The builder will notice a new package needs to be built and
    build it.</li>
<li>Once the package is built, the builder will upload the result to
    the incoming directory.</li>
<li><em>reprepro</em> will notice again the new <em>binary</em> package and
    integrate it into the archive.</li>
</ol>
<p>This workflow has the disadvantage to have many moving pieces and to
leave the user in the dark while the compilation is in
progress. Instead, a build script is used to execute each step
synchronously. The user can follow on their terminal that everything
works as expected. The build script also handles uploading the final
packages to the archive with the appropriate command:</p>
<div class="language-bash-session codehilite"><pre><span/><code><span class="gp">$ </span>reprepro<span class="w"> </span>-C<span class="w"> </span>main<span class="w"> </span>include<span class="w"> </span>precise-staging<span class="w"> </span><span class="se">\</span>
<span class="gp">&gt; </span><span class="w">     </span>wackadoodle_1.4-3~precise0+dm4_amd64.changes
</code></pre></div>

<p>Happy hacking!</p>
<div class="footnote">
<hr/>
<ol>
<li id="fn:sharegpg">
<p>The <code>gpg/</code> directory could be shared by several repositories. <a class="footnote-backref" href="#fnref:sharegpg" title="Jump back to footnote 1 in the text">↩</a></p>
</li>
<li id="fn:installer">
<p>We taught Debian Installer to work with our setup with
an <a href="https://gist.github.com/vincentbernat/7412457" title="Preseed file for Debian installer and a merged local repository">appropriate preseed file</a>. <a class="footnote-backref" href="#fnref:installer" title="Jump back to footnote 2 in the text">↩</a></p>
</li>
<li id="fn:recipe">
<p><a href="https://github.com/bernd/fpm-cookery" title="A tool for building software packages with fpm">fpm-cookery</a> is a convenient tool to write recipes
for <a href="https://github.com/jordansissel/fpm" title="fpm: Effing package managers">fpm</a>, similar to <a href="https://mxcl.dev/homebrew/" title="Homebrew: the missing package manager for OS X">Homebrew</a> or a BSD port
tree. It could be used to achieve the same goal. <a class="footnote-backref" href="#fnref:recipe" title="Jump back to footnote 3 in the text">↩</a></p>
</li>
<li id="fn:vssbuild">
<p><a href="https://wiki.debian.org/sbuild" title="sbuild">sbuild</a> is an alternative to <em>pbuilder</em> and is the
official builder for both Debian and
Ubuntu. Historically, <em>pbuilder</em> was more focused on
developers’ needs. <a class="footnote-backref" href="#fnref:vssbuild" title="Jump back to footnote 4 in the text">↩</a></p>
</li>
<li id="fn:snapshot">
<p>For a Git snapshot, we use something like <code>1.4-git20130905+1-ae42dc1</code> which is a snapshot made
after version 1.4 (use 0.0 if no version has ever been
released) at the given date. The following 1 is to be
able to package different snapshots on the same date
while the hash is here in case you need to retrieve
the exact snapshot. <a class="footnote-backref" href="#fnref:snapshot" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
</ol>
</div>
      </div>
    </content>
  </entry>
</feed>