Discussion: https://social.futuretim.io/@futuretim/110548688810770751

If you want to setup a flatpak repository it’s relatively straightforward but getting all the pieces connected can take a bit of trial and error if you aren’t as familiar with the setup.

I’ll walk through the process I used to setup a personal flatpak repository on my internal network. I’ll be using podman and podman-compose to do this but Docker should work. I assume you have either setup and that you know how to use them. That will not be covered here.

The first thing we want to do is create a PGP key that we can use to sign the repository and commits of our applications. This is a whole topic in itself and it would be wise to read up on it and fully understand it before proceeding.

Use gpg --full-generate-key and follow the prompts. I chose the default algorithms (RSA and RSA) and 4096 bits. For name I chose Flatpak. Do not enter a passphrase or flat-manager cannot automate signing.

Once you’ve done that you’ll need to use gpg --export > public_keys.asc to get the public key so you can use that when adding the repository to computers to use.

You will also need to export the private key so that you can put it on the server that will host the flatpak repositories and use it with flat-manager. Use gpg --export-secret-keys > flatpak-private.asc to do that and transfer it to the host for the repositories.

Make a directory on the host that will be shared with the containers and then use gpg to import that private key, like so:

mkdir gpg
gpg --homedir=./gpg --import ./flatpak-private.asc

It’s possible you will run into an error related to the private-keys-v1.d directory being missing. If that’s the case, evidenced by gpg: error building skey array: No such file or directory among other errors then just make the directory:

mkdir -p gpg/private-keys-v1.d
chmod 700 gpg/private-keys-v1.d

I’m not sure why this is the case but since it resolved the issue for me I just continued on without trying to root cause it at the moment.

You should be able to now confirm that the private key was loaded like so:

gpg --homedir=./gpg --list-secret-keys

Now that we’ve done that we need to setup the ostree repository that will hold our flatpak applications.

