├── .dockerignore ├── .gitattributes ├── .github └── workflows │ ├── adguard-sync.yml │ ├── adguard.yml │ ├── base.yml │ ├── cmd-socket.yml │ ├── curl.yml │ ├── dnslookup.yml │ ├── docker.yml │ ├── hysteria.yml │ ├── lego.yml │ ├── nginx.yml │ ├── node.yml │ ├── par2.yml │ ├── readme.yml │ ├── socket-proxy.yml │ ├── tini-pm.yml │ ├── traefik.yml │ └── unrar.yml ├── .gitignore ├── .json ├── LICENSE ├── README.md ├── adguard-sync.dockerfile ├── adguard.dockerfile ├── arch.dockerfile ├── build.dockerfile ├── cmd-socket.dockerfile ├── curl.dockerfile ├── dnslookup.dockerfile ├── hysteria.dockerfile ├── lego.dockerfile ├── nginx.dockerfile ├── node.dockerfile ├── par2.dockerfile ├── project.md ├── socket-proxy.dockerfile ├── tini-pm.dockerfile ├── traefik.dockerfile └── unrar.dockerfile /.dockerignore: -------------------------------------------------------------------------------- 1 | # default 2 | .git* 3 | maintain/ 4 | LICENSE 5 | *.md 6 | img/ 7 | node_modules/ -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # default 2 | * text=auto 3 | *.sh eol=lf -------------------------------------------------------------------------------- /.github/workflows/adguard-sync.yml: -------------------------------------------------------------------------------- 1 | name: adguard-sync 2 | on: 3 | push: 4 | tags: 5 | - 'adguard-sync' 6 | jobs: 7 | adguard-sync: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-adguard-sync/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"adguard-sync.dockerfile", 21 | tag:"adguard-sync", 22 | version:dot.semver.version, 23 | }; 24 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 25 | }catch(e){ 26 | core.setFailed(`workflow failed: ${e}`); 27 | } 28 | })(); 29 | 30 | - name: build docker image 31 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 32 | with: 33 | workflow: docker.yml 34 | token: "${{ secrets.REPOSITORY_TOKEN }}" 35 | inputs: '{ "release":"false", "readme":"false", "run-name":"adguard-sync", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/adguard.yml: -------------------------------------------------------------------------------- 1 | name: adguard 2 | on: 3 | push: 4 | tags: 5 | - 'adguard' 6 | jobs: 7 | adguard: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-adguard/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"adguard.dockerfile", 21 | tag:"adguard", 22 | version:dot.semver.version, 23 | }; 24 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 25 | }catch(e){ 26 | core.setFailed(`workflow failed: ${e}`); 27 | } 28 | })(); 29 | 30 | - name: build docker image 31 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 32 | with: 33 | workflow: docker.yml 34 | token: "${{ secrets.REPOSITORY_TOKEN }}" 35 | inputs: '{ "release":"false", "readme":"false", "run-name":"adguard", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/base.yml: -------------------------------------------------------------------------------- 1 | name: tags 2 | on: 3 | push: 4 | tags: 5 | - 'v*' 6 | jobs: 7 | base: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | const etc = { 16 | version:"latest" 17 | }; 18 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 19 | 20 | - name: build docker image 21 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 22 | with: 23 | workflow: docker.yml 24 | token: "${{ secrets.REPOSITORY_TOKEN }}" 25 | inputs: '{ "release":"true", "readme":"true", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/cmd-socket.yml: -------------------------------------------------------------------------------- 1 | name: cmd-socket 2 | on: 3 | push: 4 | tags: 5 | - 'cmd-socket' 6 | jobs: 7 | cmd-socket: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | const etc = { 16 | dockerfile:"cmd-socket.dockerfile", 17 | tag:"cmd-socket", 18 | version:"latest" 19 | }; 20 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 21 | 22 | - name: build docker image 23 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 24 | with: 25 | workflow: docker.yml 26 | token: "${{ secrets.REPOSITORY_TOKEN }}" 27 | inputs: '{ "release":"false", "readme":"false", "run-name":"cmd-socket", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/curl.yml: -------------------------------------------------------------------------------- 1 | name: curl 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'curl' 9 | jobs: 10 | curl: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://api.github.com/repos/curl/curl/releases/latest | jq -r '.tag_name' | sed 's/_/./g' | sed 's/curl-//') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/curl-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"curl.dockerfile", 31 | tag:"curl", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"curl", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/dnslookup.yml: -------------------------------------------------------------------------------- 1 | name: dnslookup 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'dnslookup' 9 | jobs: 10 | dnslookup: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://api.github.com/repos/ameshkov/dnslookup/releases/latest | jq -r '.tag_name' | sed 's/v//') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/dnslookup-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"dnslookup.dockerfile", 31 | tag:"dnslookup", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"dnslookup", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: docker 2 | run-name: ${{ inputs.run-name }} 3 | 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | run-name: 8 | description: 'set run-name for workflow (multiple calls)' 9 | type: string 10 | required: false 11 | default: 'docker' 12 | 13 | runs-on: 14 | description: 'set runs-on for workflow (github or selfhosted)' 15 | type: string 16 | required: false 17 | default: 'ubuntu-22.04' 18 | 19 | build: 20 | description: 'set WORKFLOW_BUILD' 21 | required: false 22 | default: 'true' 23 | 24 | release: 25 | description: 'set WORKFLOW_GITHUB_RELEASE' 26 | required: false 27 | default: 'false' 28 | 29 | readme: 30 | description: 'set WORKFLOW_GITHUB_README' 31 | required: false 32 | default: 'false' 33 | 34 | etc: 35 | description: 'base64 encoded json string' 36 | required: false 37 | 38 | jobs: 39 | docker: 40 | runs-on: ${{ inputs.runs-on }} 41 | timeout-minutes: 1440 42 | 43 | services: 44 | registry: 45 | image: registry:2 46 | ports: 47 | - 5000:5000 48 | 49 | permissions: 50 | actions: read 51 | contents: write 52 | packages: write 53 | 54 | steps: 55 | - name: init / checkout 56 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 57 | with: 58 | ref: ${{ github.ref_name }} 59 | fetch-depth: 0 60 | 61 | - name: init / setup environment 62 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 63 | with: 64 | script: | 65 | const { existsSync, readFileSync } = require('node:fs'); 66 | const { resolve } = require('node:path'); 67 | const { inspect } = require('node:util'); 68 | const { Buffer } = require('node:buffer'); 69 | const inputs = `${{ toJSON(github.event.inputs) }}`; 70 | const opt = {input:{}, dot:{}}; 71 | 72 | try{ 73 | if(inputs.length > 0){ 74 | opt.input = JSON.parse(inputs); 75 | if(opt.input?.etc){ 76 | opt.input.etc = JSON.parse(Buffer.from(opt.input.etc, 'base64').toString('ascii')); 77 | } 78 | } 79 | }catch(e){ 80 | core.warning('could not parse github.event.inputs'); 81 | } 82 | 83 | try{ 84 | const path = resolve('.json'); 85 | if(existsSync(path)){ 86 | try{ 87 | opt.dot = JSON.parse(readFileSync(path).toString()); 88 | }catch(e){ 89 | throw new Error('could not parse .json'); 90 | } 91 | }else{ 92 | throw new Error('.json does not exist'); 93 | } 94 | }catch(e){ 95 | core.setFailed(e); 96 | } 97 | 98 | core.info(inspect(opt, {showHidden:false, depth:null, colors:true})); 99 | 100 | const docker = { 101 | image:{ 102 | name:opt.dot.image, 103 | arch:(opt.dot.arch || 'linux/amd64,linux/arm64'), 104 | prefix:((opt.input?.etc?.semverprefix) ? `${opt.input?.etc?.semverprefix}-` : ''), 105 | suffix:((opt.input?.etc?.semversuffix) ? `-${opt.input?.etc?.semversuffix}` : ''), 106 | description:(opt.dot?.readme?.description || ''), 107 | tags:[], 108 | }, 109 | app:{ 110 | image:opt.dot.image, 111 | name:opt.dot.name, 112 | version:(opt.input?.etc?.version || opt.dot?.semver?.version), 113 | root:opt.dot.root, 114 | UID:(opt.input?.etc?.uid || 1000), 115 | GID:(opt.input?.etc?.gid || 1000), 116 | no_cache:new Date().getTime(), 117 | }, 118 | cache:{ 119 | registry:'localhost:5000/', 120 | }, 121 | tags:[], 122 | }; 123 | 124 | docker.cache.name = `${docker.image.name}:${docker.image.prefix}buildcache${docker.image.suffix}`; 125 | docker.cache.grype = `${docker.cache.registry}${docker.image.name}:${docker.image.prefix}grype${docker.image.suffix}`; 126 | docker.app.prefix = docker.image.prefix; 127 | docker.app.suffix = docker.image.suffix; 128 | 129 | // setup tags 130 | if(!opt.dot?.semver?.disable?.rolling){ 131 | docker.image.tags.push('rolling'); 132 | } 133 | if(opt.input?.etc?.dockerfile !== 'arch.dockerfile' && opt.input?.etc?.tag){ 134 | docker.image.tags.push(`${context.sha.substring(0,7)}`); 135 | docker.image.tags.push(opt.input.etc.tag); 136 | docker.image.tags.push(`${opt.input.etc.tag}-${docker.app.version}`); 137 | docker.cache.name = `${docker.image.name}:buildcache-${opt.input.etc.tag}`; 138 | }else if(docker.app.version !== 'latest'){ 139 | const semver = docker.app.version.split('.'); 140 | docker.image.tags.push(`${context.sha.substring(0,7)}`); 141 | if(Array.isArray(semver)){ 142 | if(semver.length >= 1) docker.image.tags.push(`${semver[0]}`); 143 | if(semver.length >= 2) docker.image.tags.push(`${semver[0]}.${semver[1]}`); 144 | if(semver.length >= 3) docker.image.tags.push(`${semver[0]}.${semver[1]}.${semver[2]}`); 145 | } 146 | if(opt.dot?.semver?.stable && new RegExp(opt.dot?.semver.stable, 'ig').test(docker.image.tags.join(','))) docker.image.tags.push('stable'); 147 | if(opt.dot?.semver?.latest && new RegExp(opt.dot?.semver.latest, 'ig').test(docker.image.tags.join(','))) docker.image.tags.push('latest'); 148 | }else{ 149 | docker.image.tags.push('latest'); 150 | } 151 | 152 | for(const tag of docker.image.tags){ 153 | docker.tags.push(`${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`); 154 | docker.tags.push(`ghcr.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`); 155 | docker.tags.push(`quay.io/${docker.image.name}:${docker.image.prefix}${tag}${docker.image.suffix}`); 156 | } 157 | 158 | // setup build arguments 159 | if(opt.input?.etc?.build?.args){ 160 | for(const arg in opt.input.etc.build.args){ 161 | docker.app[arg] = opt.input.etc.build.args[arg]; 162 | } 163 | } 164 | if(opt.dot?.build?.args){ 165 | for(const arg in opt.dot.build.args){ 166 | docker.app[arg] = opt.dot.build.args[arg]; 167 | } 168 | } 169 | const arguments = []; 170 | for(const argument in docker.app){ 171 | arguments.push(`APP_${argument.toUpperCase()}=${docker.app[argument]}`); 172 | } 173 | 174 | // export to environment 175 | core.exportVariable('DOCKER_CACHE_REGISTRY', docker.cache.registry); 176 | core.exportVariable('DOCKER_CACHE_NAME', docker.cache.name); 177 | core.exportVariable('DOCKER_CACHE_GRYPE', docker.cache.grype); 178 | 179 | core.exportVariable('DOCKER_IMAGE_NAME', docker.image.name); 180 | core.exportVariable('DOCKER_IMAGE_ARCH', docker.image.arch); 181 | core.exportVariable('DOCKER_IMAGE_TAGS', docker.tags.join(',')); 182 | core.exportVariable('DOCKER_IMAGE_DESCRIPTION', docker.image.description); 183 | core.exportVariable('DOCKER_IMAGE_ARGUMENTS', arguments.join("\r\n")); 184 | core.exportVariable('DOCKER_IMAGE_DOCKERFILE', opt.input?.etc?.dockerfile || 'arch.dockerfile'); 185 | 186 | core.exportVariable('WORKFLOW_BUILD', (opt.input?.build === undefined) ? false : opt.input.build); 187 | core.exportVariable('WORKFLOW_CREATE_RELEASE', (opt.input?.release === undefined) ? false : opt.input.release); 188 | core.exportVariable('WORKFLOW_CREATE_README', (opt.input?.readme === undefined) ? false : opt.input.readme); 189 | core.exportVariable('WORKFLOW_GRYPE_FAIL_ON_SEVERITY', (opt.dot?.grype?.fail === undefined) ? true : opt.dot.grype.fail); 190 | core.exportVariable('WORKFLOW_GRYPE_SEVERITY_CUTOFF', (opt.dot?.grype?.severity || 'high')); 191 | if(opt.dot?.readme?.comparison){ 192 | core.exportVariable('WORKFLOW_CREATE_COMPARISON', true); 193 | core.exportVariable('WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE', opt.dot.readme.comparison.image); 194 | core.exportVariable('WORKFLOW_CREATE_COMPARISON_IMAGE', `${docker.image.name}:${docker.app.version}`); 195 | } 196 | 197 | 198 | 199 | # DOCKER 200 | - name: docker / login to hub 201 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 202 | with: 203 | username: 11notes 204 | password: ${{ secrets.DOCKER_TOKEN }} 205 | 206 | - name: github / login to ghcr 207 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 208 | with: 209 | registry: ghcr.io 210 | username: 11notes 211 | password: ${{ secrets.GITHUB_TOKEN }} 212 | 213 | - name: quay / login to quay 214 | uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 215 | with: 216 | registry: quay.io 217 | username: 11notes+github 218 | password: ${{ secrets.QUAY_TOKEN }} 219 | 220 | - name: docker / setup qemu 221 | if: env.WORKFLOW_BUILD == 'true' 222 | uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a 223 | 224 | - name: docker / setup buildx 225 | if: env.WORKFLOW_BUILD == 'true' 226 | uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 227 | with: 228 | driver-opts: network=host 229 | 230 | - name: docker / build & push & tag grype 231 | if: env.WORKFLOW_BUILD == 'true' 232 | id: docker-build 233 | uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d 234 | with: 235 | context: . 236 | file: ${{ env.DOCKER_IMAGE_DOCKERFILE }} 237 | push: true 238 | platforms: ${{ env.DOCKER_IMAGE_ARCH }} 239 | cache-from: type=registry,ref=${{ env.DOCKER_CACHE_NAME }} 240 | cache-to: type=registry,ref=${{ env.DOCKER_CACHE_REGISTRY }}${{ env.DOCKER_CACHE_NAME }},mode=max,compression=zstd,force-compression=true 241 | build-args: | 242 | ${{ env.DOCKER_IMAGE_ARGUMENTS }} 243 | tags: | 244 | ${{ env.DOCKER_CACHE_GRYPE }} 245 | 246 | - name: grype / scan 247 | if: env.WORKFLOW_BUILD == 'true' 248 | id: grype 249 | uses: anchore/scan-action@dc6246fcaf83ae86fcc6010b9824c30d7320729e 250 | with: 251 | image: ${{ env.DOCKER_CACHE_GRYPE }} 252 | fail-build: ${{ env.WORKFLOW_GRYPE_FAIL_ON_SEVERITY }} 253 | severity-cutoff: ${{ env.WORKFLOW_GRYPE_SEVERITY_CUTOFF }} 254 | output-format: 'sarif' 255 | by-cve: true 256 | cache-db: true 257 | 258 | - name: grype / fail 259 | if: env.WORKFLOW_BUILD == 'true' && (failure() || steps.grype.outcome == 'failure') 260 | uses: anchore/scan-action@dc6246fcaf83ae86fcc6010b9824c30d7320729e 261 | with: 262 | image: ${{ env.DOCKER_CACHE_GRYPE }} 263 | fail-build: false 264 | severity-cutoff: ${{ env.WORKFLOW_GRYPE_SEVERITY_CUTOFF }} 265 | output-format: 'table' 266 | by-cve: true 267 | cache-db: true 268 | 269 | - name: docker / build & push 270 | if: env.WORKFLOW_BUILD == 'true' 271 | uses: docker/build-push-action@67a2d409c0a876cbe6b11854e3e25193efe4e62d 272 | with: 273 | context: . 274 | file: ${{ env.DOCKER_IMAGE_DOCKERFILE }} 275 | push: true 276 | sbom: true 277 | provenance: mode=max 278 | platforms: ${{ env.DOCKER_IMAGE_ARCH }} 279 | cache-from: type=registry,ref=${{ env.DOCKER_CACHE_REGISTRY }}${{ env.DOCKER_CACHE_NAME }} 280 | cache-to: type=registry,ref=${{ env.DOCKER_CACHE_NAME }},mode=max,compression=zstd,force-compression=true 281 | build-args: | 282 | ${{ env.DOCKER_IMAGE_ARGUMENTS }} 283 | tags: | 284 | ${{ env.DOCKER_IMAGE_TAGS }} 285 | 286 | 287 | 288 | # RELEASE 289 | - name: github / release / log 290 | continue-on-error: true 291 | id: git-log 292 | run: | 293 | LOCAL_LAST_TAG=$(git describe --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1`) 294 | echo "using last tag: ${LOCAL_LAST_TAG}" 295 | LOCAL_COMMITS=$(git log ${LOCAL_LAST_TAG}..HEAD --oneline) 296 | 297 | EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) 298 | echo "commits<<${EOF}" >> ${GITHUB_OUTPUT} 299 | echo "${LOCAL_COMMITS}" >> ${GITHUB_OUTPUT} 300 | echo "${EOF}" >> ${GITHUB_OUTPUT} 301 | 302 | - name: github / release / markdown 303 | if: env.WORKFLOW_CREATE_RELEASE == 'true' && steps.git-log.outcome == 'success' 304 | id: git-release 305 | uses: 11notes/action-docker-release@v1 306 | # WHY IS THIS ACTION NOT SHA256 PINNED? SECURITY MUCH?!?!?! 307 | # --------------------------------------------------------------------------------- 308 | # the next step "github / release / create" creates a new release based on the code 309 | # in the repo. This code is not modified and can't be modified by this action. 310 | # It does create the markdown for the release, which could be abused, but to what 311 | # extend? Adding a link to a malicious repo? 312 | with: 313 | git_log: ${{ steps.git-log.outputs.commits }} 314 | 315 | - name: github / release / create 316 | if: env.WORKFLOW_CREATE_RELEASE == 'true' && steps.git-release.outcome == 'success' 317 | uses: actions/create-release@4c11c9fe1dcd9636620a16455165783b20fc7ea0 318 | env: 319 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 320 | with: 321 | tag_name: ${{ github.ref }} 322 | release_name: ${{ github.ref }} 323 | body: ${{ steps.git-release.outputs.release }} 324 | draft: false 325 | prerelease: false 326 | 327 | 328 | 329 | 330 | # LICENSE 331 | - name: license / update year 332 | continue-on-error: true 333 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 334 | with: 335 | script: | 336 | const { existsSync, readFileSync, writeFileSync } = require('node:fs'); 337 | const { resolve } = require('node:path'); 338 | const file = 'LICENSE'; 339 | const year = new Date().getFullYear(); 340 | try{ 341 | const path = resolve(file); 342 | if(existsSync(path)){ 343 | let license = readFileSync(file).toString(); 344 | if(!new RegExp(`Copyright \\(c\\) ${year} 11notes`, 'i').test(license)){ 345 | license = license.replace(/Copyright \(c\) \d{4} /i, `Copyright (c) ${new Date().getFullYear()} `); 346 | writeFileSync(path, license); 347 | } 348 | }else{ 349 | throw new Error(`file ${file} does not exist`); 350 | } 351 | }catch(e){ 352 | core.setFailed(e); 353 | } 354 | 355 | 356 | 357 | 358 | # README 359 | - name: github / checkout HEAD 360 | continue-on-error: true 361 | run: | 362 | git checkout HEAD 363 | 364 | - name: docker / setup comparison images 365 | if: env.WORKFLOW_CREATE_COMPARISON == 'true' 366 | continue-on-error: true 367 | run: | 368 | docker image pull ${{ env.WORKFLOW_CREATE_COMPARISON_IMAGE }} 369 | docker image ls --filter "reference=${{ env.WORKFLOW_CREATE_COMPARISON_IMAGE }}" --format json | jq --raw-output '.Size' &> ./comparison.size0.log 370 | 371 | docker image pull ${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }} 372 | docker image ls --filter "reference=${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }}" --format json | jq --raw-output '.Size' &> ./comparison.size1.log 373 | 374 | docker run --entrypoint "/bin/sh" --rm ${{ env.WORKFLOW_CREATE_COMPARISON_FOREIGN_IMAGE }} -c id &> ./comparison.id.log 375 | 376 | - name: github / create README.md 377 | id: github-readme 378 | continue-on-error: true 379 | if: env.WORKFLOW_CREATE_README == 'true' 380 | uses: 11notes/action-docker-readme@v1 381 | # WHY IS THIS ACTION NOT SHA256 PINNED? SECURITY MUCH?!?!?! 382 | # --------------------------------------------------------------------------------- 383 | # the next step "github / commit & push" only adds the README and LICENSE as well as 384 | # compose.yaml to the repository. This does not pose a security risk if this action 385 | # would be compromised. The code of the app can't be changed by this action. Since 386 | # only the files mentioned are commited to the repo. Sure, someone could make a bad 387 | # compose.yaml, but since this serves only as an example I see no harm in that. 388 | with: 389 | sarif_file: ${{ steps.grype.outputs.sarif }} 390 | build_output_metadata: ${{ steps.docker-build.outputs.metadata }} 391 | 392 | - name: docker / push README.md to docker hub 393 | continue-on-error: true 394 | if: steps.github-readme.outcome == 'success' && hashFiles('README_NONGITHUB.md') != '' 395 | uses: christian-korneck/update-container-description-action@d36005551adeaba9698d8d67a296bd16fa91f8e8 396 | env: 397 | DOCKER_USER: 11notes 398 | DOCKER_PASS: ${{ secrets.DOCKER_TOKEN }} 399 | with: 400 | destination_container_repo: ${{ env.DOCKER_IMAGE_NAME }} 401 | provider: dockerhub 402 | short_description: ${{ env.DOCKER_IMAGE_DESCRIPTION }} 403 | readme_file: 'README_NONGITHUB.md' 404 | 405 | - name: github / commit & push 406 | continue-on-error: true 407 | if: steps.github-readme.outcome == 'success' && hashFiles('README.md') != '' 408 | run: | 409 | git config user.name "github-actions[bot]" 410 | git config user.email "41898282+github-actions[bot]@users.noreply.github.com" 411 | git add README.md 412 | if [ -f compose.yaml ]; then 413 | git add compose.yaml 414 | fi 415 | if [ -f LICENSE ]; then 416 | git add LICENSE 417 | fi 418 | git commit -m "github-actions[bot]: update README.md" 419 | git push origin HEAD:master 420 | 421 | 422 | 423 | 424 | # REPOSITORY SETTINGS 425 | - name: github / update description and set repo defaults 426 | run: | 427 | curl --request PATCH \ 428 | --url https://api.github.com/repos/${{ github.repository }} \ 429 | --header 'authorization: Bearer ${{ secrets.REPOSITORY_TOKEN }}' \ 430 | --header 'content-type: application/json' \ 431 | --data '{ 432 | "description":"${{ env.DOCKER_IMAGE_DESCRIPTION }}", 433 | "homepage":"", 434 | "has_issues":true, 435 | "has_discussions":true, 436 | "has_projects":false, 437 | "has_wiki":false 438 | }' \ 439 | --fail -------------------------------------------------------------------------------- /.github/workflows/hysteria.yml: -------------------------------------------------------------------------------- 1 | name: hysteria 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'hysteria' 9 | jobs: 10 | hysteria: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://api.github.com/repos/apernet/hysteria/releases/latest | jq -r '.tag_name' | sed 's/app\/v//') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/hysteria-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"hysteria.dockerfile", 31 | tag:"hysteria", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"hysteria", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/lego.yml: -------------------------------------------------------------------------------- 1 | name: lego 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'lego' 9 | jobs: 10 | lego: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://api.github.com/repos/go-acme/lego/releases/latest | jq -r '.tag_name' | sed 's/v//') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/lego-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"lego.dockerfile", 31 | tag:"lego", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"lego", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/nginx.yml: -------------------------------------------------------------------------------- 1 | name: nginx 2 | on: 3 | push: 4 | tags: 5 | - 'nginx' 6 | jobs: 7 | nginx: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-nginx/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"nginx.dockerfile", 21 | tag:"nginx", 22 | version:dot.semver.version, 23 | }; 24 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 25 | }catch(e){ 26 | core.setFailed(`workflow failed: ${e}`); 27 | } 28 | })(); 29 | 30 | - name: build docker image 31 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 32 | with: 33 | workflow: docker.yml 34 | token: "${{ secrets.REPOSITORY_TOKEN }}" 35 | inputs: '{ "release":"false", "readme":"false", "run-name":"nginx", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/node.yml: -------------------------------------------------------------------------------- 1 | name: node 2 | on: 3 | push: 4 | tags: 5 | - 'node' 6 | jobs: 7 | node: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-node/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"node.dockerfile", 21 | tag:"node", 22 | version:dot.semver.version, 23 | }; 24 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 25 | }catch(e){ 26 | core.setFailed(`workflow failed: ${e}`); 27 | } 28 | })(); 29 | 30 | - name: build docker image 31 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 32 | with: 33 | workflow: docker.yml 34 | token: "${{ secrets.REPOSITORY_TOKEN }}" 35 | inputs: '{ "release":"false", "readme":"false", "run-name":"node", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/par2.yml: -------------------------------------------------------------------------------- 1 | name: par2 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'par2' 9 | jobs: 10 | par2: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://api.github.com/repos/animetosho/par2cmdline-turbo/releases/latest | jq -r '.tag_name' | sed 's/v//') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/par2-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"par2.dockerfile", 31 | tag:"par2", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"par2", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/readme.yml: -------------------------------------------------------------------------------- 1 | name: readme 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | readme: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: update README.md 11 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 12 | with: 13 | wait-for-completion: false 14 | workflow: docker.yml 15 | token: "${{ secrets.REPOSITORY_TOKEN }}" 16 | inputs: '{ "build":"false", "release":"false", "readme":"true" }' -------------------------------------------------------------------------------- /.github/workflows/socket-proxy.yml: -------------------------------------------------------------------------------- 1 | name: socket-proxy 2 | on: 3 | push: 4 | tags: 5 | - 'socket-proxy' 6 | jobs: 7 | socket-proxy: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-socket-proxy/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"socket-proxy.dockerfile", 21 | tag:"socket-proxy", 22 | version:dot.semver.version, 23 | uid:0, 24 | gid:0, 25 | }; 26 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 27 | }catch(e){ 28 | core.setFailed(`workflow failed: ${e}`); 29 | } 30 | })(); 31 | 32 | - name: build docker image 33 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 34 | with: 35 | workflow: docker.yml 36 | token: "${{ secrets.REPOSITORY_TOKEN }}" 37 | inputs: '{ "release":"false", "readme":"false", "run-name":"socket-proxy", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/tini-pm.yml: -------------------------------------------------------------------------------- 1 | name: tini-pm 2 | on: 3 | push: 4 | tags: 5 | - 'tini-pm' 6 | jobs: 7 | tini-pm: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | const etc = { 16 | dockerfile:"tini-pm.dockerfile", 17 | tag:"tini-pm", 18 | version:"latest" 19 | }; 20 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 21 | 22 | - name: build docker image 23 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 24 | with: 25 | workflow: docker.yml 26 | token: "${{ secrets.REPOSITORY_TOKEN }}" 27 | inputs: '{ "release":"false", "readme":"false", "run-name":"tini-pm", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/traefik.yml: -------------------------------------------------------------------------------- 1 | name: traefik 2 | on: 3 | push: 4 | tags: 5 | - 'traefik' 6 | jobs: 7 | traefik: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: init / base64 nested json 11 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 12 | with: 13 | script: | 14 | const { Buffer } = require('node:buffer'); 15 | (async()=>{ 16 | try{ 17 | const master = await fetch('https://raw.githubusercontent.com/11notes/docker-traefik/refs/heads/master/.json'); 18 | const dot = await master.json(); 19 | const etc = { 20 | dockerfile:"traefik.dockerfile", 21 | tag:"traefik", 22 | version:dot.semver.version, 23 | }; 24 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 25 | }catch(e){ 26 | core.setFailed(`workflow failed: ${e}`); 27 | } 28 | })(); 29 | 30 | - name: build docker image 31 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 32 | with: 33 | workflow: docker.yml 34 | token: "${{ secrets.REPOSITORY_TOKEN }}" 35 | inputs: '{ "release":"false", "readme":"false", "run-name":"traefik", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.github/workflows/unrar.yml: -------------------------------------------------------------------------------- 1 | name: unrar 2 | on: 3 | workflow_dispatch: 4 | schedule: 5 | - cron: "0 5 * * *" 6 | push: 7 | tags: 8 | - 'unrar' 9 | jobs: 10 | unrar: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: cron-update / get latest version 14 | run: | 15 | LATEST_VERSION=$(curl -s https://www.rarlab.com/rar_add.htm | grep -i '>UnRAR source' | grep -oEi '[0-9].[0-9].[0-9]') 16 | echo "LATEST_VERSION=${LATEST_VERSION}" >> "${GITHUB_ENV}" 17 | if curl -kILs --fail https://hub.docker.com/v2/repositories/11notes/distroless/tags/unrar-${LATEST_VERSION}; then 18 | echo "tag ${LATEST_VERSION} exists already!" 19 | else 20 | echo "WORKFLOW_AUTO_UPDATE=true" >> "${GITHUB_ENV}" 21 | fi 22 | 23 | - name: init / base64 nested json 24 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 25 | uses: actions/github-script@62c3794a3eb6788d9a2a72b219504732c0c9a298 26 | with: 27 | script: | 28 | const { Buffer } = require('node:buffer'); 29 | const etc = { 30 | dockerfile:"unrar.dockerfile", 31 | tag:"unrar", 32 | version:"${{ env.LATEST_VERSION }}" 33 | }; 34 | core.exportVariable('WORKFLOW_BASE64JSON', Buffer.from(JSON.stringify(etc)).toString('base64')); 35 | 36 | - name: build docker image 37 | if: env.WORKFLOW_AUTO_UPDATE == 'true' 38 | uses: the-actions-org/workflow-dispatch@3133c5d135c7dbe4be4f9793872b6ef331b53bc7 39 | with: 40 | workflow: docker.yml 41 | token: "${{ secrets.REPOSITORY_TOKEN }}" 42 | inputs: '{ "release":"false", "readme":"false", "run-name":"unrar", "etc":"${{ env.WORKFLOW_BASE64JSON }}" }' -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # default 2 | maintain/ 3 | node_modules/ -------------------------------------------------------------------------------- /.json: -------------------------------------------------------------------------------- 1 | { 2 | "image":"11notes/distroless", 3 | "name":"distroless", 4 | "root":"/distroless", 5 | "arch":"linux/amd64,linux/arm64,linux/arm/v7", 6 | 7 | "readme":{ 8 | "description":"Build your own distroless images with this mini file system and some binaries", 9 | "parent":{ 10 | "image":"scratch" 11 | }, 12 | "distroless":true 13 | } 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 11notes 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![banner](https://github.com/11notes/defaults/blob/main/static/img/banner.png?raw=true) 2 | 3 | # DISTROLESS 4 | [](https://github.com/11notes/docker-DISTROLESS)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![size](https://img.shields.io/docker/image-size/11notes/distroless/latest?color=0eb305)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![version](https://img.shields.io/docker/v/11notes/distroless/latest?color=eb7a09)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![pulls](https://img.shields.io/docker/pulls/11notes/distroless?color=2b75d6)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)[](https://github.com/11notes/docker-DISTROLESS/issues)![5px](https://github.com/11notes/defaults/blob/main/static/img/transparent5x2px.png?raw=true)![swiss_made](https://img.shields.io/badge/Swiss_Made-FFFFFF?labelColor=FF0000&logo=data:image/svg%2bxml;base64,PHN2ZyB2ZXJzaW9uPSIxIiB3aWR0aD0iNTEyIiBoZWlnaHQ9IjUxMiIgdmlld0JveD0iMCAwIDMyIDMyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Im0wIDBoMzJ2MzJoLTMyeiIgZmlsbD0iI2YwMCIvPjxwYXRoIGQ9Im0xMyA2aDZ2N2g3djZoLTd2N2gtNnYtN2gtN3YtNmg3eiIgZmlsbD0iI2ZmZiIvPjwvc3ZnPg==) 5 | 6 | Build your own distroless images with this mini file system and some binaries 7 | 8 | # MAIN TAGS 🏷️ 9 | These are the main tags for the image. There is also a tag for each commit and its shorthand sha256 value. 10 | 11 | * [latest](https://hub.docker.com/r/11notes/distroless/tags?name=latest) 12 | 13 | # REPOSITORIES ☁️ 14 | ``` 15 | docker pull 11notes/distroless:latest 16 | docker pull ghcr.io/11notes/distroless:latest 17 | docker pull quay.io/11notes/distroless:latest 18 | ``` 19 | 20 | # SYNOPSIS 📖 21 | **What can I do with this?** This image and its different layers can be used to build a distroless boiler plate for your application. Simply add the base layer and any additional layers (tags) with the stuff you need to run your application. All binaries are statically compiled and do not depend on any OS libraries or clib. The base layer contains Root CA certificates as well as time zone data and the user configuration for root and docker. Additional layers (tags) with statically compiled binaries are: 22 | 23 | # STAND-ALONE BINARIES 24 | * [11notes/distroless:curl](https://github.com/11notes/docker-distroless/blob/master/curl.dockerfile) - curl 25 | * [11notes/distroless:dnslookup](https://github.com/11notes/docker-distroless/blob/master/dnslookup.dockerfile) - dnslookup 26 | * [11notes/distroless:lego](https://github.com/11notes/docker-distroless/blob/master/lego.dockerfile) - lego 27 | * [11notes/distroless:par2](https://github.com/11notes/docker-distroless/blob/master/par2.dockerfile) - par2 28 | 29 | # APPLICATION SUITES 30 | * [11notes/distroless:node](https://github.com/11notes/docker-node) - node 31 | * [11notes/distroless:adguard](https://github.com/11notes/docker-adguard) - adguard 32 | * [11notes/distroless:adguard-sync](https://github.com/11notes/docker-adguard-sync) - adguard-sync 33 | * [11notes/distroless:nginx](https://github.com/11notes/docker-nginx) - nginx 34 | * [11notes/distroless:traefik](https://github.com/11notes/docker-traefik) - traefik 35 | 36 | # CONTAINER BUILD HELPERS 37 | * [11notes/distroless:tini-pm](https://github.com/11notes/go-tini-pm) - tini-pm 38 | * [11notes/distroless:cmd-socket](https://github.com/11notes/go-cmd-socket) - cmd-socket 39 | * [11notes/distroless:socket-proxy](https://github.com/11notes/docker-socket-proxy) - socket-proxy 40 | 41 | Each tag has sub tags like latest, stable or semver, check the tags available for each binary. If you need more binaries, open a PR or feature request. Some of the images have their own dedicated container images to run the applications within, simply check the link for the source and explanation on how to use them. 42 | 43 | These images are meant as direct competition to very popular images which come with almost no security in mind! 44 | 45 | # BUILD 🚧 46 | ```yaml 47 | # this will create a distroless image that just contains the curl binary 48 | FROM 11notes/distroless AS distroless 49 | FROM 11notes/distroless:curl AS distroless-curl 50 | FROM scratch 51 | COPY --from=distroless --chown=1000:1000 / / 52 | COPY --from=distroless-curl --chown=1000:1000 / / 53 | USER docker 54 | ENTRYPOINT ["curl"] 55 | ``` 56 | 57 | # SOURCE 💾 58 | * [11notes/distroless](https://github.com/11notes/docker-DISTROLESS) 59 | 60 | # ElevenNotes™️ 61 | This image is provided to you at your own risk. Always make backups before updating an image to a different version. Check the [releases](https://github.com/11notes/docker-distroless/releases) for breaking changes. If you have any problems with using this image simply raise an [issue](https://github.com/11notes/docker-distroless/issues), thanks. If you have a question or inputs please create a new [discussion](https://github.com/11notes/docker-distroless/discussions) instead of an issue. You can find all my other repositories on [github](https://github.com/11notes?tab=repositories). 62 | 63 | *created 29.04.2025, 17:13:50 (CET)* -------------------------------------------------------------------------------- /adguard-sync.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=1000 3 | ARG APP_GID=1000 4 | 5 | # :: Distroless 6 | FROM 11notes/distroless AS distroless 7 | FROM 11notes/adguard-sync:${APP_VERSION} AS adguard-sync 8 | FROM scratch 9 | ARG APP_UID 10 | ARG APP_GID 11 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 12 | COPY --from=adguard-sync --chown=${APP_UID}:${APP_GID} /usr/local/bin/adguardhome-sync /usr/local/bin/adguardhome-sync 13 | 14 | # :: Start 15 | USER ${APP_UID}:${APP_GID} 16 | ENTRYPOINT ["/usr/local/bin/adguardhome-sync"] -------------------------------------------------------------------------------- /adguard.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=1000 3 | ARG APP_GID=1000 4 | 5 | # :: Distroless 6 | FROM 11notes/distroless AS distroless 7 | FROM 11notes/adguard:${APP_VERSION} AS adguard 8 | FROM scratch 9 | ARG APP_UID 10 | ARG APP_GID 11 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 12 | COPY --from=adguard --chown=${APP_UID}:${APP_GID} /usr/local/bin/AdGuardHome /usr/local/bin/AdGuardHome 13 | 14 | # :: Start 15 | USER ${APP_UID}:${APP_GID} 16 | ENTRYPOINT ["/usr/local/bin/AdGuardHome"] -------------------------------------------------------------------------------- /arch.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Header 5 | FROM alpine AS distroless 6 | ARG TARGETARCH 7 | ARG APP_ROOT 8 | ARG APP_VERSION 9 | USER root 10 | 11 | # :: create base folders 12 | RUN set -ex; \ 13 | mkdir -p ${APP_ROOT}/etc; \ 14 | mkdir -p ${APP_ROOT}/run; 15 | 16 | # :: create users 17 | RUN set -ex; \ 18 | echo "root:x:0:0:root:/root:/sbin/nologin" > ${APP_ROOT}/etc/passwd; \ 19 | echo "root:x:0:root" > ${APP_ROOT}/etc/group; \ 20 | echo "docker:x:1000:1000:docker:/:/sbin/nologin" >> ${APP_ROOT}/etc/passwd; \ 21 | echo "docker:x:1000:docker" >> ${APP_ROOT}/etc/group; 22 | 23 | # :: add ca-certificates 24 | RUN set -ex; \ 25 | apk --update --no-cache add \ 26 | ca-certificates \ 27 | tzdata; \ 28 | mkdir -p ${APP_ROOT}/usr/share/ca-certificates; \ 29 | mkdir -p ${APP_ROOT}/etc/ssl/certs; \ 30 | cp -R /usr/share/ca-certificates/* ${APP_ROOT}/usr/share/ca-certificates; \ 31 | cp -R /etc/ssl/certs/* ${APP_ROOT}/etc/ssl/certs; 32 | 33 | # :: add timezones 34 | RUN set -ex; \ 35 | apk --update --no-cache add \ 36 | tzdata; \ 37 | mkdir -p ${APP_ROOT}/usr/share/zoneinfo; \ 38 | cp -R /usr/share/zoneinfo/* ${APP_ROOT}/usr/share/zoneinfo; 39 | 40 | # :: Distroless 41 | FROM scratch 42 | ARG APP_ROOT 43 | ARG APP_UID 44 | ARG APP_GID 45 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 46 | 47 | # :: Start 48 | USER ${APP_UID}:${APP_GID} 49 | ENTRYPOINT ["/"] -------------------------------------------------------------------------------- /build.dockerfile: -------------------------------------------------------------------------------- 1 | # this will create a distroless image that just contains the curl binary 2 | FROM 11notes/distroless AS distroless 3 | FROM 11notes/distroless:curl AS distroless-curl 4 | FROM scratch 5 | COPY --from=distroless --chown=1000:1000 / / 6 | COPY --from=distroless-curl --chown=1000:1000 / / 7 | USER docker 8 | ENTRYPOINT ["curl"] -------------------------------------------------------------------------------- /cmd-socket.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM golang:1.24-alpine AS build 9 | ARG APP_ROOT 10 | ARG APP_NO_CACHE 11 | ENV BUILD_ROOT=/go/go-cmd-socket 12 | ENV BUILD_BIN=${BUILD_ROOT}/cmd-socket 13 | ENV CGO_ENABLED=0 14 | USER root 15 | 16 | COPY --from=util /usr/local/bin/ /usr/local/bin 17 | 18 | RUN set -ex; \ 19 | apk --update add \ 20 | build-base \ 21 | upx \ 22 | git; \ 23 | git clone https://github.com/11notes/go-cmd-socket.git; 24 | 25 | RUN set -ex; \ 26 | eleven printenv; 27 | 28 | RUN set -ex; \ 29 | cd ${BUILD_ROOT}; \ 30 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 31 | mkdir -p ${APP_ROOT}/run/cmd; \ 32 | go mod tidy; \ 33 | go build -ldflags="-extldflags=-static" -o ${BUILD_BIN} main.go; 34 | 35 | RUN set -ex; \ 36 | eleven checkStatic ${BUILD_BIN}; \ 37 | eleven strip ${BUILD_BIN}; \ 38 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 39 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 40 | 41 | # :: Distroless 42 | FROM scratch 43 | ARG APP_ROOT 44 | ARG APP_UID 45 | ARG APP_GID 46 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 47 | 48 | # :: Start 49 | USER ${APP_UID}:${APP_GID} 50 | ENTRYPOINT ["/usr/local/bin/cmd-socket"] -------------------------------------------------------------------------------- /curl.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM alpine AS build 9 | ARG TARGETARCH 10 | ARG APP_ROOT 11 | ARG APP_VERSION 12 | ENV BUILD_ROOT=/curl-${APP_VERSION} 13 | ENV BUILD_BIN=/curl-${APP_VERSION}/src/curl 14 | ENV CC=clang 15 | USER root 16 | COPY --from=util /usr/local/bin/ /usr/local/bin 17 | 18 | # :: Build 19 | RUN set -ex; \ 20 | apk --update --no-cache add \ 21 | build-base \ 22 | clang \ 23 | openssl-dev \ 24 | nghttp2-dev \ 25 | nghttp2-static \ 26 | libssh2-dev \ 27 | libssh2-static \ 28 | perl \ 29 | openssl-libs-static \ 30 | zlib-static \ 31 | tar \ 32 | upx \ 33 | wget; 34 | 35 | RUN set -ex; \ 36 | wget https://curl.se/download/curl-${APP_VERSION}.tar.gz; \ 37 | tar xzf curl-${APP_VERSION}.tar.gz; 38 | 39 | RUN set -ex; \ 40 | cd ${BUILD_ROOT}; \ 41 | LDFLAGS="-static" PKG_CONFIG="pkg-config --static" \ 42 | ./configure \ 43 | --disable-shared \ 44 | --enable-static \ 45 | --disable-ldap \ 46 | --disable-ipv6 \ 47 | --enable-unix-sockets \ 48 | --with-ssl \ 49 | --disable-docs \ 50 | --disable-manual \ 51 | --without-libpsl; \ 52 | make -s -j $(nproc) V=1 LDFLAGS="-static -all-static"; 53 | 54 | RUN set -ex; \ 55 | eleven checkStatic ${BUILD_BIN}; \ 56 | eleven strip ${BUILD_BIN}; \ 57 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 58 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 59 | 60 | # :: Distroless 61 | FROM 11notes/distroless AS distroless 62 | FROM scratch 63 | ARG APP_ROOT 64 | ARG APP_UID 65 | ARG APP_GID 66 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 67 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 68 | 69 | # :: Start 70 | USER ${APP_UID}:${APP_GID} 71 | ENTRYPOINT ["/usr/local/bin/curl"] -------------------------------------------------------------------------------- /dnslookup.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM golang:1.24-alpine AS distroless 9 | ARG APP_ROOT 10 | ARG APP_VERSION 11 | ENV BUILD_ROOT=/go/dnslookup 12 | ENV BUILD_BIN=${BUILD_ROOT}/dnslookup 13 | ENV CGO_ENABLED=0 14 | COPY --from=util /usr/local/bin/ /usr/local/bin 15 | USER root 16 | 17 | # :: Build 18 | RUN set -ex; \ 19 | apk --update --no-cache add \ 20 | build-base \ 21 | upx \ 22 | git; \ 23 | git clone https://github.com/ameshkov/dnslookup.git -b v${APP_VERSION}; \ 24 | cd ${BUILD_ROOT}; \ 25 | eleven patchGoMod go.mod "golang.org/x/crypto|v0.31.0|CVE-2024-45337"; \ 26 | eleven patchGoMod go.mod "github.com/quic-go/quic-go|v0.48.2|CVE-2024-53259"; \ 27 | eleven patchGoMod go.mod "golang.org/x/net|v0.36.0|CVE-2025-22870"; \ 28 | go mod tidy; \ 29 | go build -ldflags="-extldflags=-static"; 30 | 31 | RUN set -ex; \ 32 | eleven checkStatic ${BUILD_BIN}; \ 33 | eleven strip ${BUILD_BIN}; \ 34 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 35 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 36 | 37 | # :: Distroless 38 | FROM scratch 39 | ARG APP_ROOT 40 | ARG APP_UID 41 | ARG APP_GID 42 | COPY --from=distroless ${APP_ROOT}/ / 43 | 44 | # :: Start 45 | USER ${APP_UID}:${APP_GID} 46 | ENTRYPOINT ["/usr/local/bin/dnslookup"] -------------------------------------------------------------------------------- /hysteria.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM golang:1.24-alpine AS build 9 | ARG TARGETARCH 10 | ARG APP_ROOT 11 | ARG APP_VERSION 12 | ENV BUILD_ROOT=/go/hysteria 13 | ENV BUILD_BIN=${BUILD_ROOT}/build/hysteria-linux-${TARGETARCH} 14 | ENV CGO_ENABLED=0 15 | COPY --from=util /usr/local/bin/ /usr/local/bin 16 | USER root 17 | 18 | # :: Build 19 | RUN set -ex; \ 20 | apk --update add \ 21 | python3 \ 22 | build-base \ 23 | upx \ 24 | git; \ 25 | git clone https://github.com/apernet/hysteria.git -b app/v${APP_VERSION}; \ 26 | cd ${BUILD_ROOT}; \ 27 | python3 hyperbole.py build -r; 28 | 29 | RUN set -ex; \ 30 | eleven checkStatic ${BUILD_BIN}; \ 31 | eleven strip ${BUILD_BIN}; \ 32 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 33 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin/hysteria; 34 | 35 | # :: Distroless 36 | FROM 11notes/distroless AS distroless 37 | FROM scratch 38 | ARG APP_ROOT 39 | ARG APP_UID 40 | ARG APP_GID 41 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 42 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 43 | 44 | # :: Start 45 | USER ${APP_UID}:${APP_GID} 46 | ENTRYPOINT ["/usr/local/bin/hysteria"] -------------------------------------------------------------------------------- /lego.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM golang:1.24-alpine AS build 9 | ARG TARGETARCH 10 | ARG APP_ROOT 11 | ARG APP_VERSION 12 | ENV BUILD_ROOT=/go/lego 13 | ENV BUILD_BIN=${BUILD_ROOT}/dist/lego 14 | ENV CGO_ENABLED=0 15 | COPY --from=util /usr/local/bin/ /usr/local/bin 16 | USER root 17 | 18 | # :: Build 19 | RUN set -ex; \ 20 | apk --update --no-cache add \ 21 | build-base \ 22 | upx \ 23 | git; \ 24 | git clone https://github.com/go-acme/lego.git -b v${APP_VERSION}; 25 | 26 | RUN set -ex; \ 27 | cd ${BUILD_ROOT}; \ 28 | go build -trimpath -ldflags '-X "main.version='${APP_VERSION}'" -extldflags=-static' -o dist/lego ./cmd/lego/; 29 | 30 | RUN set -ex; \ 31 | cd ${BUILD_ROOT}; \ 32 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 33 | eleven strip ${BUILD_BIN}; \ 34 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 35 | 36 | # :: Distroless 37 | FROM 11notes/distroless AS distroless 38 | FROM scratch 39 | ARG APP_ROOT 40 | ARG APP_UID 41 | ARG APP_GID 42 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 43 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 44 | 45 | # :: Start 46 | USER ${APP_UID}:${APP_GID} 47 | ENTRYPOINT ["/usr/local/bin/lego"] -------------------------------------------------------------------------------- /nginx.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=1000 3 | ARG APP_GID=1000 4 | 5 | # :: Distroless 6 | FROM 11notes/nginx:${APP_VERSION} AS nginx 7 | FROM scratch 8 | ARG APP_UID 9 | ARG APP_GID 10 | COPY --from=nginx --chown=${APP_UID}:${APP_GID} /usr/local/bin/nginx /usr/local/bin 11 | COPY --from=nginx --chown=${APP_UID}:${APP_GID} /nginx / 12 | COPY --from=nginx --chown=${APP_UID}:${APP_GID} /etc/nginx /etc 13 | 14 | # :: Start 15 | USER ${APP_UID}:${APP_GID} 16 | ENTRYPOINT ["/usr/local/bin/nginx"] -------------------------------------------------------------------------------- /node.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=1000 3 | ARG APP_GID=1000 4 | 5 | # :: Distroless 6 | FROM 11notes/node:${APP_VERSION} AS node 7 | FROM scratch 8 | ARG APP_UID 9 | ARG APP_GID 10 | COPY --from=node --chown=${APP_UID}:${APP_GID} /usr/local/bin/node /usr/local/bin 11 | 12 | # :: Start 13 | USER ${APP_UID}:${APP_GID} 14 | ENTRYPOINT ["/usr/local/bin/node"] -------------------------------------------------------------------------------- /par2.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM alpine AS build 9 | ARG TARGETARCH 10 | ARG APP_ROOT 11 | ARG APP_VERSION 12 | ENV BUILD_ROOT=/par2cmdline-turbo 13 | ENV BUILD_BIN=${BUILD_ROOT}/par2 14 | USER root 15 | COPY --from=util /usr/local/bin/ /usr/local/bin 16 | 17 | # :: Build 18 | RUN set -ex; \ 19 | apk --no-cache --update add \ 20 | autoconf \ 21 | automake \ 22 | build-base \ 23 | libffi-dev \ 24 | openssl-dev \ 25 | python3-dev \ 26 | git; 27 | 28 | RUN set -ex; \ 29 | git clone https://github.com/animetosho/par2cmdline-turbo -b v${APP_VERSION}; 30 | 31 | RUN set -ex; \ 32 | cd ${BUILD_ROOT}; \ 33 | ./automake.sh; \ 34 | ./configure \ 35 | --prefix="/usr"; \ 36 | make -s -j $(nproc) V=1 LDFLAGS="--static"; 37 | 38 | RUN set -ex; \ 39 | eleven checkStatic ${BUILD_BIN}; \ 40 | eleven strip ${BUILD_BIN}; \ 41 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 42 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 43 | 44 | # :: Distroless 45 | FROM scratch 46 | ARG APP_ROOT 47 | ARG APP_UID 48 | ARG APP_GID 49 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 50 | 51 | # :: Start 52 | USER ${APP_UID}:${APP_GID} 53 | ENTRYPOINT ["/usr/local/bin/par2"] -------------------------------------------------------------------------------- /project.md: -------------------------------------------------------------------------------- 1 | ${{ content_synopsis }} This image and its different layers can be used to build a distroless boiler plate for your application. Simply add the base layer and any additional layers (tags) with the stuff you need to run your application. All binaries are statically compiled and do not depend on any OS libraries or clib. The base layer contains Root CA certificates as well as time zone data and the user configuration for root and docker. Additional layers (tags) with statically compiled binaries are: 2 | 3 | # STAND-ALONE BINARIES 4 | * [11notes/distroless:curl](https://github.com/11notes/docker-distroless/blob/master/curl.dockerfile) - curl 5 | * [11notes/distroless:dnslookup](https://github.com/11notes/docker-distroless/blob/master/dnslookup.dockerfile) - dnslookup 6 | * [11notes/distroless:lego](https://github.com/11notes/docker-distroless/blob/master/lego.dockerfile) - lego 7 | * [11notes/distroless:par2](https://github.com/11notes/docker-distroless/blob/master/par2.dockerfile) - par2 8 | * [11notes/distroless:unrar](https://github.com/11notes/docker-distroless/blob/master/unrar.dockerfile) - unrar 9 | 10 | # APPLICATION SUITES 11 | * [11notes/distroless:node](https://github.com/11notes/docker-node) - node 12 | * [11notes/distroless:adguard](https://github.com/11notes/docker-adguard) - adguard 13 | * [11notes/distroless:adguard-sync](https://github.com/11notes/docker-adguard-sync) - adguard-sync 14 | * [11notes/distroless:nginx](https://github.com/11notes/docker-nginx) - nginx 15 | * [11notes/distroless:traefik](https://github.com/11notes/docker-traefik) - traefik 16 | * [11notes/distroless:hysteria](https://github.com/11notes/docker-hysteria) - hysteria 17 | 18 | # CONTAINER ENTRYPOINTS 19 | * [11notes/distroless:tini-pm](https://github.com/11notes/go-tini-pm) - tini-pm 20 | 21 | # CONTAINER HELPERS 22 | * [11notes/distroless:socket-proxy](https://github.com/11notes/docker-socket-proxy) - socket-proxy 23 | * [11notes/distroless:cmd-socket](https://github.com/11notes/go-cmd-socket) - cmd-socket 24 | 25 | Each tag has sub tags like latest, stable or semver, check the tags available for each binary. If you need more binaries, open a PR or feature request. Some of the images have their own dedicated container images to run the applications within, simply check the link for the source and explanation on how to use them. 26 | 27 | These images are meant as direct competition to very popular images which come with almost no security in mind! 28 | 29 | ${{ content_build }} 30 | 31 | ${{ content_source }} -------------------------------------------------------------------------------- /socket-proxy.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=0 3 | ARG APP_GID=0 4 | 5 | # :: Distroless 6 | FROM 11notes/distroless AS distroless 7 | FROM 11notes/socket-proxy:${APP_VERSION} AS socket-proxy 8 | FROM scratch 9 | ARG APP_UID 10 | ARG APP_GID 11 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 12 | COPY --from=socket-proxy --chown=${APP_UID}:${APP_GID} /usr/local/bin/socket-proxy /usr/local/bin/socket-proxy 13 | 14 | # :: Start 15 | USER ${APP_UID}:${APP_GID} 16 | ENTRYPOINT ["/usr/local/bin/socket-proxy"] -------------------------------------------------------------------------------- /tini-pm.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM golang:1.24-alpine AS build 9 | ARG APP_ROOT 10 | ARG APP_NO_CACHE 11 | ENV BUILD_ROOT=/go/go-tini-pm 12 | ENV BUILD_BIN=${BUILD_ROOT}/tini-pm 13 | ENV CGO_ENABLED=0 14 | USER root 15 | 16 | COPY --from=util /usr/local/bin/ /usr/local/bin 17 | 18 | RUN set -ex; \ 19 | apk --update add \ 20 | build-base \ 21 | upx \ 22 | git; \ 23 | git clone https://github.com/11notes/go-tini-pm.git; 24 | 25 | RUN set -ex; \ 26 | eleven printenv; 27 | 28 | RUN set -ex; \ 29 | cd ${BUILD_ROOT}; \ 30 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 31 | go mod tidy; \ 32 | go build -ldflags="-extldflags=-static" -o ${BUILD_BIN} main.go; 33 | 34 | RUN set -ex; \ 35 | eleven checkStatic ${BUILD_BIN}; \ 36 | eleven strip ${BUILD_BIN}; \ 37 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 38 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 39 | 40 | # :: Distroless 41 | FROM 11notes/distroless:cmd-socket AS distroless-cmd-socket 42 | FROM scratch 43 | ARG APP_ROOT 44 | ARG APP_UID 45 | ARG APP_GID 46 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 47 | COPY --from=distroless-cmd-socket --chown=${APP_UID}:${APP_GID} / / 48 | 49 | # :: Start 50 | USER ${APP_UID}:${APP_GID} 51 | ENTRYPOINT ["/usr/local/bin/tini-pm"] -------------------------------------------------------------------------------- /traefik.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_VERSION=stable 2 | ARG APP_UID=1000 3 | ARG APP_GID=1000 4 | 5 | # :: Distroless 6 | FROM 11notes/distroless AS distroless 7 | FROM 11notes/traefik:${APP_VERSION} AS traefik 8 | FROM scratch 9 | ARG APP_UID 10 | ARG APP_GID 11 | COPY --from=distroless --chown=${APP_UID}:${APP_GID} / / 12 | COPY --from=traefik --chown=${APP_UID}:${APP_GID} /usr/local/bin/traefik /usr/local/bin 13 | COPY --from=traefik --chown=${APP_UID}:${APP_GID} /traefik / 14 | 15 | # :: Start 16 | USER ${APP_UID}:${APP_GID} 17 | ENTRYPOINT ["/usr/local/bin/traefik"] -------------------------------------------------------------------------------- /unrar.dockerfile: -------------------------------------------------------------------------------- 1 | ARG APP_UID=1000 2 | ARG APP_GID=1000 3 | 4 | # :: Util 5 | FROM 11notes/util AS util 6 | 7 | # :: Header 8 | FROM alpine AS build 9 | ARG TARGETARCH 10 | ARG APP_ROOT 11 | ARG APP_VERSION 12 | ENV BUILD_ROOT=/unrar 13 | ENV BUILD_BIN=${BUILD_ROOT}/unrar 14 | USER root 15 | COPY --from=util /usr/local/bin/ /usr/local/bin 16 | 17 | # :: Build 18 | RUN set -ex; \ 19 | apk --update --no-cache add \ 20 | build-base \ 21 | gcc \ 22 | clang \ 23 | g++ \ 24 | make \ 25 | cmake \ 26 | git \ 27 | curl \ 28 | tar \ 29 | xz; 30 | 31 | RUN set -ex; \ 32 | curl -SL https://www.rarlab.com/rar/unrarsrc-${APP_VERSION}.tar.gz | tar -zxC /; 33 | 34 | RUN set -ex; \ 35 | cd ${BUILD_ROOT}; \ 36 | make -s -j $(nproc) V=1 LDFLAGS="--static"; 37 | 38 | RUN set -ex; \ 39 | eleven checkStatic ${BUILD_BIN}; \ 40 | eleven strip ${BUILD_BIN}; \ 41 | mkdir -p ${APP_ROOT}/usr/local/bin; \ 42 | cp ${BUILD_BIN} ${APP_ROOT}/usr/local/bin; 43 | 44 | # :: Distroless 45 | FROM scratch 46 | ARG APP_ROOT 47 | ARG APP_UID 48 | ARG APP_GID 49 | COPY --from=build --chown=${APP_UID}:${APP_GID} ${APP_ROOT}/ / 50 | 51 | # :: Start 52 | USER ${APP_UID}:${APP_GID} 53 | ENTRYPOINT ["/usr/local/bin/unrar"] --------------------------------------------------------------------------------