This tutorial covers:

  • Creating a simple Debian package using fpm
  • Uploading it to a private APT repository on RepoForge.io
  • Installing it from RepoForge in a Docker container

Step 1 – Create a Debian package

For this tutorial, we are going to make a simple hello world application. First, let’s create a working directory called hello, and add a subfolder called usr inside it, then another subfolder called bin inside that. Then, we’re going to add a file called hello inside of the bin folder. Your folder structure should look like this:

/hello
  /usr
    /bin
      hello

Note that we have deliberately NOT added an extension to the hello file. Next, let’s open our hello file and add some extremely simple Python code to it:

#!/usr/bin/env python3
print('hello world!')

Next, let’s make the hello file executable, by running this command:

sudo chmod +x hello

And that’s it! Our script is now ready to be packaged. There are a number of different ways to convert this script into a Debian package, but probably the easiest way is to use fpm, which is a tool that you can use to generate all sorts of package formats very easily.

You can install fpm using gem, like this:

gem install fpm

Once installed, you can run the below command from your working directory root to generate a debian package:

fpm -s dir -t deb -n hello --architecture all usr/bin/hello

This should produce a file called hello_1.0_all.deb - this is the Debian package that we will be deploying. Your folder structure should now look like this:

/hello
  /usr
    /bin
      hello
  hello_1.0_all.deb

Before we upload it anywhere, let’s do a quick test to make sure it works as expected. To do this, I’m going to spin up a simple Dockerfile, add the package file to it, and try to install and run it from the file.

First, let’s create the Dockerfile in the same folder as your .deb package:

# we will use the official Ubuntu base image for this
FROM ubuntu:20.04

# we will update our sources and install Python, so our script can be run
RUN apt-get update -y && apt-get install python3 -y

# add our debian file to the docker container
COPY hello_1.0_all.deb ./

# install the script from the file
RUN dpkg -i hello_1.0_all.deb

# tell the docker container to run our command on startup
CMD hello

Let’s build the docker container and run it to make sure everything is working as expected:

# docker build -t hello .
...
 => => naming to docker.io/library/hello                                   0.0s

# docker run hello
hello world!

Everything is looking good — let’s move on to the next stage.

Step 2 – Deploy your package to RepoForge

If you haven’t already, you’ll first need to sign up for a free trial of RepoForge.io. Once you’ve done so, log into your account and click on Debian in the left side navigation bar:

Once you’ve done that, click on the Show me how to publish packages button in the top right hand corner.

This dialog will give you your unique RepoForge Debian repository URL — we’ll use this link to both publish and download our Debian package — make a note of it somewhere as you’ll need this later.

The first step is simply to send a POST request to the above URL with the .deb file that you have generated. There are a number of ways to do this, but here’s a simple example with Python:

import requests

with open("hello_1.0_all.deb", "rb") as f:
    response = requests.post(
        # change the below line to add your own RepoForge.io URL
        "https://api.repoforge.io/debian/your-hash-id/",
        files={"content": f},
        # use __token__ as username and your access token as password
        auth=("__token__", "$REPOFORGE_ACCESS_TOKEN")
    )
    assert response.status_code == 200

You’ll need to create an access token in the RepoForge dashboard under Access Tokens, with Debian write permissions.

Once you’ve run that, refresh the RepoForge.io dashboard in your browser, and you should find that your package now exists!

And that’s it for this stage — our Debian package is now in the cloud, ready to be installed.

Step 3 – Install your package on a target machine

Once again, I’m going to use a Dockerfile with the Ubuntu base image as a target to install my package. Other linux distros may have slightly different steps, but the general process is the same. You will need to:

  1. Download and sign the RepoForge.io public key
  2. Add RepoForge as an apt source
  3. Add your RepoForge credentials to the target machine

The RepoForge.io public key

All Debian apt sources must be signed with a GPG key in order to be considered trusted by Ubuntu. We’ll therefore first need to download the RepoForge.io public key from the below path. You’ll need to change the URL to use your own unique RepoForge URL:

