Corporate Repository
Today, I’m trying to solve a problem for my Anti-Microsoft Organization - how to manage installing software and configurations on organizational computers. I’m building my own packages, and relying on the package manager (apt or dnf) to manage my software!
Contents⌗
Video⌗
Package Test⌗
I’m going to create a Git repo which holds all of the configuration packages in my organization. You could create this as individual repos, but I prefer to lean more to the monorepo life, so my organization has one Git repo for all of our packages.
#Create the git repo (or do this your preferred way)
mkdir src
cd src
git init .
In this repo, we need a directory for each package. Some packages are just ‘meta packages’, meaning they only exist to depend on other things. Other packages have config files, or just plain files. My example here is a web app, so I have the html, and the nginx config to serve it, as well as depending on the nginx package. Since I am using the package as a method of config automation, I don’t want dpkg / rpm to manage the config files, so I’ve created them in the app directory and symlinked them to nginx.
Fine, I’ll let you guys see the test page here
Deb - Package⌗
# Tools we need
sudo apt install build-essential debhelper devscripts dpkg-dev gnupg
Here, I need a few files in the debian directory of my package. The big one is control:
Source: my-app
Section: admin
Priority: optional
Maintainer: Apalrd <adventure@apalrd.net>
Standards-Version: 4.7.0
Build-Depends: debhelper-compat (= 13)
Package: my-app
Architecture: all
Depends: nginx
Description: My own app, distributed to my own servers
Next up I need a listing of my files, that file is called install:
etc/nginx/sites-enabled/myapp.conf etc/nginx/sites-enabled/
var/myapp/myapp.conf var/myapp/
var/myapp/www/index.html var/myapp/www/
Also need a basic makefile (rules) (which needs to be chmod +x):
#!/usr/bin/make -f
%:
dh $@
And a changelog:
my-app (1.0-1) stable; urgency=medium
* Initial release.
-- Apalrd <adventure@apalrd.net> Sun, 28 Sep 2026 12:00:00 +0300
Once this is all done, we can build the deb package:
dpkg-buildpackage -us -uc -b
Deb - Repository⌗
To create a basic repository, we need a directory structure. I’m using /var/repo/ as my parent path here, which I will serve over nginx later.
#Packages pool
mkdir -p /var/repo/pool/main/my-app
#Repo itself
mkdir -p /var/repo/dists/stable/main/binary-all
We also need to configure the repo, which is done with a file I have called apt-ftparchive.conf:
APT::FTPArchive::Release {
Origin "My Corporate Repository";
Label "My Corporate Repository";
Suite "stable";
Codename "stable";
Architectures "amd64 all";
Components "main";
Description "My internal Debian repository";
};
Now copy in the files
cp my-app_1.0-1_all.deb /var/repo/pool/main/my-app/
And finally, scan packages to build the repo Release file:
cd /var/repo
#Scan repo
dpkg-scanpackages \
pool \
/dev/null \
> dists/stable/main/binary-all/Packages
#Compress it
gzip -k -f dists/stable/main/binary-all/Packages
#Generate Release
apt-ftparchive \
-c apt-ftparchive.conf \
release dists/stable \
> dists/stable/Release
At this point, you can create a sources.list file on your test system and it should work, although unsigned:
deb [trusted=yes] http://my-repo.test stable main
This isn’t intended to be the most production-ready way to handle repositories, but I just want to demonstrate that it’s not all that hard to create your own repo for your own purposes.
For any real deployment, you will want to sign the repository.
This does NOT need to be done on the webserver itself. It should be done where you generate the repo, which is not necessarily where you serve the repo. You should be careful with this signing key.
Anyway, create a signing key with GPG:
gpg --quick-gen-key "Apalrd Corporate Repo <apt@apalrd.net>" ed25519 sign 2y
#Also export the public key for clients to use
gpg --armor --export "Apalrd Corporate Repo <apt@apalrd.net>" > apalrd.asc
Now, use it to sign the repository’s Release file
gpg --clearsign -o dists/stable/InRelease dists/stable/Release
Clients now can go through the usual Debian process for adding a repository:
curl http://repo.test/apalrd.asc -o - | gpg --dearmor | sudo tee /etc/apt/keyrings/apalrd.asc
echo "deb [signed-by=/etc/apt/keyrings/apalrd.asc] http://repo.test stable main" | sudo tee /etc/apt/sources.list.d/apalrd.list
Rpm - Package⌗
For the RPM option, I’m starting with the same index.html and myapp.conf file, and building the same package. I’m sharing the same Git repo with Debian here.
Anyway, let’s start with our tools:
sudo dnf install rpmdevtools rpm-build createrepo_c gnupg pinentry-tty rpm-sign
Next we need a spec file for our package. Here’s mine:
Name: my-app
Version: 1.0
Release: 1%{?dist}
Summary: My corporate web app
License: None
Requires: nginx
%description
My demonstration package.
%prep
%build
%install
mkdir -p %{buildroot}
cp -a %{_package_srcdir}/etc %{buildroot}/
cp -a %{_package_srcdir}/var %{buildroot}/
%files
/etc/nginx/conf.d/myapp.conf
/var/myapp/myapp.conf
/var/myapp/www/index.html
%changelog
* Mon Sep 28 2026 apalrd <adventure@apalrd.net> - 1.0-1
- Initial package
And finally, we build it:
rpmbuild -ba my-app.spec --define "_package_srcdir $(pwd)"
Unlike dpkg, rpm builds in a global directory, ~/rpmbuild and puts all of your source and compiled RPMs there.
RPM - Repository⌗
Repo is pretty simple here. I’m again going to put it at /var/repo and point nginx to that location.
#Create repo
mkdir -p /var/repo/fedora/44/x86_64/packages
#Copy in my rpm
cp ~/rpmbuild/RPMS/x86_64/my-app-1.0-1.fc44.x86_64.rpm /var/repo/fedora/44/x86_64/packages/
#Build repo from packages we have copied
sudo createrepo_c /var/repo/fedora/44/x86_64/
Now, assuming you serve this directory over http, you can add it as an unsigned repo in Fedora, by creating /etc/yum.repos.d/apalrd.repo:
[myrepo]
name=Apalrds Corporate Repo
baseurl=http://repo.test/fedora/44/x86_64/
enabled=1
gpgcheck=0
After that, we should probably start signing here also. Here’s the process to sign my repo:
gpg --quick-gen-key "Apalrd's Corporate Repo Key 2026 <rpm@apalrd.net>" ed25519 sign 2y
gpg --armor --export "Apalrd's Corporate Repo Key 2026 <rpm@apalrd.net>" > apalrd.asc
Unlike Debian, in RPM we sign each RPM individually, not the repo as a whole. So, we need to go back to the rpm step and sign it during the build, then regenerate the repo from the now-signed rpm:
#Tell RPM to sign with gpg key
echo "%_gpg_name Apalrd's Corporate Repo Key 2026 <rpm@apalrd.net>" > ~/.rpmmacros
echo "%_signature gpg" >> ~/.rpmmacros
#Sign the package from earlier
rpm --addsign ~/rpmbuild/RPMS/x86_64/my-app-1.0-1.fc44.x86_64.rpm
#Re-copy and re-build repo
cp ~/rpmbuild/RPMS/x86_64/my-app-1.0-1.fc44.x86_64.rpm /var/repo/fedora/44/x86_64/packages/
sudo createrepo_c --update /var/repo/fedora/44/x86_64/
Next, we can update the repo on the clients, to point to our signature
[myrepo]
name=Apalrds Corporate Repo
baseurl=http://repo.test/fedora/44/x86_64/
enabled=1
gpgcheck=1
gpgkey=http://repo.test/apalrd.asc
This time, dnf will ask to accept the key the first time you install something from this repo.