Install deb package on Arch based Linux

Install Convert deb to pkg.tar.zst

One could consider the topic title clickbait but the question surfaces from time to time

The short answer is You cannot!

Keep reading for the longer answer. If you reach the bottom of this thread - you also get an idea of how much work there is for a package maintainer to keep up with upstream changes.

Think of the work if the package is compiled from source ....

Perhaps then you will appreciate the work done by the team voluntering their free time to maintain the current packages and you will also know why requests for added packages is often rejected.

There is a lot of work involved .... and if you continue reading you also have some insight into the work of a package maintainer.

Why you cannot install .deb packages

.deb packages are created for Debian and derivatives and there is a major difference in what is provided inside a package or in dependencies.

Therefore you cannot install .deb packages as it is - it must be mogrified to match an Arch based system.

Content of a Arch package

The native package manager is called pacman.

pacman uses a native package format which is a compressed archive providing the complete folder/file structure and some simple instructions instructions used by pacman.

pacman uses these instructions to execute various actions when installing or removing a package and to keep track of files and their locations, thereby knowing precisely which file belongs to what package and where it resides.

This is used to ensure that the system - the distribution part - is kept in a manageable manner.

When pacman - during the initial processing of a package - encounters a file in filesystem - which is not known to exist as recorded by pacman - the package manager will refuse to continue.

The file(s) may be owned by another package or it has been placed there by a user, perhaps as a result of a local make and the subsequent sudo make install and as such they are legit but pacman needs you to resolve the conflict.

libraries and dependencies

Complicated applications often depends on functionality provided in libraries which are then provided by a dependency package.

Debian and Arch does not provide the libraries in packages with the same name and therefore some leg-work must be done in order to correctly identify the Arch counterparts of Debian packages.

That leg-work may not be as easy as you think - but the process of collecting the information gives you an invaluable insight into how simple and straight forward Arch package management is compared to Debian.

Debian may provide installation of multiple versions of the same library due to their philosophy whereas Arch only has one version - the latest.

A PKGBUILD providing content of .deb package

Yes - this is possible.

The google-chrome PKGBUILD in AUR is an example of a .deb package converted to fit the Arch eco-system.

Lookup the PKGBUILD on AUR and take a look inside - it is very educating.

get dirty

The mention and the Debian package used in this example does not mean any endorsement or recommendation of AtlasVPN.

I have seen a couple of attempts to use AtlasVPN on Arch and since it doesn't exist in AUR - let us have some fun. I do not care anything for AtlasVPN as the only use I have for them is to showcase how to make something useful from a .deb package.

The mention and the Debian package used in this example does not mean any endorsement or recommendation of AtlasVPN.

Information gathering

They have a download for they Linux app - but as it turns out - the download is only to install the required keyrings and source.list identifying the servers hosting the final client application.

Downloading will provide you with a file named atlasvpn-repo.deb - a file we need to unpack to extract the info inside the archive.

Create a work folder and save the file in it - e.g. a folder named workdir in your home.

Everything will be terminal - it is the best way of learning your way around a Linux system.

mkdir ~/workdir

Change directory into the folder

cd ~/workdir

I copied the link you see below from their web @2023-03-10 18:16 - just in case they change it.

You can download the file using your web browser - just be sure to either save the file in the work folder or move it there after download.

Or you can use curl or wget to fetch the file using the url

wget https://downloads.atlasvpn.com/apps/linux/atlasvpn-repo.deb

Now we need to extract the content

ar x atlasvpn-repo.deb

Let us see what we got

 $ ls
atlasvpn-repo.deb  control.tar.xz  data.tar.xz  debian-binary

The file we are interested in is data.tar.xz which is yet an archive to unpack

tar -x data.tar.xz

Again let us see what new we have

 $ ls
atlasvpn-repo.deb  control.tar.xz  data.tar.xz  debian-binary  etc

This time we got a folder named etc - let us look inside - this time using the command tree - because we need to know where to go. tree is likely not available - so install it

