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.