├── .gitignore ├── .dockerignore ├── .github ├── CODEOWNERS ├── renovate.json ├── dependabot.yml └── workflows │ ├── documentation.yml │ ├── tests.yml │ └── release.yml ├── .editorconfig ├── LICENSE ├── patches └── fail-exit-code.patch ├── Dockerfile └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | ## IDEs 2 | /.vscode 3 | /.idea 4 | 5 | ## Temp 6 | /temp 7 | /tmp 8 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ## Ignore everything 2 | * 3 | 4 | ## Except the following files and directories 5 | !/patches 6 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # @link 2 | 3 | * @tarampampam 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | insert_final_newline = true 7 | indent_style = space 8 | indent_size = 4 9 | trim_trailing_whitespace = true 10 | 11 | [*.{yml, yaml, json}] 12 | indent_size = 2 13 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>tarampampam/.github//renovate/default", 5 | ":rebaseStalePrs" 6 | ], 7 | "minimumReleaseAge": "1 day", 8 | "schedule": ["every weekend"] 9 | } 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) tarampampam 2 | 3 | Everyone is permitted to copy and distribute verbatim or modified copies of this license 4 | document, and changing it is allowed as long as the name is changed. 5 | 6 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, 7 | DISTRIBUTION AND MODIFICATION 8 | 9 | 0. You just DO WHAT THE FUCK YOU WANT TO. 10 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Docs: 2 | 3 | version: 2 4 | 5 | updates: 6 | - package-ecosystem: github-actions 7 | directory: / 8 | groups: {github-actions: {patterns: ['*']}} 9 | schedule: {interval: monthly} 10 | assignees: [tarampampam] 11 | 12 | - package-ecosystem: docker 13 | directory: / 14 | groups: {docker: {patterns: ['*']}} 15 | schedule: {interval: monthly} 16 | assignees: [tarampampam] 17 | -------------------------------------------------------------------------------- /patches/fail-exit-code.patch: -------------------------------------------------------------------------------- 1 | diff --git a/src/tool_operate.c b/src/tool_operate.c 2 | index a1212c9..16334d3 100644 3 | --- a/src/tool_operate.c 4 | +++ b/src/tool_operate.c 5 | @@ -772,6 +772,12 @@ skip: 6 | free(per->errorbuffer); 7 | curl_slist_free_all(per->hdrcbdata.headlist); 8 | per->hdrcbdata.headlist = NULL; 9 | + 10 | + /* return exit code 1 when flag --fail is used */ 11 | + if (config->failonerror && (CURLE_HTTP_RETURNED_ERROR == result)) { 12 | + result = CURLE_UNSUPPORTED_PROTOCOL; 13 | + } 14 | + 15 | return result; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/documentation.yml: -------------------------------------------------------------------------------- 1 | name: documentation 2 | 3 | on: 4 | push: 5 | branches: [master, main] 6 | paths: [README.md, .github/workflows/documentation.yml] 7 | 8 | jobs: 9 | docker-hub-description: 10 | name: Docker Hub Description 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v5 14 | 15 | - uses: peter-evans/dockerhub-description@v5 # Action page: 16 | with: 17 | username: ${{ secrets.DOCKER_LOGIN }} 18 | password: ${{ secrets.DOCKER_USER_PASSWORD }} 19 | repository: tarampampam/curl 20 | -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | branches: [master, main] 6 | paths-ignore: ['**.md'] 7 | tags-ignore: ['**'] 8 | pull_request: 9 | paths-ignore: ['**.md'] 10 | 11 | concurrency: 12 | group: ${{ github.ref }} 13 | cancel-in-progress: true 14 | 15 | jobs: # Docs: 16 | gitleaks: 17 | name: Gitleaks 18 | runs-on: ubuntu-latest 19 | steps: 20 | - uses: actions/checkout@v5 21 | with: {fetch-depth: 0} 22 | 23 | - uses: gacts/gitleaks@v1 # Action page: 24 | 25 | build-image: 26 | name: Build docker image 27 | runs-on: ubuntu-latest 28 | steps: 29 | - uses: actions/checkout@v5 30 | 31 | - run: docker build -f ./Dockerfile --tag curl:local . 32 | 33 | - run: docker build -f ./Dockerfile --build-arg "BASE_IMAGE=alpine:latest" --tag curl:local-alpine . 34 | 35 | - run: docker save curl:local > ./docker-image.tar 36 | 37 | - uses: actions/upload-artifact@v5 38 | with: 39 | name: docker-image 40 | path: ./docker-image.tar 41 | retention-days: 1 42 | 43 | scan-image: 44 | name: Scan docker image 45 | runs-on: ubuntu-latest 46 | needs: [build-image] 47 | steps: 48 | - uses: actions/download-artifact@v6 49 | with: {name: docker-image, path: .artifact} 50 | 51 | - working-directory: .artifact 52 | run: docker load < docker-image.tar 53 | 54 | - uses: anchore/scan-action@v7 # action page: 55 | with: 56 | image: curl:local 57 | fail-build: true 58 | severity-cutoff: low # negligible, low, medium, high or critical 59 | 60 | try-to-use: 61 | name: Use docker image 62 | runs-on: ubuntu-latest 63 | needs: [build-image] 64 | steps: 65 | - uses: actions/download-artifact@v6 66 | with: 67 | name: docker-image 68 | path: .artifact 69 | 70 | - name: Prepare image to run 71 | working-directory: .artifact 72 | run: docker load < docker-image.tar 73 | 74 | - name: Try to run (github.com) 75 | run: docker run --rm curl:local --fail https://github.com/ 76 | 77 | - name: Try to run (1.1.1.1) 78 | run: docker run --rm curl:local --fail https://1.1.1.1/ 79 | 80 | - name: Try to run (ppa.launchpad.net) 81 | run: docker run --rm curl:local --fail http://ppa.launchpad.net/ 82 | 83 | - name: Should exit with code 1 84 | run: | 85 | docker run --rm curl:local --fail "https://httpbin.org/status/401" || ec=$? 86 | test $ec -eq 1 && echo "all is ok (code = $ec)" || ( echo "Wrong exit code: $ec"; exit 1 ) 87 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | release: # Docs: 5 | types: [published] 6 | 7 | jobs: 8 | docker-image: 9 | name: Build docker image 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v5 13 | - {uses: gacts/github-slug@v1, id: slug} 14 | - uses: docker/setup-qemu-action@v3 15 | - uses: docker/setup-buildx-action@v3 16 | - uses: docker/login-action@v3 17 | with: {username: '${{ secrets.DOCKER_LOGIN }}', password: '${{ secrets.DOCKER_PASSWORD }}'} 18 | - uses: docker/login-action@v3 19 | with: {registry: ghcr.io, username: '${{ github.actor }}', password: '${{ secrets.GITHUB_TOKEN }}'} 20 | - uses: docker/build-push-action@v6 21 | with: 22 | context: . 23 | file: Dockerfile 24 | push: true 25 | platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7 # <-- important 26 | tags: | 27 | tarampampam/curl:latest 28 | tarampampam/curl:${{ steps.slug.outputs.version-major }} 29 | tarampampam/curl:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} 30 | tarampampam/curl:${{ steps.slug.outputs.version }} 31 | ghcr.io/${{ github.actor }}/curl:latest 32 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version-major }} 33 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }} 34 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version }} 35 | 36 | - uses: docker/build-push-action@v6 37 | with: 38 | context: . 39 | file: Dockerfile 40 | push: true 41 | platforms: linux/amd64,linux/386,linux/arm64,linux/arm/v6,linux/arm/v7 42 | build-args: "BASE_IMAGE=alpine:latest" 43 | tags: | 44 | tarampampam/curl:latest-alpine 45 | tarampampam/curl:${{ steps.slug.outputs.version-major }}-alpine 46 | tarampampam/curl:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}-alpine 47 | tarampampam/curl:${{ steps.slug.outputs.version }}-alpine 48 | ghcr.io/${{ github.actor }}/curl:latest-alpine 49 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version-major }}-alpine 50 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version-major }}.${{ steps.slug.outputs.version-minor }}-alpine 51 | ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version }}-alpine 52 | 53 | binary-files: 54 | name: Publish the binary file (${{ matrix.platform }}) 55 | runs-on: ubuntu-latest 56 | needs: [docker-image] 57 | strategy: 58 | matrix: 59 | platform: # the list should be the same as the platforms listed above 60 | - linux/amd64 61 | - linux/386 62 | - linux/arm64 63 | - linux/arm/v6 64 | - linux/arm/v7 65 | steps: 66 | - uses: actions/checkout@v5 67 | - {uses: gacts/github-slug@v1, id: slug} 68 | - uses: docker/login-action@v3 69 | with: {registry: ghcr.io, username: '${{ github.actor }}', password: '${{ secrets.GITHUB_TOKEN }}'} 70 | - run: | 71 | docker pull --platform "${{ matrix.platform }}" ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version }} 72 | docker create --name app --platform "${{ matrix.platform }}" ghcr.io/${{ github.actor }}/curl:${{ steps.slug.outputs.version }} 73 | docker cp app:/bin/curl ./curl 74 | - {uses: gacts/github-slug@v1, id: filename, with: {to-slug: 'curl-${{ matrix.platform }}'}} 75 | - name: Upload the binary file to the release 76 | uses: svenstaro/upload-release-action@v2 77 | with: 78 | repo_token: ${{ secrets.GITHUB_TOKEN }} 79 | file: ./curl 80 | asset_name: ${{ steps.filename.outputs.slug }} 81 | tag: ${{ github.ref }} 82 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1.2 2 | # 3 | # Building script: 4 | # Official curl dockerfile: 5 | 6 | # e.g.: `docker build --rm --build-arg "BASE_IMAGE=alpine:latest" -f ./Dockerfile .` 7 | ARG BASE_IMAGE="scratch" 8 | 9 | FROM alpine:3.22 as builder 10 | 11 | # renovate: source=github-tags name=curl/curl versioning=regex:^(?:curl-)?(?\d+)_(?\d+)_(?\d+)$ extractVersion=^(?:curl-)?(?[\d_]+)$ 12 | ENV CURL_VERSION="8_16_0" 13 | 14 | # install system dependencies 15 | RUN apk add \ 16 | build-base \ 17 | clang \ 18 | openssl-dev \ 19 | nghttp2-dev \ 20 | nghttp2-static \ 21 | openssl-libs-static \ 22 | zlib-static \ 23 | autoconf \ 24 | automake \ 25 | libtool 26 | 27 | WORKDIR /tmp 28 | 29 | # download curl sources 30 | RUN set -x \ 31 | && CURL_VERSION=$(echo $CURL_VERSION | sed s/_/./g) \ 32 | && wget -O curl.tar.gz "https://curl.haxx.se/download/curl-$CURL_VERSION.tar.gz" \ 33 | && tar xzf curl.tar.gz \ 34 | && rm curl.tar.gz \ 35 | && mv ./curl-* ./src 36 | 37 | # change working directory to the directory with curl sources 38 | WORKDIR /tmp/src 39 | 40 | # apply patches to the source code 41 | COPY ./patches ./patches 42 | RUN for f in ./patches/*.patch; do patch -p1 < "$f"; done 43 | 44 | ENV CC="clang" \ 45 | LDFLAGS="-static" \ 46 | PKG_CONFIG="pkg-config --static" 47 | 48 | RUN autoreconf -fi 49 | 50 | #RUN ./configure --help=short && exit 1 # show the help 51 | 52 | RUN ./configure \ 53 | --disable-shared \ 54 | --enable-static \ 55 | \ 56 | --enable-dnsshuffle \ 57 | --enable-werror \ 58 | \ 59 | --disable-cookies \ 60 | --disable-crypto-auth \ 61 | --disable-dict \ 62 | --disable-file \ 63 | --disable-ftp \ 64 | --disable-gopher \ 65 | --disable-imap \ 66 | --disable-ldap \ 67 | --disable-pop3 \ 68 | --disable-proxy \ 69 | --disable-rtmp \ 70 | --disable-rtsp \ 71 | --disable-scp \ 72 | --disable-sftp \ 73 | --disable-smtp \ 74 | --disable-telnet \ 75 | --disable-tftp \ 76 | --disable-versioned-symbols \ 77 | --disable-doh \ 78 | --disable-netrc \ 79 | --disable-mqtt \ 80 | --disable-largefile \ 81 | --without-gssapi \ 82 | --without-libidn2 \ 83 | --without-libpsl \ 84 | --without-librtmp \ 85 | --without-libssh2 \ 86 | --without-nghttp2 \ 87 | --without-ntlm-auth \ 88 | --without-brotli \ 89 | --without-zlib \ 90 | --with-ssl 91 | 92 | # compile the curl 93 | RUN set -x \ 94 | && make -j$(nproc) V=1 LDFLAGS="-static -all-static" \ 95 | && strip ./src/curl 96 | 97 | # exit with error code 1 if the executable is dynamic, not static 98 | RUN ldd ./src/curl && exit 1 || true 99 | 100 | # print out some info about binary file 101 | RUN set -x \ 102 | && ls -lh ./src/curl \ 103 | && file ./src/curl \ 104 | && ./src/curl --version 105 | 106 | WORKDIR /tmp/rootfs 107 | 108 | # prepare the rootfs for scratch 109 | RUN set -x \ 110 | && mkdir -p ./bin ./etc/ssl \ 111 | && mv /tmp/src/src/curl ./bin/curl \ 112 | && echo 'curl:x:10001:10001::/nonexistent:/sbin/nologin' > ./etc/passwd \ 113 | && echo 'curl:x:10001:' > ./etc/group \ 114 | && cp -R /etc/ssl/certs ./etc/ssl/certs 115 | 116 | # just for a test 117 | RUN /tmp/rootfs/bin/curl --fail -o /dev/null https://cloudflare.com/robots.txt 118 | 119 | FROM ${BASE_IMAGE} 120 | 121 | LABEL \ 122 | # Docs: 123 | org.opencontainers.image.title="curl" \ 124 | org.opencontainers.image.description="curl (static binary file) in docker image" \ 125 | org.opencontainers.image.url="https://github.com/tarampampam/curl-docker" \ 126 | org.opencontainers.image.source="https://github.com/tarampampam/curl-docker" \ 127 | org.opencontainers.image.vendor="tarampampam" \ 128 | org.opencontainers.image.licenses="WTFPL" 129 | 130 | # use an unprivileged user 131 | USER curl:curl 132 | 133 | # import from builder 134 | COPY --from=builder /tmp/rootfs / 135 | 136 | ENTRYPOINT ["/bin/curl"] 137 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | 5 | > [!WARNING] 6 | > This repository is no longer maintained. The reason is quite simple - the main purpose of this image was to 7 | > provide a static binary of `curl` for use in `scratch`-based images as a healthcheck, but in my opinion, 8 | > `curl` is too large for this purpose (~6 MB). Instead of this image, I recommend using [microcheck][microcheck], 9 | > a tiny (~75 KB, in **80 times smaller**) HTTP healthcheck utility with some additional features, developed by me. 10 | > 11 | > To avoid breaking existing integrations, the Docker image will remain available on both Docker Hub and GitHub 12 | > Container Registry, but no further updates will be made. 13 | > 14 | > GG;HF! 15 | 16 | [microcheck]:https://github.com/tarampampam/microcheck 17 | 18 | # Docker image with [curl][link_curl] 19 | 20 | [![Build Status][badge_build_status]][link_build_status] 21 | [![Release Status][badge_release_status]][link_build_status] 22 | [![Image size][badge_size_latest]][link_docker_hub] 23 | [![Docker Pulls][badge_docker_pulls]][link_docker_hub] 24 | [![License][badge_license]][link_license] 25 | 26 | ## Why was this image created? 27 | 28 | As you may know, `curl` comprises two components: the library with the same name and a dynamically linked executable 29 | file. When using `curl` in Docker images based on `scratch` (empty file system), we have two options: 30 | 31 | - Include all required libraries for `curl` in the image. 32 | - Compile `curl` as a **static** binary. 33 | 34 | This repository contains a Dockerfile using the second approach (the main idea was found [here](https://github.com/moparisthebest/static-curl)). 35 | 36 | > Important note: Some `curl` features (such as `gopher`, `imap`, `proxy`, and others) have been disabled for binary 37 | > file size reasons. 38 | 39 | Another important change is that when the `--fail` flag is used, the exit code on error is **1** (instead of 22). You 40 | can find more details about the patch [here](patches/fail-exit-code.patch). This change was made for use in Docker 41 | health checks (the possible exit codes for Docker health checks are: 0 for success, indicating the container is 42 | healthy and ready for use, and 1 for unhealthy, indicating the container is not working correctly): 43 | 44 | ```bash 45 | $ docker run --rm tarampampam/curl -s --fail --show-error https://httpbin.org/status/401 46 | curl: (22) The requested URL returned error: 401 47 | 48 | $ echo "Exit code: $?" 49 | Exit code: 1 50 | ``` 51 | 52 | ## Image 53 | 54 | | Registry | Image | 55 | |-----------------------------------------------------|----------------------------| 56 | | [GitHub Container Registry][link_github_containers] | `ghcr.io/tarampampam/curl` | 57 | | [Docker Hub][link_docker_tags] | `tarampampam/curl` | 58 | 59 | > Images, based on the `alpine` image has a postfix `-alpine` in the tag name, e.g.: `tarampampam/curl:8.0.1-alpine`. 60 | 61 | Following platforms for this image are available: 62 | 63 | ```bash 64 | $ docker run --rm mplatform/mquery tarampampam/curl:latest 65 | Image: tarampampam/curl:latest 66 | * Manifest List: Yes (Image type: application/vnd.docker.distribution.manifest.list.v2+json) 67 | * Supported platforms: 68 | - linux/amd64 69 | - linux/386 70 | - linux/arm64 71 | - linux/arm/v6 72 | - linux/arm/v7 73 | ``` 74 | 75 | ## How can I use this? 76 | 77 | For example - as a docker healthcheck (note - we use `scratch` as a base): 78 | 79 | ```Dockerfile 80 | # use empty filesystem 81 | FROM scratch 82 | 83 | # import some executable application 84 | COPY --from=docker.io/containous/whoami:v1.5.0 /whoami /whoami 85 | 86 | # import curl from current repository image 87 | COPY --from=ghcr.io/tarampampam/curl:8.6.0 /bin/curl /bin/curl 88 | 89 | # Docs: 90 | HEALTHCHECK --interval=5s --timeout=2s --retries=2 --start-period=2s CMD [ \ 91 | "curl", "--fail", "http://127.0.0.1:80/" \ 92 | ] 93 | 94 | ENTRYPOINT ["/whoami"] 95 | ``` 96 | 97 | After that you can build this image, run, and watch the state: 98 | 99 | ```bash 100 | $ docker build --tag healthcheck-test:local . 101 | ... 102 | Successfully built 72bf22424af7 103 | Successfully tagged healthcheck-test:local 104 | 105 | $ docker run --rm -d --name healthcheck-test healthcheck-test:local 106 | b3f20332ac19b42dfed03021c0b90b3650b9a7efbaea7c8800d35551e43d35d7 107 | 108 | $ docker ps --filter 'name=healthcheck-test' --format '{{.Status}}' 109 | Up 1 minutes (healthy) 110 | 111 | $ docker kill healthcheck-test 112 | ``` 113 | 114 | ## Releasing 115 | 116 | New versions publishing is very simple - just make required changes in this repository and "publish" new release using 117 | repo releases page. 118 | 119 | Docker images will be build and published automatically. 120 | 121 | > The new release will overwrite the `latest` and `latest-alpine` docker image tags in both registers. 122 | 123 | ## Support 124 | 125 | [![Issues][badge_issues]][link_issues] 126 | [![Issues][badge_pulls]][link_pulls] 127 | 128 | If you find any package errors, please, [make an issue][link_create_issue] in current repository. 129 | 130 | ## License 131 | 132 | WTFPL. Use anywhere for your pleasure. 133 | 134 | [badge_build_status]:https://img.shields.io/github/actions/workflow/status/tarampampam/curl-docker/tests.yml?branch=master&logo=github&label=build 135 | [badge_release_status]:https://img.shields.io/github/actions/workflow/status/tarampampam/curl-docker/release.yml?logo=github&label=release 136 | [badge_issues]:https://img.shields.io/github/issues/tarampampam/curl-docker.svg?style=flat-square&maxAge=180 137 | [badge_pulls]:https://img.shields.io/github/issues-pr/tarampampam/curl-docker.svg?style=flat-square&maxAge=180 138 | [badge_license]:https://img.shields.io/github/license/tarampampam/curl-docker.svg?longCache=true 139 | [badge_size_latest]:https://img.shields.io/docker/image-size/tarampampam/curl/latest?maxAge=30 140 | [badge_docker_pulls]:https://img.shields.io/docker/pulls/tarampampam/curl.svg 141 | [link_issues]:https://github.com/tarampampam/curl-docker/issues 142 | [link_pulls]:https://github.com/tarampampam/curl-docker/pulls 143 | [link_build_status]:https://github.com/tarampampam/curl-docker/actions 144 | [link_create_issue]:https://github.com/tarampampam/curl-docker/issues/new 145 | [link_license]:https://github.com/tarampampam/curl-docker/blob/master/LICENSE 146 | [link_docker_tags]:https://hub.docker.com/r/tarampampam/curl/tags 147 | [link_docker_hub]:https://hub.docker.com/r/tarampampam/curl/ 148 | [link_github_containers]:https://github.com/tarampampam/curl-docker/pkgs/container/curl 149 | [link_curl]:https://curl.se/ 150 | --------------------------------------------------------------------------------