https://api.repoforge.io/debian/my-repoforge-hash/repoforge.public.key

This file will need to be stored in apt’s trusted GPG key folder, which on Ubuntu is /etc/apt/trusted.gpg.d/. In order for Ubuntu to be able to sign requests properly, you’ll also need to install the gpg, gnupg and ca-certificates package.

Let’s start our Dockerfile by installing these dependencies, as well as the wget package, and downloading the public key to our container.

FROM ubuntu:20.04

# install the dependencies we need
RUN apt-get update && apt-get install wget gpg gnupg ca-certificates -y

# download the public key to the right place (UPDATE YOUR REPOFORGE URL)
RUN wget https://api.repoforge.io/debian/my-repoforge-hash/repoforge.public.key -O \
  /etc/apt/trusted.gpg.d/repoforge.public.key

Adding RepoForge as an apt source

On Ubuntu and all other Debian based distributions, the apt software repositories are defined in the /etc/apt/sources.list file or in separate files under the /etc/apt/sources.list.d/ directory. Therefore, the next step of our Dockerfile is to add a new source file for RepoForge in that directory:

RUN echo \
  "deb [signed-by=/etc/apt/trusted.gpg.d/repoforge.public.key] \
  https://api.repoforge.io/debian/my-repoforge-hash/ hello main" \
  > /etc/apt/sources.list.d/repoforge.list

In the above statement, the signed-by key tells Ubuntu where to find the RepoForge public key we downloaded earlier.

Adding your RepoForge credentials

By default, any packages that you upload to RepoForge are private. This means that you need to provide your RepoForge credentials to apt so that it has the right permissions to pull the package.

It is possible to make them public by flicking the Private switch in the RepoForge dashboard. If you do that, then you won’t need to complete this step at all.

Once again, apt has a special folder for storing things like this. On Ubuntu the folder is at /etc/apt/auth.conf.d/.

In the interest of security, you should never add your access token directly to your Dockerfile. For the sake of this tutorial we are using a Docker build arg instead. However even this isn’t ideal – you should probably use Docker secrets.

With all that in mind, lets add the next bit to our Dockerfile:

ARG REPOFORGE_ACCESS_TOKEN

RUN echo "machine https://api.repoforge.io\n \
    login __token__\n \
    password ${REPOFORGE_ACCESS_TOKEN}" > /etc/apt/auth.conf.d/repoforge

Finally, we should be able to wrap it up by adding the apt-get update and apt-get install commands to our Dockerfile:

RUN apt-get update && apt-get install hello -y

Putting it all together

Our completed Dockerfile should now look something like this:

FROM ubuntu:20.04

# don't do the below in production - use build secrets instead!
ARG REPOFORGE_ACCESS_TOKEN

# update system and install dependencies required for signing the public key
RUN apt-get update
RUN apt-get install wget gpg gnupg ca-certificates -y

# Download the RepoForge.io public key and save it in /etc/apt/trusted.gpg.d/
RUN wget https://api.repoforge.io/debian/your-hash-id/repoforge.public.key -O \
  /etc/apt/trusted.gpg.d/repoforge.public.key

# Add RepoForge as an apt source
RUN echo \
  "deb [signed-by=/etc/apt/trusted.gpg.d/repoforge.public.key] \
  https://api.repoforge.io/debian/your-hash-id/ hello main" \
  > /etc/apt/sources.list.d/repoforge.list

# store RepoForge.io access token on the target machine
RUN echo "machine https://api.repoforge.io\n \
    login __token__\n \
    password ${REPOFORGE_ACCESS_TOKEN}" > /etc/apt/auth.conf.d/repoforge

# update apt again then install your package
RUN apt-get update
RUN apt install hello -y
CMD hello

Let’s build the image, remembering to pass in our RepoForge access token:

docker build -t hello --build-arg REPOFORGE_ACCESS_TOKEN=your-access-token .

And finally, we should be able to run our Dockerfile:

$ docker run hello
Hello, world!

Your private Debian package is now hosted on RepoForge and can be installed on any machine with the correct credentials configured.