{"id":16895930,"url":"https://github.com/stevenacoffman/small","last_synced_at":"2026-05-13T07:37:46.981Z","repository":{"id":147330309,"uuid":"233457626","full_name":"StevenACoffman/small","owner":"StevenACoffman","description":"Example small webserver in a small docker container","archived":false,"fork":false,"pushed_at":"2024-07-26T18:50:35.000Z","size":9129,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T05:40:35.052Z","etag":null,"topics":["distroless","docker","go","golang"],"latest_commit_sha":null,"homepage":"","language":"Shell","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/StevenACoffman.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-01-12T20:57:24.000Z","updated_at":"2024-07-26T18:50:38.000Z","dependencies_parsed_at":null,"dependency_job_id":"fb4fa236-7c3b-4343-98f9-5b32c5c2d0ce","html_url":"https://github.com/StevenACoffman/small","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenACoffman%2Fsmall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenACoffman%2Fsmall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenACoffman%2Fsmall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/StevenACoffman%2Fsmall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/StevenACoffman","download_url":"https://codeload.github.com/StevenACoffman/small/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244592072,"owners_count":20477837,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["distroless","docker","go","golang"],"created_at":"2024-10-13T17:27:05.692Z","updated_at":"2026-05-13T07:37:46.962Z","avatar_url":"https://github.com/StevenACoffman.png","language":"Shell","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Get Small with Distroless Docker and Go\n### Create the smallest secured golang docker image\n\nRestricting what's in your runtime container to precisely what's necessary for your app is a best practice employed by Google and other tech giants that have used containers in production for many years. Security scanners finding vulnerabilities (e.g. CVE) in unrelated baggage just adds tedious maintenance work. Skip all that and just maintain only what you need.\n\nRunning your containers as a non-root user prevents malicious code from gaining permissions in the container host and means that not just anyone who has pulled your container from the Docker Hub can gain access to everything on your server, for example.\n\n```\n✓ usage: make [target]\n\nbuild                          - Build the application\nclean                          - Cleans the binary\ndocker-build-no-cache          - Build the smallest secure golang docker image based on distroless static with no cache\ndocker-build                   - Build the smallest secure golang docker image based on distroless static\ndocker-push                    - Pushes the docker image to registry\ndocker-run                     - Run the smallest and secured golang docker image based on distroless static\nhelp                           - Show help message\nlint                           - Lint the application code for problems and nits\nls                             - List size docker images\nrun                            - Runs go run main.go\ntest                           - Runs go test with default values\n```\n\n`make run` will start a webserver that will listen and respond http://127.0.0.1:8080/health and everything else gives 404 Not Found.\n### Quickstart \n\n```\nmake build \u0026\u0026 make run\n```\n\n#### [Docker Security Best Practices](https://snyk.io/blog/10-docker-image-security-best-practices/):\n1. Prefer minimal base images\n2. Least privileged user\n3. Sign and verify images to mitigate MITM attacks\n4. Find, fix and monitor for open source vulnerabilities\n5. Don’t leak sensitive information to Docker images\n6. Use fixed tags for immutability\n7. Use COPY instead of ADD\n8. Use metadata labels\n9. Use multi-stage build for small and secure images\n10. Use a [linter](https://github.com/hadolint/hadolint)\n\n### Docker Image Size Comparison\n\n| **Builder Stage** | **Final Stage** | **Final Image Size** |\n|---|---|---|\n| `golang:1.13.6-alpine3.11` | `scratch` | **6.84MB**  |\n| `golang:1.13.6-buster` | `gcr.io/distroless/static:nonroot` | **7.27MB** |\n\nAlpine uses the musl library, and [Distroless](https://github.com/GoogleContainerTools/distroless/tree/master/base) uses glibc library. \nIf you are using libraries that require cgo, sometimes they don't work well with musl.\n\n+ **Alpine** is basically busybox linux with a package manager.\n+ **Distroless** is basically debian _without_ a package manager.\n\n`gcr.io/distroless/static` contains:\n\n* ca-certificates\n* A /etc/passwd entry for a root user and nobody (unprivileged)\n* A /tmp directory\n* tzdata\n\nThe `Dockerfile.alpine` here shows how securing alpine-based docker builds is more complicated than with distroless.\n\nBTW, Distroless has [different tags for base images](https://console.cloud.google.com/gcr/images/distroless/GLOBAL/base?gcrImageListsize=10) not always mentioned in documentation :\n\n+ latest\n+ debug\n+ nonroot\n+ debug-nonroot\n\n### Credit Where It Is Due\n\nAlpine Docker container inspired by [this excellent article](https://medium.com/@chemidy/create-the-smallest-and-secured-golang-docker-image-based-on-scratch-4752223b7324)\n\n### More to read\n\n[Go docker images: small and simple](https://laurentsv.com/blog/2024/06/25/stop-the-go-and-docker-madness.html)\n\n### To Do:\nI would like to add:\n```\n  // Per https://pkg.go.dev/runtime/debug#SetMemoryLimit\n  // A negative input does not adjust the limit, and allows for retrieval of the currently set memory limit.\n\tmemlimit := debug.SetMemoryLimit(-1)\n\tif memlimit == math.MaxInt64 {\n\t\tlog.Warnf(\"Memory limit not set, please set the GOMEMLIMIT env var; e.g. GOMEMLIMIT=1GiB\")\n\t}\n```\n\n### Wanna get **real** small?\nIf you add upx to your builder stage, you can shrink the binary even more:\n`upx --brute app`\nThis is is very slow to build and minutely impacts startup time and resources.\n\nHowever, you can often fit the result on a floppy disk, so that's cool.\n\n### Graceful shutdown\nThis is a pretty good writeup about how to do [Graceful shutdown](https://rafallorenz.com/go/handle-signals-to-graceful-shutdown-http-server/) as is this [errgroup / waitgroup one](https://www.rudderstack.com/blog/implementing-graceful-shutdown-in-go/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenacoffman%2Fsmall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstevenacoffman%2Fsmall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstevenacoffman%2Fsmall/lists"}