205

I have a Dockerfile to build a Docker image that is based on Alpine Linux. Now I need to install a package as part of this Dockerfile.

Currently I have:

RUN apk update && \
    apk upgrade && \
    apk add git

Apparently this is a bad idea, as the result is non-deterministic. Instead, it depends on the point in time at which I build the image, which version of git is getting installed.

What is the correct way of doing this?

I guess that I have to tell updated, upgrade and add which versions to use, but how do I do this?

I have seen that apk supports pinning of repositories, but that is not what I want (at least I think so), because I do not want to pin a repository, but a package.

In other words: If git could be installed via npm, I'd be able to run:

npm install git@1.9.2

(or whatever version I want to have). What is the equivalent to this for Alpine Linux?

Steve Chambers
  • 1,032
  • 2
  • 12
  • 23
Golo Roden
  • 2,359
  • 3
  • 13
  • 11

6 Answers6

187

You can set "sticky" versions like this:

# Both are equal
apk add packagename=1.2.3-suffix
apk add 'packagename<1.2.3-suffix'

That will upgrade packages only until the specified version. You can then safely use …

apk upgrade

to upgrade all packages, while packages with versions will remain with their version. To set a minimum version just use …

apk add "packagename>1.2.3-suffix"

In case you can't find a package, while you can see it in the UI for Alpine packages, update your sources/package database:

apk update

The package repository can be found here:

https://pkgs.alpinelinux.org/packages

Never pin packages from the "edge" branch of the alpine package repo, as these are in test and may be revoked. (At pkgs.alpinelinux.org/packages, click "edge" and change it to the alpine image version you use, and click "search" again.)


Additional info by cowlinator

Pinning a package to an exact version carries the risk that the package will be dropped from the repo, and your Dockerfile will fail to build in the future. The official recommendation can be read here, citation below.

Alternately, you could simply set a minimum package version instead of an exact version.

We don't at the moment have resources to store all built packages indefinitely in our infra. Thus we currently keep only the latest for each stable branch, and has always been like that.

There has been discussion of keep all packages tagged as Alpine in the future. However, this is still "in-progress". The official recommendation is to keep your own mirror / repository with all the specific package and their versions that you may want to use.

— Timo Teräs, @fabled (Alpine)


The complete Alpine Linux organisations repositories can be found on this self hosted GitLab instance.