sudo pacman -Syu tree

You should get a similar output

 $ tree
.
├── atlasvpn-repo.deb
├── control.tar.xz
├── data.tar.xz
├── debian-binary
└── etc
    └── apt
        ├── keyrings
        │ └── atlasvpn.gpg
        └── sources.list.d
            └── atlasvpn.list

We are not interested in the keyring but the content of the atlasvpn.list file

 $ cat etc/apt/sources.list.d/atlasvpn.list
deb [signed-by=/etc/apt/keyrings/atlasvpn.gpg] http://repo.atlasvpn.com/debian stable main

From the content we the url http://repo.atlasvpn.com/debian as the location from which to install the app.

Now that we know this - we can clean the folder

rm -r ~/workdir/*

Open a browser and navigat to http://repo.atlasvpn.com/debian - you can browse around to see if you can figure out what you need.

Let me help - you need to go down the pool road - you eventually end at

http://repo.atlasvpn.com/debian/pool/main/a/atlasvpn/

Click the newest file - at time of writing it is atlasvpn_1.0.3_amd64.deb - save the file in our work folder.

Or you can use wget one more time - do remember - the filename may not be the same in a month from now - or a year ....

wget https://repo.atlasvpn.com/debian/pool/main/a/atlasvpn/atlasvpn_1.0.3_amd64.deb

We learned above that a .deb file can be unpacked using ar and the files we are interested in is inside data.tar.xz - so let us unpack and see what we got

ar x atlasvpn_2.0.3_amd64.deb
tar -xf data.tar.xz
 $ tree
.
├── atlasvpn_1.0.3_amd64.deb
├── control.tar.xz
├── data.tar.xz
├── debian-binary
└── usr
    ├── bin
    │ └── atlasvpn
    ├── lib
    │ └── systemd
    │     └── system
    │         ├── atlasvpnd.service
    │         └── atlasvpnd.socket
    └── sbin
        └── atlasvpnd

That looks simple enough.

Hold your horse - not so fast - perhaps you have noted the structure is familiar - and yes - the data part of deb files are packaged using the same structure as Arch - files are located where they are supposed to go - so one could think - hey - we just sudo cp the whole #! and be done with it - and you can - you can stop right here and be done with it.

But where is the challenge in that?

And do you know what to do with the files?

What if you decide the freebie VPN was a bummer - in couple of month from now - how do you uninstall - can you even remember the files? Can you reverse the steps needed so you do not accidently break your system?

On Debian the archive control.tar.xz contains the package manager instructions and to satisfy our inner student we need to know what instructions could be applicable for this app.

After all - our intention is to create a PKGBUILD so you can update with ease when the next release lands.

So let us unpack that archive as well

tar -xf control.tar.xz
 $ tree
.
├── atlasvpn_1.0.3_amd64.deb
├── control
├── control.tar.xz
├── data.tar.xz
├── debian-binary
├── postinst
├── postrm
├── prerm
└── usr
    ├── bin
    │ └── atlasvpn
    ├── lib
    │ └── systemd
    │     └── system
    │         ├── atlasvpnd.service
    │         └── atlasvpnd.socket
    └── sbin
        └── atlasvpnd

We now have three extra files and their names correspond to the processes

$ cat postinst
#!/usr/bin/env bash

LOG_DIR="/var/log/atlasvpn"
ENV=$(ps --no-headers -o comm 1)
installSuccess="AtlasVPN for Linux successfully installed!"

    case "$1" in
   configure)
       update-desktop-database 2> /dev/null
       mkdir -m 0750 -p "$LOG_DIR"
       case "$ENV" in
           systemd)
               systemctl daemon-reload &>/dev/null || :
               systemctl enable atlasvpnd.socket &>/dev/null || :
               systemctl enable atlasvpnd.service &>/dev/null || :
               systemctl start atlasvpnd.socket &>/dev/null || :
               systemctl start atlasvpnd.service &>/dev/null || :
           ;;
       esac

       if [[ ! "$2" ]]; then
           echo "${installSuccess}"
       fi
   ;;
   abort-upgrade|abort-remove|abort-deconfigure)
   ;;
   *)
       echo "[ERROR] postinst called with unknown argument \`$1'" >&2
       exit 1
   ;;
esac

exit 0   
 $ cat prerm 
#!/usr/bin/env bash

ENV=$(ps --no-headers -o comm 1)

case "$1" in
   remove|upgrade|deconfigure)
       case "$ENV" in
           *sh) # executed in docker
           ;;
           systemd)
               systemctl stop atlasvpnd.service &>/dev/null || :
               systemctl stop atlasvpnd.socket &>/dev/null || :
               systemctl disable atlasvpnd.service &>/dev/null || :
               systemctl disable atlasvpnd.socket &>/dev/null || :
               systemctl daemon-reload &>/dev/null || :
           ;;
           *)
               echo "[ERROR] Unknown environment \`$ENV'"
           ;;
       esac
   ;;
   *)
       echo "prerm called with unknown argument \`$1'" >&2
       exit 1
   ;;
esac

exit 0
$ cat postrm 
#!/usr/bin/env bash

case "$1" in
   remove|purge)
       rm -f /usr/share/applications/atlasvpn.desktop
       update-desktop-database 2> /dev/null
       rm -f /usr/share/zsh/functions/Completion/Unix/_atlasvpn
       rm -f /usr/share/bash-completion/completions/atlasvpn
       rm -f /usr/lib/systemd/system/atlasvpnd.*
       rm -f /usr/lib/systemd/tmpfiles.d/atlasvpn.conf
       rm -f /etc/init.d/atlasvpn
       rm -rf /root/.config/atlasvpn
       rm -rf /usr/share/doc/atlasvpn
       rm -rf /var/lib/atlasvpn
       rm -rf /var/log/atlasvpn
       rm -rf /run/atlasvpn
   ;;
   disappear|upgrade|failed-upgrade|abort-install|abort-upgrade)
   ;;
   *)
       echo "$0: didn't understand being called with \`$1'" 1>&2
       exit 0
   ;;
esac

exit 0
[/details]

The file is used by the Debian package manager - so what really interest us is what is done and when.

On installation the service and socket is enabled and started

systemctl daemon-reload &>/dev/null || :
systemctl enable atlasvpnd.socket &>/dev/null || :
systemctl enable atlasvpnd.service &>/dev/null || :
systemctl start atlasvpnd.socket &>/dev/null || :
systemctl start atlasvpnd.service &>/dev/null || :

Before we remove the app we stop and disable the service and socket

systemctl stop atlasvpnd.service &>/dev/null || :
systemctl stop atlasvpnd.socket &>/dev/null || :
systemctl disable atlasvpnd.service &>/dev/null || :
systemctl disable atlasvpnd.socket &>/dev/null || :
systemctl daemon-reload &>/dev/null || :

The actual removal involve these steps on a Debian system

rm -f /usr/share/applications/atlasvpn.desktop
update-desktop-database 2> /dev/null
rm -f /usr/share/zsh/functions/Completion/Unix/_atlasvpn
rm -f /usr/share/bash-completion/completions/atlasvpn
rm -f /usr/lib/systemd/system/atlasvpnd.*
rm -f /usr/lib/systemd/tmpfiles.d/atlasvpn.conf
rm -f /etc/init.d/atlasvpn
rm -rf /root/.config/atlasvpn
rm -rf /usr/share/doc/atlasvpn
rm -rf /var/lib/atlasvpn
rm -rf /var/log/atlasvpn
rm -rf /run/atlasvpn

the satisfaction of satisfying the pacman

We present :drum: the PKGBUILD - the most overlooked file in the Arch universe - yet responsible for so much in the hands of the very capable :drum: pacman.

In this section will take a closer look following the coverage in https://wiki.archlinux.org/title/Arch_package_guidelines

In the pacman shared folder we find template files (and the default keyrings) - ignore the keyrings please

 $ tree /usr/share/pacman
/usr/share/pacman
├── keyrings
│ ├── archlinux.gpg
│ ├── archlinux-revoked
│ ├── archlinux-trusted
│ ├── Arch.gpg
│ ├── Arch-revoked
│ └── Arch-trusted
├── PKGBUILD.proto
├── PKGBUILD-split.proto
├── PKGBUILD-vcs.proto
└── proto.install

Creating the PKGBUILD

Now create the final folder to hold our build script and copy the prototypes into it

The PKGBUILD is the heart of our package and the install file is defining the actions during the transaction pacman invokes.

mkdir ~/atlasvpn
cd atlasvpn
cp /usr/share/pacman/PKGBUILD.proto ~/atlasvpn/PKGBUILD
cp /usr/share/pacman/proto.install ~/atlasvpn/atlasvpn.install

Open the PKGBUILD file in your favorite editor.

The file consist of two sections, a properties (variables) section and a functions section.

Which properties are used depends on the targeted application so the below list is with reference to our test subject atlasvpn and which functions also depends on what needs to be done.

How to decide which properties to use

  • pkgname is atlasvpn
  • pkgver is the version we downloaded earlier 1.0.3
  • pkgrel is 1 - since it is our first time around - pkgrel is incremented when you have installed the package but then discover an error in the package and need to rebuild it - perhaps a file was misplaced or entirely missing any change in the packaging which is not a pkgver increment.
  • epoch we do not use - remove the line
  • pkgdesc is a short text describing what the package provides and it's usage
    • in this case it could be My port of atlasvpn installer to Arch
  • arch is the architecture for which this package has been created. As the downloaded archive specifically state it is for amd64 we can assume it will not work for arm based device - so we add x86_64 to the array.
  • url is the url containing the source https://atlasvpn.com - it could be your Codeberg repo
  • license is tricky in this case because there is no reference on their web site - so we follow the guidelines and add 'unknown' to the array
  • groups is not used
  • depends is not known. It is an vpn we could assume openvpn - perhaps wireguard-tools if they support wireguard - but as we do not know we will have to test before we can decide. Use something like ldd to detemine which libraries the binary has been linked with.
  • makedepends is not used
  • checkdepends is not used
  • optdepends is not used
  • provides is implicit by the package name - don't use
  • conflicts is not used
  • replaces is not used
  • backup is not used
  • options is not used
  • install this is our install script - which we created as atlasvpn.install
  • changelog this is string describing what changed with this particular build not needed
  • source is an array of sources needed to build this package - in this case it is the url where the upstream installer is located https://repo.atlasvpn.com/debian/pool/main/a/atlasvpn/atlasvpn_1.0.3_amd64.deb
    • here we will use variable expansion as we have seen both pkgname and pkgver is part of the filename
  • noextract is not needed
  • md5sums or sha256sums or sha512sums is a control mechanism to ensure the file downloaded is complete compared to the time of creating the PKGBUILD. It should not be taken as verification the download is legit.
    • The checksum is added to the PKGBUILD by using the updpkgsums command in the folder containing the PKGBUILD.
    • The new default checksum is sha256 and updpkgsums will update to this
  • validpgpkeys is the seal that verifies the downloaded package is legit but for that we need the signature and since there is no signature - we cannot compare - therefore we cannot use this property

A PKGBUILD is sourced by makepkg which is a bash script and therefore it is important to know the difference between using 'string' and "string". The first usage defines a literal string - the second usage allows for embedding properties e.g. "${pkgname}_${pkgver}_amd64.deb" which at runtime is replace with the value in the PKGBUILD's pkgver property.

The populated property list

The properties we do not use we remove and when done the properties section of the file should look like this.

# Maintainer: Your Name <youremail@domain.com>
pkgname=atlasvpn
pkgver=1.0.3
pkgrel=1
pkgdesc="My port of atlasvpn installer to Arch"
arch=('x86_64')
url='https://atlasvpn.com'
license=('unknown')
depends=()
install=$pkgname.install
source=("https://repo.atlasvpn.com/debian/pool/main/a/atlasvpn/"$pkgname"_"$pkgver"_amd64.deb")
sha256sums=()

The packaging instructions

Let us look at the functions section and decide what we need

  • prepare() function - no - remove it
  • build() function - no - remove it
  • check() function - no - remove it
  • package() function - yes - always

Now we need to tell the package function what to do with the content of unpacked archive - remember the tree when we unpacked the archive manually?

└── usr
    ├── bin
    │ └── atlasvpn
    ├── lib
    │ └── systemd
    │     └── system
    │         ├── atlasvpnd.service
    │         └── atlasvpnd.socket
    └── sbin
        └── atlasvpnd

The populated instruction

Modify the PKGBUILD package() function as follows and save the file

package() {
    echo "  -> Extracting the data.tar.xz"
    bsdtar -xf data.tar.xz -C "$pkgdir/"
    mv "$pkgdir"/usr/sbin/atlasvpnd "$pkgdir"/usr/bin
    # ensure correct permission on usr tree
    chmod 755 "$pkgdir"/usr -R
    # remove execute permissions on service files
    chmod 644 "$pkgdir"/usr/lib/systemd/system/*
    # remove unused folder (symlink on Arch)
    rm -r "$pkgdir"/usr/sbin
}

Then populate the checksums - this will download the file if it is not present.

updpkgsums

Building the package

When you build the package the following happens

  1. makepkg downloads the file if it is not present
  2. makepkg creates two work folders pkg and src and unpacks the deb file into the src folder
  3. The package() instructs makepkg to unpack the data.tar.xz and place it in the pkg folder
  4. Ensure correct directory permissions inside the package 755
  5. Remove execute permissions for unit files
  6. As Arch does not use the /usr/sbin folder we follow the guide lines and move the content to the /usr/bin folder.
  7. Then we remove the /usr/sbin from the pkg folder.
  8. makepkg packages the work folder and creates the necessary information that enables pacman to keep track of the installed files

Excute makepkg

Now run the the makepkg script

makepkg

When you dive into this be prepared to run makepkg numerous times - until you get it right.

Take a look at the folder using tree

 $ tree
.
├── atlasvpn-1.0.3-1-x86_64.pkg.tar.zst
├── atlasvpn_1.0.3_amd64.deb
├── atlasvpn.install
├── pkg
│   └── atlasvpn
│       └── usr
│           ├── bin
│           │   ├── atlasvpn
│           │   └── atlasvpnd
│           └── lib
│               └── systemd
│                   └── system
│                       ├── atlasvpnd.service
│                       └── atlasvpnd.socket
├── PKGBUILD
└── src
    ├── atlasvpn_1.0.3_amd64.deb -> /home/$USER/atlasvpn/atlasvpn_1.0.3_amd64.deb
    ├── control.tar.xz
    ├── data.tar.xz
    ├── debian-binary
    └── usr
        ├── bin
        │   └── atlasvpn
        ├── lib
        │   └── systemd
        │       └── system
        │           ├── atlasvpnd.service
        │           └── atlasvpnd.socket
        └── sbin
            └── atlasvpnd

Now you have built a package atlasvpn-1.0.3-1-x86_64.pkg.tar.zst.

Verify content of package file

Check the content of the archive to see the files has been placed correct according to source and guidelines.

pacman -Qlp atlasvpn-1.0.3-1-x86_64.pkg.tar.zst

The install script

We still need to implement the activation of socket and service - let us do that in minute - first we will check our installtion.

Open the file atlasvpn.install next to the PKGBUILD.

The available functions is listed and we need to decide which we need to use.

  • pre_install() - no - remove
  • post_install() - yes - enable and start service and socket
  • pre_upgrade() - yes - it is likely a good idea to stop the service and socket
    • inform user that service will be disconnected
  • post_upgrade() - yes - re-enable service and socket
  • pre_remove() - yes - we don't want dangling symlinks - so we disable the service and socket
  • post_remove() - yes - display message

The populated install script

The final install file could look like this.

Remember the deb's postrm - it removed several files not installed - so they have to be generated by the application when it is in use.

    echo "  -> Starting services ...."
    systemctl enable --now atlasvpnd.service > /dev/null 2>&1
    systemctl enable --now atlasvpnd.socket > /dev/null 2>&1
}

pre_upgrade() {
    echo "  -> If connection is active it will now disconnect ...."
    atlasvpn disconnect > /dev/null 2>&1
    echo "  -> Stopping services ...."
    systemctl stop atlasvpnd.service > /dev/null 2>&1
    systemctl stop atlasvpnd.socket > /dev/null 2>&1
}

post_upgrade() {
    echo "  -> Starting services ...."
    echo "  -> Remember to reconnect to VPN service ...."
    systemctl enable --now atlasvpnd.service > /dev/null 2>&1
    systemctl enable --now atlasvpnd.socket > /dev/null 2>&1
}

pre_remove() {
    echo "  -> Attempt to disconnect vpn service ...."
    atlasvpn disconnect> /dev/null 2>&1
    echo "  -> Disable and remove services ...."
    systemctl disable --now atlasvpnd.service > /dev/null 2>&1
    systemctl disable --now atlasvpnd.socket > /dev/null 2>&1
    # assuming they are creating at runtime
    echo "  -> Removing files"
    rm -f /usr/share/applications/atlasvpn.desktop > /dev/null 2>&1
    rm -f /usr/share/zsh/functions/Completion/Unix/_atlasvpn > /dev/null 2>&1
    rm -f /usr/share/bash-completion/completions/atlasvpn > /dev/null 2>&1
    rm -f /usr/lib/systemd/system/atlasvpnd.* > /dev/null 2>&1
    rm -f /usr/lib/systemd/tmpfiles.d/atlasvpn.conf > /dev/null 2>&1
    rm -f /etc/init.d/atlasvpn > /dev/null 2>&1
    rm -rf /root/.config/atlasvpn > /dev/null 2>&1
    rm -rf /usr/share/doc/atlasvpn > /dev/null 2>&1
    rm -rf /var/lib/atlasvpn > /dev/null 2>&1
    rm -rf /var/log/atlasvpn > /dev/null 2>&1
    rm -rf /run/atlasvpn > /dev/null 2>&1
}

post_remove() {
    echo "   -> atlasvpn has been removed!"
}

Install your package

  • Using makepkg
    makepkg -i
  • Using pacman
    sudo pacman -U atlasvpn-1.0.3-1-x86_64.pkg.tar.zst
  • Using pamac
    pamac install atlasvpn-1.0.3-1-x86_64.pkg.tar.zst
  • Using yay
    yay -U atlasvpn-1.0.3-1-x86_64.pkg.tar.zst

Conclusion

If you hoped the AtlasVPN was usable - you may be disappointed.

You can create an account and login - but you need a paid account to be able to connect to the servers. At least I wasn't able to find anything on how to connect using the advertised free-wheeling servers.

Note: you will need a premium Atlas VPN subscription to connect to the VPN on Linux. To get a premium subscription, go to our pricing page and pick the deal that suits you best! -- https://help.atlasvpn.com/hc/en-us/articles/7301882184082-Installing-Atlas-VPN-on-Linux

More reading

  • https://wiki.archlinux.org/title/PKGBUILD

  • Crossposted at https://forum.manjaro.org/t/root-tip-how-to-install-a-deb-package-on-manjaro/136015

  • Crossposted at https://forum.endeavouros.com/t/how-to-install-a-deb-package-on-endeavouros/38318