Installing a new workstation (or reinstalling one) is quite a boring task; the OS setup itself is pretty quick, but then you have to install all the tools and applications you use every day.

I end up reinstalling both my laptop and desktop quite often, almost every time a new Ubuntu release comes out (both to check out the news in the installer and to start with a fresh system); so at some point I started putting the list of the packages I needed in a script, which would apt install them. This was a quick and simple solution that would allow me to install everything with a single command.

But wouldn't it be nice to also have an automatic way to keep this list in sync across mulitple machines and be able to also remove packages if they become unneeded?

To do that, metapackages are the perfect tool. They're just empty packages that depend on other packages. Our list of packages just becomes a Depends directive in the metapackage.

Metapackages to the rescue

Building a metapackage is very easy, all you need is a tool named equivs and a few steps:

  1. Install it

    $ sudo apt install equivs
    
  2. Create a template control file for the package

    $ equivs-control ack-desktop.control
    
  3. Edit the control file. Only a few lines are required, but at least at least Package, Version, Maintainer, and Description should be filled.

    But the most important directive, as said, is Depends. Here we must list all the packages we want to install. The result is something like this:

    Package: ack-desktop
    Version: 1.0
    Maintainer: Alberto Donato <alberto.donato@gmail.com>
    Architecture: all
    Section: misc
    Priority: optional
    Depends:
     emacs-snapshot, emacs-snapshot-el,
     bpython, ipython, ipython3, python-pip, python-virtualenv,
     ...
    Description: metapackage for installation of my package selection.
     This installs a whole set of packages that I currently use on Ubuntu desktops.
    
  4. Build the .deb package

    $ equivs-build ack-desktop.control
    

    which creates ack-desktop_1.0_all.deb in the current directory.

  5. Install package and dependencies

    $ sudo dpkg -i ack-desktop_1.0_all.deb
    $ sudo apt install -f  # pull in all dependencies
    
  6. Profit!

Whenever the list of needed packages changes, it's enough to update the control file, rebuild and install the new package.

IMPORTANT: you need to increment the package version at every change.

A nice thing of this approach is that all dependencies will be marked as automatically installed. So, if packages are removed from the list, apt will show them as "automatically installed and no longer needed", and sudo apt autoremove will uninstall them.

Setting up a repository

Now, what about making these package available on multiple computers?

The easiest way is to create a Debian repository hosting the metapackages, and make it available to all computers. This way, just adding an APT source on each machine will packages available everywhere, and they'll be updatable as any other package in the distribution.

Setting up a repository for just a couple of packages might seem overkill, but it's actually so simple that it's worth the effort.

A repository can be build and maintained with just few steps, using reprepro.

  1. Install it

    $ sudo apt install reprepro
    
  2. Create the base directory of your repository (let's call it ubuntu/), and a conf/ directory in it. Then create a distributions file in this directory, which will contain the configuration for reprepro:

    $ tree ubuntu/
    ubuntu/
    └── conf
        └── distributions
    
    1 directory, 1 file
    
  3. Edit the distributions file like this:

    Codename: unstable
    Architectures: source i386 amd64
    Origin: local-unstable
    Label: Local repository
    Components: main
    

    The reason for the "unstable" codename is that equivs will use it by default when building packages.

  4. From the ubuntu/ directory, import the package created before

    $ reprepro includedeb unstable ../ack-desktop_1.0_all.deb
    

    At the first import, reprepro will create the repository tree.

    Once the repository is set up, this is the only command that needs to be run again, when importing a new package or a new version for an existing package.

The repository is now ready to be used.

To make it available to all the machines, you can put host the ubuntu/ tree on on a machine running a web server, but if you already sync your files using a sevice like Dropbox or OwnCloud, an easy solution is to just have the tree synced by it. Since metapackages have no real content, they're very small.

All you need is to create a repository entry

$ echo "deb file:///home/ack/repo/ubuntu/ unstable main" | sudo tee /etc/apt/sources.list.d/local.list
deb file:///home/ack/repo/ubuntu/ unstable main

with the correct path to the ubuntu/ directory.

At this point, you can just install your package with apt, and it will automatically pull dependencies:

$ sudo apt update
...
$ sudo apt install ack-desktop