├── .dockerignore ├── Dockerfile ├── .github └── ISSUE_TEMPLATE │ └── config.yml ├── docs ├── build.adoc ├── caveats.adoc ├── usage.adoc └── about.adoc ├── LICENSE ├── tests ├── common.bats └── fetch-latest-releases_spec.lua ├── README.adoc ├── prepare-branch.sh └── fetch-latest-releases.lua /.dockerignore: -------------------------------------------------------------------------------- 1 | .git 2 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine:3.16 2 | RUN apk add --no-cache lua5.3 lua-filesystem lua-lyaml lua-http 3 | COPY fetch-latest-releases.lua /usr/local/bin 4 | VOLUME /out 5 | ENTRYPOINT [ "/usr/local/bin/fetch-latest-releases.lua" ] 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: Package-specific issues 4 | url: https://gitlab.alpinelinux.org/alpine/aports/-/issues 5 | about: For a single or multiple packages, and not specific to containers 6 | - name: Community support 7 | url: https://alpinelinux.org/community/ 8 | about: If you need help and or have support questions 9 | -------------------------------------------------------------------------------- /docs/build.adoc: -------------------------------------------------------------------------------- 1 | = Build 2 | 3 | Normally, it is sufficient to simply run `docker pull alpine:tag` to get a locally cached image. 4 | However, you can use the scripts present in this repository to build your own local copies. 5 | 6 | == Automatic 7 | You can use `prepare-branch.sh` in order to generate the tarballs and Dockerfiles necessary to create an Alpine image for all of the supported architectures. 8 | Simply decide which release of alpine you wish to be able to build (such as `v3.9` or `edge`) and call the `prepare` function of `prepare-branch.sh`. 9 | For example, to generate edge images, you would run `prepare-branch.sh prepare edge`. 10 | This will create a directory under `/tmp`, download release tarballs for the selected version, generate necessary Dockerfiles, run tests and print the directory. 11 | At that point, you may enter it and use any of the desired Dockerfiles as per usual. 12 | 13 | CAUTION: This process currently relies on Alpine already being available as a specific tagged image. 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Natanael Copa 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /docs/caveats.adoc: -------------------------------------------------------------------------------- 1 | = Caveats 2 | 3 | The musl libc may work differently than what you are used to. 4 | Musl's https://wiki.musl-libc.org/functional-differences-from-glibc.html[functional differences from glibc] document does a good job of outlining everything. 5 | Please do go over it. 6 | This document will outline the most common kinds of issues you are likely to find. 7 | 8 | == Incompatible Binaries 9 | 10 | While there are binaries that will run on musl libc without needing to be recompiled, you will likely encounter binaries and applications that rely on specific glibc functionality that will fail to start up. 11 | An example of this would be Oracle's Java implementation, which relies on specific symbols only found in glibc. 12 | You can often use ldd to determine the exact symbol: 13 | [source] 14 | ---- 15 | # ldd bin/java 16 | /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000) 17 | libpthread.so.0 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000) 18 | libjli.so => bin/../lib/amd64/jli/libjli.so (0x7f542e9a0000) 19 | libdl.so.2 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000) 20 | libc.so.6 => /lib64/ld-linux-x86-64.so.2 (0x7f542ebb5000) 21 | Error relocating bin/../lib/amd64/jli/libjli.so: __rawmemchr: symbol not found 22 | ---- 23 | In this case, upstream would need to remove the dependency on the offending symbol, or offer you the ability to compile the software natively on a musl-based system. 24 | Be sure to check the https://pkgs.alpinelinux.org[Alpine Linux package index] to see if a suitable replacement package already exists. 25 | -------------------------------------------------------------------------------- /docs/usage.adoc: -------------------------------------------------------------------------------- 1 | = Usage 2 | 3 | == Packages 4 | Replacing your current base image with the Docker Alpine Linux image usually requires updating the package names to the corresponding ones in the https://pkgs.alpinelinux.org[Alpine Linux package index]. 5 | We use the `apk` command to manage packages. 6 | It works similarly to `apt` and `yum`. 7 | 8 | For example, installing the `nginx` package would be done by running `apk add --no-cache nginx`. 9 | The `--no-cache` argument prevents `apk` from caching the package index - something that would normally take up precious space, as well as go stale quickly. 10 | 11 | === Example 12 | This is an example of a complete Dockerfile for `nginx`: 13 | [source, dockerfile] 14 | ---- 15 | FROM alpine 16 | 17 | RUN apk --no-cache add nginx 18 | 19 | EXPOSE 80 20 | CMD ["nginx", "-g", "daemon off;"] 21 | ---- 22 | 23 | === Virtual Packages 24 | Another great `apk` feature is the concept of user-defined virtual packages. 25 | Packages added under a virtual package name can later be removed as one group. 26 | An example use-case would be removing all build dependencies at once, as in this example: 27 | [source, dockerfile] 28 | ---- 29 | FROM alpine 30 | 31 | WORKDIR /myapp 32 | COPY . /myapp 33 | 34 | RUN apk add --no-cache python py-pip openssl ca-certificates 35 | RUN apk add --no-cache --virtual build-dependencies python-dev build-base wget \ 36 | && pip install -r requirements.txt \ 37 | && python setup.py install \ 38 | && apk del build-dependencies 39 | 40 | CMD ["myapp", "start"] 41 | ---- 42 | 43 | // COMBAK: update docs link 44 | === Further Details 45 | For further details on `apk` and how it's used, you can look at https://docs.alpinelinux.org/user-handbook/0.1a/Working/apk.html[Alpine's User Documentation] for it. 46 | -------------------------------------------------------------------------------- /tests/common.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | : ${BRANCH:=edge} 4 | VER=${BRANCH#v} 5 | TAG=${VER}-test 6 | 7 | setup() { 8 | docker history alpine:$TAG >/dev/null 2>&1 9 | } 10 | 11 | @test "version is correct $VER" { 12 | case $BRANCH in 13 | edge) skip;; 14 | esac 15 | run docker run --rm alpine:$TAG sh -c '. ./etc/os-release; echo ${VERSION_ID%.*}' 16 | [ $status -eq 0 ] 17 | [ "${lines[0]}" = "$VER" ] 18 | } 19 | 20 | @test "package installs cleanly" { 21 | run docker run --rm alpine:$TAG apk add --no-cache bash 22 | [ $status -eq 0 ] 23 | } 24 | 25 | @test "timezone" { 26 | run docker run --rm alpine:$TAG date +%Z 27 | [ $status -eq 0 ] 28 | [ "$output" = "UTC" ] 29 | } 30 | 31 | @test "repository list is correct" { 32 | run docker run --rm alpine:$TAG cat /etc/apk/repositories 33 | [ $status -eq 0 ] 34 | [ "${lines[0]}" = "http://dl-cdn.alpinelinux.org/alpine/$BRANCH/main" ] \ 35 | || [ "${lines[0]}" = "https://dl-cdn.alpinelinux.org/alpine/$BRANCH/main" ] 36 | [ "${lines[1]}" = "http://dl-cdn.alpinelinux.org/alpine/$BRANCH/community" ] \ 37 | || [ "${lines[1]}" = "https://dl-cdn.alpinelinux.org/alpine/$BRANCH/community" ] 38 | [ "${lines[2]}" = "" ] 39 | } 40 | 41 | @test "cache is empty" { 42 | run docker run --rm alpine:$TAG sh -c "ls -1 /var/cache/apk | wc -l" 43 | [ $status -eq 0 ] 44 | [ "$output" = "0" ] 45 | } 46 | 47 | @test "root password is disabled with default su" { 48 | run docker run --rm --user nobody alpine:$TAG su 49 | [ $status -eq 1 ] 50 | } 51 | 52 | @test "root login is disabled" { 53 | run docker run --rm alpine:$TAG awk -F: '$1=="root"{print $2}' /etc/shadow 54 | [ $status -eq 0 ] 55 | [ "$output" = "*" ] || [ "$output" = "!" ] 56 | } 57 | 58 | @test "/dev/null should be missing" { 59 | container=$(docker create alpine:$TAG) 60 | run sh -c "docker export $container | tar -t dev/null" 61 | [ $status -ne 0 ] 62 | docker rm $container 63 | } 64 | 65 | -------------------------------------------------------------------------------- /docs/about.adoc: -------------------------------------------------------------------------------- 1 | = About 2 | 3 | https://alpinelinux.org[Alpine Linux] is a lightweight Linux distribution based on the https://www.musl-libc.org[musl libc] and https://busybox.net/[BusyBox]. 4 | The base is extremely small, builds as a Docker image in a matter of seconds, and has a full-featured package index. 5 | 6 | == Musl Libc 7 | Musl is consistent and provides high implementation quality regardless of whether the user is a tiny embedded system or a full-fledged server. 8 | Minimal machine-specific code means less chance of breakage on minority architectures and better success with “write once run everywhere” C development. 9 | Designed from the ground up for static linking, musl carefully avoids pulling in large amounts of code or data that the applications will not use. 10 | 11 | Using musl maximizes application deployability. 12 | Binaries statically linked with musl have no external dependencies, even for features like DNS lookups or character set conversions that are implemented with dynamic loading on glibc. 13 | An application truly can be deployed as a single binary file and run on any machine with the appropriate instruction set architecture and Linux kernel or Linux syscall ABI emulation layer. 14 | 15 | == BusyBox 16 | BusyBox combines tiny versions of many common UNIX utilities into a single small executable. 17 | It provides replacements for most of the utilities you usually find in GNU fileutils, shellutils, etc. 18 | The utilities in BusyBox generally have fewer features than their full-featured GNU cousins. 19 | However, the otions that *are* included provide the expected functionality and are tuned to behave like their GNU counterparts. 20 | BusyBox provides a fairly complete environment for any small or embedded system. 21 | 22 | BusyBox has been written with size-optimization and limited resources in mind. 23 | It is also extremely modular so you can easily include or exclude commands (or features) at compile time. 24 | This makes it easy to customize your embedded systems. 25 | To create a working system, just add some device nodes in `/dev`, a few configuration files in `/etc`, and a Linux kernel. 26 | 27 | == Match made in heaven 28 | Pairing musl libc with BusyBox to combine common UNIX utilities into a single small executable, it makes for an excellent Docker image base. 29 | We get extremely small builds (the base image is only 5 MB) that end up cutting time during push and pull. 30 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | = docker-alpine 2 | 3 | :ao: alpinelinux.org 4 | :hubp: _/alpine 5 | :hub: https://hub.docker.com/r/{hubp}/ 6 | 7 | image:https://img.shields.io/docker/stars/{hubp}.svg[link={hub}] 8 | image:https://img.shields.io/docker/pulls/{hubp}.svg[link={hub}] 9 | 10 | The official Docker image for https://{ao}[Alpine Linux]. 11 | The image is only 5MB and has access to a package repository that is much more featureful than other BusyBox based images. 12 | 13 | == Why 14 | Docker images today are big. 15 | Usually much larger than they need to be. 16 | There are a lot of ways to make them smaller, but the Docker populace still jumps to the `ubuntu` base image for most projects. 17 | The size savings over `ubuntu` and other bases are huge: 18 | [source] 19 | ---- 20 | REPOSITORY TAG IMAGE ID CREATED SIZE 21 | alpine latest 961769676411 4 weeks ago 5.58MB 22 | ubuntu latest 2ca708c1c9cc 2 days ago 64.2MB 23 | debian latest c2c03a296d23 9 days ago 114MB 24 | centos latest 67fa590cfc1c 4 weeks ago 202MB 25 | ---- 26 | There are images such as `progrium/busybox` which get us close to a minimal container and package system, but these particular BusyBox builds piggyback on the OpenWRT package index, which is often lacking and not tailored towards generic everyday applications. 27 | Alpine Linux has a much more featureful and up to date https://pkgs.{ao}[Package Index]: 28 | [source] 29 | ---- 30 | $ docker run progrium/busybox opkg-install nodejs 31 | Unknown package 'nodejs'. 32 | Collected errors: 33 | * opkg_install_cmd: Cannot install package nodejs. 34 | 35 | $ docker run alpine apk add --no-cache nodejs 36 | fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/main/x86_64/APKINDEX.tar.gz 37 | fetch http://dl-cdn.alpinelinux.org/alpine/v3.9/community/x86_64/APKINDEX.tar.gz 38 | (1/7) Installing ca-certificates (20190108-r0) 39 | (2/7) Installing c-ares (1.15.0-r0) 40 | (3/7) Installing libgcc (8.3.0-r0) 41 | (4/7) Installing http-parser (2.8.1-r0) 42 | (5/7) Installing libstdc++ (8.3.0-r0) 43 | (6/7) Installing libuv (1.23.2-r0) 44 | (7/7) Installing nodejs (10.14.2-r0) 45 | Executing busybox-1.29.3-r10.trigger 46 | Executing ca-certificates-20190108-r0.trigger 47 | OK: 31 MiB in 21 packages 48 | ---- 49 | This makes Alpine Linux a great image base for utilities, as well as production applications. 50 | https://www.{ao}/about/[Read more about Alpine Linux here] and it will become obvious how its mantra fits in right at home with Docker images. 51 | 52 | NOTE: All of the example outputs above were last generated/updated on May 3rd 2019. 53 | 54 | == Usage 55 | Stop doing this: 56 | [source, dockerfile] 57 | ---- 58 | FROM ubuntu:22.04 59 | RUN apt-get update -q \ 60 | && DEBIAN_FRONTEND=noninteractive apt-get install -qy mysql-client \ 61 | && apt-get clean \ 62 | && rm -rf /var/lib/apt 63 | ENTRYPOINT ["mysql"] 64 | ---- 65 | This took 28 seconds to build and yields a 169 MB image. 66 | Start doing this: 67 | [source, dockerfile] 68 | ---- 69 | FROM alpine:3.16 70 | RUN apk add --no-cache mysql-client 71 | ENTRYPOINT ["mysql"] 72 | ---- 73 | Only 4 seconds to build and results in a 41 MB image! 74 | -------------------------------------------------------------------------------- /tests/fetch-latest-releases_spec.lua: -------------------------------------------------------------------------------- 1 | describe("test fetch-latest-releases functions", function() 2 | local f 3 | 4 | local function fetch(url) 5 | data = {} 6 | data["/v3.13/releases/aarch64/latest-releases.yaml"] = [[--- 7 | - 8 | title: "Mini root filesystem" 9 | branch: v3.13 10 | arch: aarch64 11 | version: 3.13.4 12 | flavor: alpine-minirootfs 13 | file: alpine-minirootfs-3.13.4-aarch64.tar.gz 14 | iso: alpine-minirootfs-3.13.4-aarch64.tar.gz 15 | date: 2021-04-14 16 | time: 10:28:29 17 | size: 2620916 18 | sha256: 5b359d72aa693d38945c0eb22a0f7a8071071af1914d04f59267230c9a2fe2b5 19 | sha512: 32d8832b81848566a5aecfbb21ea507e439bca123b611e586d27b02cadb94e5169e82e56f76c12c32d037aa4c0e8bec9bc4d98e5dd321d94876807d09e4c7f9c 20 | ]] 21 | data["/v3.13/releases/x86/latest-releases.yaml"] = [[--- 22 | - 23 | title: "Mini root filesystem" 24 | branch: v3.13 25 | arch: x86 26 | version: 3.13.5 27 | flavor: alpine-minirootfs 28 | file: alpine-minirootfs-3.13.5-x86.tar.gz 29 | iso: alpine-minirootfs-3.13.5-x86.tar.gz 30 | date: 2021-04-14 31 | time: 10:25:51 32 | size: 2742455 33 | sha256: 7144c4b209ba7cf2e7c29eefefd1194a1150b5f34b6854104cf4a3d20b7e3053 34 | sha512: 843077403fc3ea031c8c4e2907d707b235890cc6ba7ea6489894ba5559cff04d988b3bad0cdbddfc4ebd5a54fed42a582823ea3f8b4bc2d22cf49c97aca9f70f 35 | - 36 | title: "Virtual" 37 | branch: v3.13 38 | arch: x86 39 | version: 3.13.5 40 | flavor: alpine-virt 41 | file: alpine-virt-3.13.5-x86.iso 42 | iso: alpine-virt-3.13.5-x86.iso 43 | date: 2021-04-14 44 | time: 10:30:25 45 | size: 38797312 46 | sha256: cf7ca3ae1459a2b8e973decd74ae939e70d09e14fdeb8edc6c064d7d27e4ea83 47 | sha512: 034954ae76d920067f56fc9275512d9b611887f9806a90040cd85ba6346a4503a9afa16ef8f6a2f950b637371be06ca5a280a6f7632f9d2ba658ea69746afd18 48 | ]] 49 | if data[url] then 50 | return "200", data[url] 51 | end 52 | return "404", "" 53 | end 54 | 55 | setup(function() 56 | f = require("fetch-latest-releases") 57 | f.fetch = fetch 58 | f.mirror = "" 59 | end) 60 | 61 | teardown(function() 62 | f = nil 63 | end) 64 | 65 | describe("test fatal", function() 66 | it("should call os.exit", function() 67 | stub(os, "exit") 68 | f.fatal("hello") 69 | assert.stub(os.exit).was_called_with(1) 70 | end) 71 | end) 72 | 73 | describe("test minirootfs_image", function() 74 | it("should return flavor=alpine-minirootfs", function() 75 | local img = f.minirootfs_image({ { flavor="alpine-rpi"}, { flavor="alpine-minirootfs"}}) 76 | assert.are.same({flavor="alpine-minirootfs"}, img) 77 | end) 78 | end) 79 | 80 | describe("test get_minirootfs", function() 81 | it("should call fetch_file", function() 82 | res = f.get_minirootfs({ 83 | { flavor="alpine-rpi", version="3.13.5"}, 84 | { flavor="alpine-minirootfs", version="3.13.5"}, 85 | }) 86 | assert.are.same("3.13.5", res.version) 87 | end) 88 | end) 89 | 90 | describe("test get_releases", function() 91 | it("should return 3.13.4 for aarch64", function() 92 | rels = f.get_releases("v3.13") 93 | assert.are.same("3.13.4", rels.aarch64.version) 94 | end) 95 | it("should return 3.13.5 for x86", function() 96 | rels = f.get_releases("v3.13") 97 | assert.are.same("3.13.5", rels.x86.version) 98 | end) 99 | end) 100 | 101 | describe("test equal_versions", function() 102 | it("should return false", function() 103 | ret = f.equal_versions(f.get_releases("v3.13")) 104 | assert.is_falsy(ret) 105 | end) 106 | end) 107 | end) 108 | -------------------------------------------------------------------------------- /prepare-branch.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | 3 | set -e 4 | 5 | run_tests() { 6 | local branch="$1" 7 | local dir="$2" 8 | local arch=$(uname -m) 9 | local testimage="alpine:${branch#v}-test" 10 | docker build -t "$testimage" "$dir/$arch/" 11 | BRANCH="$branch" bats ./tests/common.bats 12 | docker rmi "$testimage" 13 | } 14 | 15 | prepare() { 16 | local branch="$1" 17 | local dir=$(mktemp -d /tmp/docker-brew-alpine-XXXXXX) 18 | docker build -t docker-brew-alpine-fetch . 19 | docker run \ 20 | ${MIRROR+ -e "MIRROR=$MIRROR"} \ 21 | --user $(id -u) --rm \ 22 | -v $dir:/out \ 23 | docker-brew-alpine-fetch $branch /out 24 | echo "=> Verifying checksums" 25 | ( cd $dir && sha512sum -c checksums.sha512) 26 | echo "=> temp dir: $dir" 27 | run_tests "$branch" "$dir" 28 | echo "=> To create git branch run:" 29 | echo "" 30 | echo " $0 branch $branch $dir" 31 | echo "" 32 | TMPDIR="$dir" 33 | } 34 | 35 | 36 | branch() { 37 | local branch="$1" 38 | local dir="$2" 39 | local version=$(cat $dir/VERSION) 40 | if [ -z "$dir" ]; then 41 | help 42 | exit 1 43 | fi 44 | echo "=> Creating branch for release $version from $dir" 45 | 46 | if [ -n "$(git status --porcelain)" ]; then 47 | echo "=> git status is not clean. Aborting" 48 | git status --porcelain 49 | exit 1 50 | fi 51 | 52 | git checkout master 53 | git branch -D "$branch" || true 54 | git checkout --orphan "$branch" 55 | git rm --cached -r . 56 | rm -fr ./* .dockerignore .github 57 | mv "$dir"/* . 58 | rmdir "$dir" 59 | git add * 60 | git commit -m "Update Alpine $branch - $version" 61 | 62 | echo "=> Branch created:" 63 | echo "" 64 | echo "=> To upload release do: git push -f origin $branch" 65 | library "$branch" 66 | echo "" 67 | echo "=> After 'git push -f origin $branch', add the above to:" 68 | echo "=> https://github.com/docker-library/official-images/blob/master/library/alpine" 69 | git checkout master 70 | } 71 | 72 | library_arch() { 73 | case "$1" in 74 | armhf) echo "arm32v6";; 75 | armv7) echo "arm32v7";; 76 | aarch64) echo "arm64v8";; 77 | ppc64le) echo "ppc64le";; 78 | riscv64) echo "riscv64";; 79 | s390x) echo "s390x";; 80 | x86) echo "i386";; 81 | x86_64) echo "amd64";; 82 | *) echo "Unknown architecture: $1" >&2; exit 1;; 83 | esac 84 | } 85 | 86 | library() { 87 | local branch="$1" 88 | local gitbranch=$(git rev-parse --abbrev-ref HEAD) 89 | if [ "$gitbranch" != "$branch" ]; then 90 | git checkout --quiet "$branch" 91 | fi 92 | 93 | local arches= dirs= 94 | local version=$(cat VERSION) 95 | 96 | for file in */Dockerfile; do 97 | local a=${file%/Dockerfile} 98 | arches="${arches}${arches:+, }$(library_arch $a)" 99 | dirs="$dirs $a" 100 | done 101 | cat <<-EOF 102 | 103 | Tags: $version, ${branch#v} 104 | Architectures: $arches 105 | GitFetch: refs/heads/$branch 106 | GitCommit: $(git rev-parse HEAD) 107 | EOF 108 | for dir in $dirs; do 109 | echo "$(library_arch $dir)-Directory: $dir/" 110 | done 111 | 112 | if [ "$gitbranch" != "$branch" ]; then 113 | git checkout --quiet "$gitbranch" 114 | fi 115 | } 116 | 117 | help() { 118 | cat <