Packaging and Automation of Docker Linux Apps
Step 1: Organize Your Repository Structure
mydockerapp/
├── DEBIAN/ # Debian packaging files
│ ├── control # Package metadata and dependencies
│ ├── postinst # Post-install script pulling docker image, enabling service
│ ├── prerm # Pre-removal script stopping service & container
│ └── postrm # Optional cleanup script
├── rpm/ # RPM packaging files (Fedora, Amazon Linux, CentOS)
│ └── mydockerapp.spec # Spec file for RPM packaging
├── pacman/ # Arch packaging files
│ └── PKGBUILD # Arch package build script
├── usr/local/bin/
│ └── mydockerapp # Executable script to manage Docker container lifecycle
├── etc/systemd/system/
│ └── mydockerapp.service # systemd service unit managing the Docker container lifecycle
├── opt/mydockerapp/
│ ├── README.md # Documentation
│ └── docker-compose.yml # Optional docker-compose file
├── .github/workflows/
│ ├── build-and-push-image.yml # GitHub Actions (Docker image)
│ └── build-packages.yml # GitHub Actions (Linux packages)
├── .gitlab-ci.yml # Optional GitLab CI/CD config
├── LICENSE
└── README.md
Step 2: Docker Management Script (Executable)
usr/local/bin/mydockerapp
#!/bin/bash
case "$1" in
start)
docker run -d --name mydockerapp yourdockerhubusername/yourproject:latest
;;
stop)
docker stop mydockerapp || true
docker rm mydockerapp || true
;;
restart)
$0 stop
$0 start
;;
logs)
docker logs -f mydockerapp
;;
*)
echo "Usage: $0 {start|stop|restart|logs}"
exit 1
;;
esac
Step 3: systemd Service Unit
etc/systemd/system/mydockerapp.service
[Unit]
Description=MyDockerApp Docker Container Service
After=docker.service
Requires=docker.service
[Service]
ExecStart=/usr/local/bin/mydockerapp start
ExecStop=/usr/local/bin/mydockerapp stop
Restart=always
[Install]
WantedBy=multi-user.target
Step 4: Debian Packaging Files
DEBIAN/control
Package: mydockerapp
Version: 1.0-1
Architecture: amd64
Maintainer: Your Name <[email protected]>
Depends: docker-ce
Description: MyDockerApp Docker container as systemd service
DEBIAN/postinst
#!/bin/bash
set -e
docker pull yourdockerhubusername/yourproject:latest
systemctl enable mydockerapp.service
systemctl start mydockerapp.service
DEBIAN/prerm
#!/bin/bash
systemctl stop mydockerapp.service || true
docker stop mydockerapp || true
docker rm mydockerapp || true
Optional
DEBIAN/postrm
#!/bin/bash
docker rmi yourdockerhubusername/yourproject:latest || true
Make executable: chmod +x DEBIAN/*
Step 5: RPM Packaging Files
rpm/mydockerapp.spec
Name: mydockerapp
Version: 1.0
Release: 1%{?dist}
Summary: MyDockerApp Docker container service
License: MIT
URL: https://github.com/yourusername/mydockerapp
Requires: docker-ce
%description
MyDockerApp packaged as a Docker container and managed with systemd.
%prep
%build
%install
mkdir -p %{buildroot}/usr/local/bin
cp ../usr/local/bin/mydockerapp %{buildroot}/usr/local/bin/
cp ../etc/systemd/system/mydockerapp.service %{buildroot}/etc/systemd/system/
%post
docker pull yourdockerhubusername/yourproject:latest
systemctl enable mydockerapp.service
systemctl start mydockerapp.service
%preun
systemctl stop mydockerapp.service || true
docker stop mydockerapp || true
docker rm mydockerapp || true
%files
/usr/local/bin/mydockerapp
/etc/systemd/system/mydockerapp.service
%changelog
* $(date +"%a %b %d %Y") Your Name <[email protected]> - 1.0-1
- Initial package
Step 6: Arch Linux PKGBUILD Example
pacman/PKGBUILD
pkgname=mydockerapp
pkgver=1.0
pkgrel=1
pkgdesc="MyDockerApp packaged as Docker container service"
arch=('x86_64')
url="https://github.com/yourusername/mydockerapp"
license=('MIT')
depends=('docker')
source=()
md5sums=()
package() {
install -Dm755 ../usr/local/bin/mydockerapp "$pkgdir/usr/local/bin/mydockerapp"
install -Dm644 ../etc/systemd/system/mydockerapp.service "$pkgdir/etc/systemd/system/mydockerapp.service"
}
Build with:
makepkg -si
Step 7. Package Repository Setup and GPG Key Management Guide
Prerequisite: Install GPG on Client Machines
Before clients can import and verify your repository’s GPG key, they must have GPG installed:
Debian/Ubuntu clients:
sudo apt update sudo apt install gnupg -y
RHEL/CentOS clients:
sudo yum install gnupg2 -y
SUSE/OpenSUSE clients:
sudo zypper install gpg2 -y
Install Web Server Software on the public-facing linux server
Install Nginx or Apache2 to serve your package repositories:
# Debian/Ubuntu:
sudo apt update
sudo apt install nginx apache2 -y
# RHEL/CentOS/AlmaLinux/Rocky:
sudo yum install nginx httpd -y
# SUSE/OpenSUSE:
sudo zypper install nginx apache2 -y
Open Firewall Ports for HTTP/HTTPS
# Debian/Ubuntu (UFW):
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
# RHEL/CentOS/SUSE (firewalld):
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --permanent --add-service=https
sudo firewall-cmd --reload
Create Repository Directory and Set Permissions
Based on your web server:
Apache default:
/var/www/html/repo/
Nginx default:
/usr/share/nginx/html/repo/
Create directory and set ownership:
sudo mkdir -p /var/www/html/repo
# Debian/Ubuntu Apache/Nginx
sudo chown -R www-data:www-data /var/www/html/repo
# RHEL/CentOS Apache (httpd)
sudo chown -R apache:apache /var/www/html/repo
Install Repository Management Tools
# Debian/Ubuntu (APT repositories)
sudo apt install reprepro -y
# RHEL/CentOS (YUM/DNF repositories)
sudo yum install createrepo -y
# SUSE/OpenSUSE (Zypper repositories)
sudo zypper install createrepo -y
Generate GPG Key on the Server
If you haven’t created a GPG key yet, generate one now:
# Generate new key (recommended RSA 4096 bits)
gpg --full-generate-key
List keys and get your key ID:
gpg --list-keys
Export and Publish Your GPG Public Key for Clients
Export your public key as ASCII armor file for client download:
gpg --armor --export "Your Name or Email" > /var/www/html/repo/public-key.asc
sudo chmod 644 /var/www/html/repo/public-key.asc
Also create a de-armored binary version for server use, if needed:
gpg --dearmor /var/www/html/repo/public-key.asc
sudo mv public-key.gpg /usr/share/keyrings/yourrepo.gpg
sudo chmod 644 /usr/share/keyrings/yourrepo.gpg
Configure Repository Manager to Use GPG for Signing Packages
Create conf/distributions
file in your APT repository directory:
Origin: YourRepoName
Label: YourRepoLabel
Codename: your-distro-codename
Architectures: amd64
Components: main
Description: Your repository description
SignWith: your-gpg-key-id
Add and sign packages:
reprepro -b /var/www/html/repo includedeb your-distro-codename /path/to/package.deb
For RPM repositories:
rpm --addsign package.rpm
createrepo /path/to/repo/
Configure the Web Server to Serve Your Repository
Nginx configuration at /etc/nginx/conf.d/repo.conf
:
server {
listen 80;
server_name your.repo.domain;
root /var/www/html/repo;
autoindex on;
location ~ /(.*)/conf { deny all; }
location ~ /(.*)/db { deny all; }
location ~ /(.*)/incoming { deny all; }
}
Test and reload Nginx:
sudo nginx -t
sudo systemctl reload nginx
Apache configuration at /etc/apache2/sites-available/repo.conf
:
<VirtualHost *:80>
ServerName your.repo.domain
DocumentRoot /var/www/html/repo
<Directory /var/www/html/repo>
Options Indexes FollowSymLinks
AllowOverride None
Require all granted
</Directory>
<Directory /var/www/html/repo/*/db> Require all denied </Directory>
<Directory /var/www/html/repo/*/conf> Require all denied </Directory>
<Directory /var/www/html/repo/*/incoming> Require all denied </Directory>
</VirtualHost>
Enable site and restart Apache:
sudo a2ensite repo
sudo systemctl restart apache2
Client Configuration for Multiple Linux Distributions
Verify GPG Availability (Usually Preinstalled)
Most Linux distributions include GPG by default. Verify GPG is available by running:
gpg --version
If this command errors, manually install GPG with your package manager:
Debian/Ubuntu:
sudo apt update sudo apt install gnupg -y
RHEL/CentOS/Fedora:
sudo yum install gnupg2 -y
Arch Linux:
sudo pacman -Sy gnupg --noconfirm
This step is generally unnecessary on modern systems.
Import the Repository's Public GPG Key
Download the public key from the repository server and import it for package verification.
Debian/Ubuntu:
wget http://your.repo.domain/public-key.asc gpg --dearmor public-key.asc sudo mv public-key.gpg /usr/share/keyrings/public-key.gpg
RHEL/CentOS/Fedora:
sudo rpm --import http://your.repo.domain/public-key.asc
Arch Linux:
curl -O http://your.repo.domain/public-key.asc gpg --import public-key.asc
Configure the Package Manager to Use Your Repository
Debian/Ubuntu (APT)
Create a source list file
/etc/apt/sources.list.d/yourrepo.list
:deb [arch=amd64 signed-by=/usr/share/keyrings/public-key.gpg] http://your.repo.domain/ your-distro-codename main
Update package cache and install packages:
sudo apt update sudo apt install your-package-name
RHEL/CentOS/Fedora (YUM/DNF)
Create a repo file
/etc/yum.repos.d/yourrepo.repo
:[yourrepo] name=Your Repo Name baseurl=http://your.repo.domain/ enabled=1 gpgcheck=1 gpgkey=http://your.repo.domain/public-key.asc
Clean metadata cache and install packages:
sudo yum clean all sudo yum install your-package-name
Or on Fedora with dnf:
sudo dnf clean all sudo dnf install your-package-name
Arch Linux (Pacman)
Add the repository to
/etc/pacman.conf
:[yourrepo] SigLevel = Optional TrustAll Server = http://your.repo.domain/$arch
Update package database and install:
sudo pacman -Sy your-package-name
Verify Package Installation
After configuration, you can verify package installation and trust:
Check package signature verification errors (APT/YUM/DNF/Pacman handle this automatically if configured).
Use standard package manager commands to confirm package is correctly installed.
Step 8: User Installation Commands
APT:
curl -fsSL https://yourrepo.example.com/gpg | sudo gpg --dearmor -o /usr/share/keyrings/yourrepo.gpg
echo "deb [arch=amd64 signed-by=/usr/share/keyrings/yourrepo.gpg] https://yourrepo.example.com stable main" | sudo tee /etc/apt/sources.list.d/yourrepo.list
sudo apt update
sudo apt install mydockerapp
sudo systemctl status mydockerapp
DNF/YUM:
sudo tee /etc/yum.repos.d/yourrepo.repo <<EOF
[yourrepo]
name=Your Repo
baseurl=https://yourrepo.example.com/
enabled=1
gpgcheck=1
gpgkey=https://yourrepo.example.com/gpg
EOF
sudo dnf makecache
sudo dnf install mydockerapp
sudo systemctl start mydockerapp
Pacman:
sudo pacman -Sy mydockerapp
Step 9: Automate with GitHub Actions
.github/workflows/build-and-push-image.yml
name: Build and Publish Docker Image
on:
push:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write
steps:
- uses: actions/checkout@v5
- uses: docker/login-action@v2
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
- uses: docker/build-push-action@v4
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
.github/workflows/build-packages.yml
name: Build and Publish Packages
on:
push:
branches: [main]
jobs:
build-packages:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install packaging tools
run: |
sudo apt update
sudo apt install -y rpm dpkg-dev fakeroot
- name: Build Debian package
run: dpkg-deb --build mydockerapp
- name: Build RPM package
run: rpmbuild -ba rpm/mydockerapp.spec
- name: Upload built packages
uses: actions/upload-artifact@v3
with:
name: mydockerapp-packages
path: |
mydockerapp.deb
~/rpmbuild/RPMS/x86_64/mydockerapp-*.rpm
Step 10: Automate with GitLab CI/CD (Optional)
.gitlab-ci.yml
stages:
- build
- push
- package
- deploy
variables:
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_REF_SLUG
build-image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $IMAGE_TAG .
- docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
push-image:
stage: push
image: docker:latest
services:
- docker:dind
script:
- docker push $IMAGE_TAG
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $CI_REGISTRY_IMAGE:latest
build-packages:
stage: package
image: ubuntu:22.04
script:
- apt-get update && apt-get install -y rpm dpkg-dev fakeroot
- dpkg-deb --build mydockerapp
- rpmbuild -ba rpm/mydockerapp.spec
artifacts:
paths:
- mydockerapp.deb
- ~/rpmbuild/RPMS/x86_64/mydockerapp-*.rpm
deploy:
stage: deploy
script:
- scp mydockerapp.deb [email protected]:/var/www/html/apt-repo/pool/main/
- ssh [email protected] 'reprepro -b /var/www/html/apt-repo includedeb stable mydockerapp.deb'
- scp ~/rpmbuild/RPMS/x86_64/mydockerapp-*.rpm [email protected]:/var/www/html/yumrepo/mydockerapp/
- ssh [email protected] 'createrepo --update /var/www/html/yumrepo/'
Final Tips for Beginners
Use
chmod +x
on all scripts.Use Let’s Encrypt SSL for hosting repos securely.
Keep GPG private keys secure and only distribute the public key.
Test your packages by installing on clean virtual machines.
Use GitHub/GitLab secrets for storing tokens securely.
Provide clear documentation in your repository’s README.
References
Last updated