Packaging Software with ‘fpm’
Tags: devops, linux, deployment, packaging
Published: 2020-03-14 17:44:58

---
title: Packaging Software with ‘fpm’
published: true
date: 2020-03-14 05:00:00 UTC
tags: devops, linux, deployment, packaging
series: Deploying Python Software to Production
cover_image: https://dev-to-uploads.s3.amazonaws.com/i/mvqycmvtu5a1se23bn8j.png
canonical_url: https://jhermann.github.io/blog/linux/deployment/2020/03/14/fpm_effing_package_managers.html
---
> *fpm (eFfing Package Management) enables you to build all sorts of Linux packages with great ease and sanity.*

## What it does

Basically, ‘fpm’ allows you to deploy any software via OS packages, from an installation tree on disk or from already built artifacts specific to the chosen implementation language.

The main advantage over native tooling is you do not need to know about every minute detail of the involved commands and metafile formats for every platform. In case of Debian, that is at least the “control” and “rules” files, and tools like “buildpackage” and “debhelpers”.


## How to install it

‘fpm’ is written in Ruby and can thus be installed via `gem install`. But you can also run it with JRuby in a JVM, that you might happen to already have on a host anyway. Using JRuny spares you the headache of juggling multiple Ruby versions and isolating gems against other Ruby applications.

If you package it up that way, it also can be installed on any Linux release because you only need a Java8 JRE installed to run it – no native code involved. You can use the `fpm.sh` script in the [priscilla](https://github.com/jhermann/priscilla) project on GitHub to package ‘fpm’ with itself. As written, it works for Debian derivatives, but should be adaptable to other distros with a few changes (remember, ‘fpm’ makes that easy).

If you call `./fpm.sh pkg`, the package contents is created in `build` as a staging area, and when everything is ready, `fpm` is called from within that staging area to create the final package:

```
build/opt_tools_fpm/opt/tools/fpm/bin/fpm \
    -s dir -t deb -n opt-tools-fpm -v 1.11.0 \
    --iteration 1 --category tools \
    --deb-user root --deb-group root \
    -m '"Juergen Hermann" <jh@web.de>' \
    --license 'See contained license, or homepage' \
    --vendor github.com/jhermann/priscilla \
    --description 'fpm helps you build packages quickly and easily' \
    --url http://fpm.readthedocs.io/ \
    --workdir $PWD/build/opt_tools_fpm/tmp \
    -a all -d 'openjdk-8-jre|…|java8-runtime-headless' \
    opt usr

```

Yes, this is a mouthful, but still shorter than a `control` or `.spec` file, and easily adapted to other package managers.

The created DEB file `./build/opt_tools_fpm/opt-tools-fpm_1.11.0-1_all.deb` has this metadata:

```
 new debian package, version 2.0.
 size 23763108 bytes: control archive= 30446 bytes.
     462 bytes, 12 lines control              
  119039 bytes, 1118 lines md5sums              
 Package: opt-tools-fpm
 Version: 1.11.0-1
 License: See contained license, or homepage
 Vendor: github.com/jhermann/priscilla
 Architecture: all
 Maintainer: "Juergen Hermann" <jh@web.de>
 Installed-Size: 27046
 Depends: openjdk-8-jre|zulu8|…|java8-runtime-headless
 Section: tools
 Priority: extra
 Homepage: http://fpm.readthedocs.io/
 Description: fpm helps you build packages quickly and easily

```

The major part of files is installed into `/opt/tools/fpm`, but a symlink at `/usr/bin/fpm` makes the command available on the path.

Building fpm 1.11.0 that way was tested on Ubuntu Bionic with openjdk-8-jre-headless 8u242.


## How to use it

There are a lot of source and target types available in fpm (dir, gem, deb, npm, rpm, tar, cpan, pear, empty, puppet, python, osxpkg, solaris, p5p, pkgin, freebsd, apk, snap, pleaserun, zip, virtualenv, pacman, sh), this example converts a Python workdir into a DEB package file.

The [rudiments](https://github.com/jhermann/rudiments) project serves as the example here, but you can use any pure Python project built with setuptools. You have to clone the project and then call fpm like so:

```
( deactivate 2>/dev/null; py=/usr/bin/python3; \
  fpm -s python -t deb --category python \
    --python-bin $py \
    --python-pip "$py -m pip" \
    --python-package-name-prefix "$(basename $py)" \
    --python-obey-requirements-txt \
    --python-install-data "/usr/local/share/$(basename $py)/$($py ./setup.py --name)" \
    -m "\"$($py ./setup.py --author)\" <$($py ./setup.py --author-email)>" \
    --vendor "$($py ./setup.py --url | cut -f3-4 -d/)" \
    --force $PWD/setup.py )
```

The `--force` option overwrites an existing package file, so you can call the command multiple times without an error.

If you inspect the built package with `dpkg-deb -I python3-rudiments_*_all.deb`, this is the output:

```
 new Debian package, version 2.0.
 size 31062 bytes: control archive=1417 bytes.
     366 bytes, 12 lines control              
    3789 bytes, 30 lines md5sums              
 Package: python3-rudiments
 Version: 0.3.1
 License: Apache 2.0
 Vendor: github.com/jhermann
 Architecture: all
 Maintainer: "Jürgen Hermann" <jh@web.de>
 Installed-Size: 79
 Depends: python3-requests (>= 2.6)
 Section: python
 Priority: extra
 Homepage: https://github.com/jhermann/rudiments
 Description: Rudiments – Fundamental elements for any Python project.
```

The package's content is placed into these directories:

```
/usr/local/lib/python3.6
/usr/local/share/python3/rudiments
/usr/share/doc/python3-rudiments
```

You can install it using `dpkg -i …` and then do a quick import test with this command:

```
python3 -c "import rudiments; print(rudiments)"
```


## Where to go from here

`fpm` is available on GitHub.

{% github jordansissel/fpm no-readme %}

Read more about it on the project's [GitHub wiki](https://github.com/jordansissel/fpm/wiki), or watch [this slide deck](http://goo.gl/sWs3Z).

> _Credits:_ Package icon by [Breathe Icon Team: Sebastian Porta, Cory Kontros, Andrew Starr-Bochicchio](https://de.wikipedia.org/wiki/Datei:Breathe-package-x-generic.svg)