kaiser
  • 2,060
  • 1
  • 12
  • 14
  • 1
    Are you sure this works? From this thread, it seems that it doesn't actually do anything: https://forum.alpinelinux.org/forum/general-discussion/it-not-possible-install-old-version-packages#comment-1101 – Travis Reeder Nov 16 '17 at 18:23
  • 2
    @TravisR Take a look at the Alpine APK docs [here](https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management#Advanced_APK_Usage). – kaiser Nov 18 '17 at 01:26
  • In my case, it helped to just downgrade from alpine 3.7 to alpine 3.6! – DUzun Dec 18 '17 at 11:39
  • 13
    Semver tilde ranges also work as in `apk add ansible~=2.4.1` meaning `>=2.4.1 <2.5.0`. Check out http://jubianchi.github.io/semver-check to test your own ranges. – Mike D May 13 '18 at 12:17
  • The repo with the list of available packages https://pkgs.alpinelinux.org/packages was the most helpful for me. – Egel Dec 05 '18 at 12:31
  • Beware that pinning a version at a specific version is likely to break, since Alpine has a habit of deleting old package versions: https://medium.com/@stschindler/the-problem-with-docker-and-alpines-package-pinning-18346593e891 – Will Vousden Oct 17 '19 at 12:51
  • 2
    @WillVousden Have you [read this comment here](https://medium.com/@devalias/i-dont-believe-they-delete-the-old-versions-so-much-as-the-newer-version-of-alpine-eg-63d18daae710)? They do not delete packages, but you need to carefully craft your *Docker images*. – kaiser Oct 18 '19 at 21:22
  • @ WillVousden @ kaiser, in particular, never pin packages from the "edge" branch of the alpine package repo, as these are in test and may be revoked. At https://pkgs.alpinelinux.org/packages , click "edge" and change it to the alpine image version you use, and click "search" again. – cowlinator Feb 14 '20 at 21:05
  • @cowlinator That's pretty good advice! Could you please file an [edit] as comments get cleaned up from time to time and aren't read by everyone. Thanks in advance! – kaiser Feb 14 '20 at 21:41
  • Note: turns out that packages on all branches, not just "edge" branch, can be dropped from the repo. Submitted an answer edit request. See https://gitlab.alpinelinux.org/alpine/abuild/-/issues/9996 for details. – cowlinator May 10 '20 at 05:25
  • 1
    @cowlinator While your edit was rejected by some users (it deleted other relevant info), I added it in addition to the other information at the end of the question. Imho it's good to know information. Thanks for your efforts there! – kaiser May 11 '20 at 21:07
  • 1
    Why are people upvoting this? It doesnt work! Alpine always comes with a fixed version of each tool, so installing tool in v 1.2.3 is not possible if the bundled version is 1.2.1. Bizarre! – Sliq Sep 09 '21 at 17:21
  • @MikeD thanks, I found that `apk add samba~=4.14.8-r0` didn't work (with or without the `-r0`) when only `4.14.12-r0` was available - however `apk add samba~=4.14` did work. The former _should_ work AFAICS but either way posting this in case it helps anyone - i.e. you may need to omit the patch portion of the version. – sparrowt Mar 01 '22 at 13:03
30

Currently, there is no way to install arbitrary older versions of a package from official repositories in Alpine Linux. The best thing you can achieve is using repositories of the earlier releases:

# cat /etc/alpine-release
3.3.3

# echo 'http://dl-cdn.alpinelinux.org/alpine/v3.2/main' >> /etc/apk/repositories

# apk update
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz
fetch http://dl-cdn.alpinelinux.org/alpine/v3.2/main/x86_64/APKINDEX.tar.gz

# apk add bash==4.3.33-r0
(1/1) Updating pinning bash (4.3.33-r0)
OK: 13 MiB in 17 packages

# apk add bash==4.3.42-r3
(1/2) Upgrading bash (4.3.33-r0 -> 4.3.42-r3)
Executing bash-4.3.42-r3.post-upgrade
(2/2) Purging ncurses5-libs (5.9-r1)
Executing busybox-1.24.1-r7.trigger
OK: 13 MiB in 16 packages
Vlad Frolov
  • 359
  • 2
  • 9
  • 3
    This shows an example of how to pull from the v3.2 branch of the Alpine package repo while using v3.3 Alpine Linux. Be aware that mixing branches/versions is not officially supported. – cowlinator Feb 14 '20 at 22:10
14

The syntax for pinning alpine packages with apk is apk add packageName=x.y.z.

To set a minimum version, use apk add packageName>x.y.z

Example:

FROM alpine:3.3
RUN apk update && apk upgrade
RUN apk add --no-cache \
  git=2.8.6-r0 \
  bash=4.3.42-r6 \
  python3=3.5.1-r0

However, the official Alpine package repo can drop any package version from any branch. This means that if you pin your package to an exact version in your Dockerfile, your Dockerfile may not work in the future.

The official recommendation for pinning exact package versions is to keep your own mirror / repository with all the specific package and their versions that you may want to use.

Alternately, you could use a minimum package version instead of a exact pinned package version.

cowlinator
  • 734
  • 1
  • 8
  • 16
7

Vlad Frolov already gave the answer. I am writing the docker solution. I was trying to add some package from the v3.8 repository.

  • Browse the old archive http://dl-cdn.alpinelinux.org/alpine/ and get the specific repository version of your software.
  • After getting the repository version, add the version on your docker file
  • Specify exact version of your package from the repository

    RUN echo 'http://dl-cdn.alpinelinux.org/alpine/v3.8/main' >> /etc/apk/repositories
    RUN apk update
    RUN apk --no-cache add ca-certificates=20190108-r0 gettext=0.19.8.1-r2 postfix=3.3.0-r4 rsyslog=8.34.0-r1 libsasl=2.1.26-r15
    
maruf571
  • 171
  • 1
  • 2
3

Because I was using the testing repo. I ended up building my own copy. Steps:

Go to package details. Ex:

https://pkgs.alpinelinux.org/package/edge/testing/armhf/watchman

Click on the commit, click on the APKBUILD file links and "Log" on the menu to get the commit log of the APKBUILD file. Then choose a commit for your APKBUILD file and download it. Ex:

https://git.alpinelinux.org/cgit/aports/tree/testing/watchman/APKBUILD?id=63f5e7d295659a855709901ce22a3e5f40fce455

Install the build tools:

apk -U add alpine-sdk

You need to be not root user so create a packager user with password:

adduser -D packager && addgroup packager abuild
passwd packager

Then build it as packager in the same directory as the APKBUILD file:

su - packager
abuild-keygen -a -i
abuild -r

You might need to figure out errors and install dependencies. In my example, I needed to do this on my existing Docker image as root:

apk add python-dev

After a successful build as packager, install it as root:

apk add /home/packager/packages/<something...>/watchman-4.7.0-r0.apk --allow-untrusted

Not sure how to remove the --allow-untrusted part, but the steps worked for me.

s12chung
  • 131
  • 3
  • You can remove the --allow-untrusted part by copying the public rsa key produced by the command ```abuild-keygen -a -i``` you stated before into the machine were you are going to add the package, into /etc/apk/keys path. apk finds the key used to sign the package of "packager" user and is able to verify it. – Francesco Colista Jan 01 '21 at 10:26
0

Specifying package versions for Alpine Linux doesn't make sense. Why are you rebuilding your docker containers anyway? Why aren't you using a docker image registry? A registry would ensure package versions are deterministic based on the date of the original build. Alpine Linux seems to dictate package versions based on the version of Alpine Linux. For example when I attempt to update the version of Alpine Linux from 3.9 to 3.17 with specified package versions, the Dockerfile doesn't build and I get the error below.

DockerFile

FROM alpine:3.17

RUN apk add --update --no-cache \
    bash=4.4.19-r1 \
    lftp=4.8.4-r1 \
    curl=7.64.0-r5

DockerFile Build Error

  ERROR: unable to select packages:
  bash-5.2.12-r0:
    breaks: world[bash=4.4.19-r1]
  curl-7.86.0-r1:
    breaks: world[curl=7.64.0-r5]
  lftp-4.9.2-r4:
    breaks: world[lftp=4.8.4-r1]

This makes sense when you look at how Alpine Linux packages are organized so there is one package version per Alpine Linux version.

enter image description here