We’re going to be using [flat-manager][https://github.com/flatpak/flat-manager] to handle loading builds of applications into our repository so we’ll setup our ostree repository and a directory for dynamically generated repositories used by flat-manager and we’ll keep it all in a subdirectory called flat-manager.

mkdir -p flat-manager/build-repo
mkdir flat-manager/stable-repo

Then we’ll setup the ostree repository for the stable-repo:

ostree --repo=flat-manager/stable-repo init --mode=archive-z2

Now we need a config file for flat-manager. One could look like this:

{
    "repos": {
        "stable": {
            "path": "/repos/stable-repo",
            "collection-id": "org.test.Stable",
            "suggested-repo-name": "testrepo",
            "runtime-repo-url": "https://example.com:8888/repo/flathub.flatpakrepo",
            "gpg-key": "67530A28FF5D5226A43D3C9DC86EFAA0A61EC50A",
            "base-url": null,
            "subsets": {
                "all": {
                    "collection-id": "org.test.Stable",
                    "base-url": null
                },
                "free": {
                    "collection-id": "org.test.Stable.test",
                    "base-url": null
                }
            }
        },
    },
    "host": "0.0.0.0",
    "port": 10000,
    "delay-update-secs": 10,
    "database-url": "postgres://postgres:example@db:5432/repo",
    "build-repo-base": "/repos/build-repo",
    "build-gpg-key": null,
    "gpg-homedir": "/gpg",
    "secret": "bm90IHZlcnkgc2VjcmV0"
}

The important things to notice to start off with is the repo stable path. This reflects where it’s mapped inside the container which we’ll setup later.

The gpg-key is the id of the key we created. If you need to get it again you can see it with this command: gpg --homedir=./gpg --list-keys "Flatpak". The output should be similar to this:

pub   rsa3072 2023-04-14 [SC] [expires: 2025-04-13]                                                                                                                                                                                                                                                                        
      67530A28FF5D5226A43D3C9DC86EFAA0A61EC50A                                                                                                                                                                                                                                                                             
uid           [ultimate] Flatpak                                                                                                                                                                                                                                                                                  
sub   rsa3072 2023-04-14 [E]

Taking the last 16 characters and removing the spaces will give you the id we need.

Flat-manager uses a postgresql database so we see that URL there as well and we’ll set that up later with our compose file. We’ll map in our gpg directory tp /gpg and we have a secret that is the base64 encoded value of our actual secret. In this case you can use the following to obtain the value above:

echo -n 'not very secret' | base64

You can then confirm that by going the other way:

echo -n 'bm90IHZlcnkgc2VjcmV0' | base64 --decode

We’ll use this later to generate a token for our flat-manager client to use so that it can add builds, commit and publish them.

Now we need to setup our compose file for all of our containers. We’ll start with the basic compose file with our postgres instance:

version: '3.4'

services:

  db:
    image: postgres
    restart: always
    ports:
      - 5432:5432
    volumes:
      - $PWD/postgres:/var/lib/postgresql/data:Z
    environment:
      POSTGRES_PASSWORD: example

Obviously passwords, secrets and keys throughout should be replace with your own secure ones.

Let’s make our postgres directory:

mkdir postgres

It’s also not necessary to map the database port externally but I have done so, feel free to remove that port mapping.

To that we’ll add an entirely optional service for the simple database administration app Adminer:

  adminer:
    image: adminer
    restart: always
    ports:
      - 8080:8080

Next we’ll add nginx to serve the flatpak repositories themselves to use with the flatpak client so that we can actually use them to install applications:

  nginx:                                                                                                                                                                                                                                                                                                                      
    image: nginx                                                                                                                                                                                                                                                                                                              
    ports:                                                                                                                                                                                                                                                                                                                    
      - 8888:80                                                                                                                                                                                                                                                                                                               
    volumes:                                                                                                                                                                                                                                                                                                                  
      - $PWD/flat-manager/stable-repo:/usr/share/nginx/html/stable-repo:z    

We map our stable-repo ostree repository that we created earlier to where nginx serves content from by default.

The last thing we’ll add to our compose file is flat-manager itself. A satisfactory container image does not exist for our purposes (that I could find and use) so we’ll need to build the image ourselves. I’m not going to cover that here but the Dockerfile I used is in the flat-manager repo here. Build it and tag it as flat-manager.

  flat-manager:
    depends_on:
      db:
        condition: service_healthy
        restart: true
    image: flat-manager                                                                                                                                         
    ports: 
      - 10000:10000                                                                                                                                             
    volumes:
      - $PWD/config.json:/etc/config.json:Z                                                                                                                     
      - $PWD/flat-manager/repo:/repos/repo:z
      - $PWD/flat-manager/beta-repo:/repos/beta-repo:z
      - $PWD/flat-manager/build-repo:/repos/build-repo:Z                                                                                                        
      - $PWD/gpg:/gpg:z
    environment: 
      REPO_CONFIG: /etc/config.json 

Finally, using podman-compose we bring the stack up:

podman-compose up -d && podman-compose logs -f

Immediately flat-manager will fail because the repo database does not exist. This is where having Adminer as part of the stack makes it very convienient. There are certainly many ways to do this bt bring Adminer up (port 8080) and create the repo database or use any method you like to do the same.

Once we’ve done that we restart flat-manager:

podman-compose restart flat-manager

This should result in flat-manager running on port 10000 both in the container and on the host.

In the flat-manager repo we can find a binary gentoken (needs to be built). We need to use this to generate a token that we can use with the flat-manager-client. Once we have that token we can export it like so:

export REPO_TOKEN=token-value-goes-here

Do this on the host you intend to use flat-manager-client from.

Then using flat-manager-client (after installing / resolving dependencies) try to create a build:

./flat-manager-client create http://example.com:10000 stable                                                                                                                                                                                                               

http://example.com:10000/api/v1/build/1

You’ll know you’re successful if you see the build URL for the build you just created returned.

In the next post I’ll talk about setting up the repository to actually use it on another host, adding an actual application to the respository and installing it.