automemlimit

Automatically sets Go's GOMEMLIMIT from the Linux cgroup memory limit so the garbage collector stays aware of container memory constraints.

Library
Go
vv1.0.0
541stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
54/100Fair
Development Activity56
Maintenance28
Community36
Maturity56
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture78
Code Quality82
Innovation72
Learning Curve65

automemlimit reads the memory limit assigned to a process by Linux cgroups (v1 or v2) and uses it to configure Go’s GOMEMLIMIT, the soft memory cap the garbage collector uses to avoid over-committing heap growth. Without it, a Go binary running in a container or Kubernetes pod has no idea it’s memory-constrained and can keep growing its heap until the kernel OOM-kills it — even though runtime/debug.SetMemoryLimit has existed since Go 1.19, nothing sets it automatically from the environment.

The simplest usage is a single blank import (import _ "github.com/KimMachineGun/automemlimit") which sets GOMEMLIMIT to 90% of the detected cgroup limit at process start. The memlimit subpackage exposes the same logic as a composable API — configurable ratio, minimum bound, custom or fallback providers, periodic refresh for containers whose limits change at runtime, and structured logging — for callers who need more control than the side-effect import gives them.

What You Get

  • A zero-config blank import that sets GOMEMLIMIT to 90% of the cgroup memory limit as soon as the process starts
  • A memlimit.Set API with functional options for ratio, minimum bound, custom providers, and periodic refresh
  • Correct cgroup v1 and v2 detection, including nested hierarchy walking and conflicting-mount resolution via /proc/self/mountinfo and /proc/self/cgroup
  • A FromSystem fallback provider (via github.com/pbnjay/memory) for processes running outside cgroups entirely
  • Structured log/slog logging of every limit change, with a safe no-op default when logging isn’t configured

Common Use Cases

  • Preventing Go services in Kubernetes pods from being OOM-killed by making the GC aware of the pod’s memory request/limit
  • Running Go binaries in Docker containers with -m/--memory limits without hand-tuning GOMEMLIMIT per deployment
  • Reacting to memory limits that change at runtime (e.g. vertical pod autoscaling) via the periodic refresh option
  • Falling back to total system memory as a soft limit for processes that aren’t containerized at all

Under The Hood

Architecture The module splits into a root automemlimit package holding only a side-effect init() that calls memlimit.Set with a default slog logger, and a memlimit subpackage carrying all the logic: memlimit.go defines the functional-options Set entrypoint and config struct, provider.go defines the Provider type (func() (uint64, error)) plus composable wrappers ApplyRatio, ApplyFallback, and Limit, cgroups.go/cgroups_linux.go/cgroups_unsupported.go handle cgroup v1/v2 detection behind Linux/non-Linux build tags, and system.go supplies a total-system-memory fallback. Set composes a provider chain (ratio-adjusted, then bounded by a configured minimum), calls runtime/debug.SetMemoryLimit, and optionally starts a refresh goroutine on a ticker. Because every wrapper depends on the same Provider function signature, changing that one abstraction would ripple through the entire package.

Tech Stack Pure Go, targeting go 1.21+, with a single external dependency (github.com/pbnjay/memory) used only for the system-memory fallback provider. Everything else is stdlib: runtime/debug.SetMemoryLimit, log/slog, and direct parsing of /proc/self/mountinfo and /proc/self/cgroup. There’s no build tooling beyond go build/go test; the examples/ directory ships three standalone example modules (dynamic, logger, system) each with their own go.mod. CI runs the test suite inside real golang:1.21 Docker containers launched with -m=<limit>, asserting the actual resulting GOMEMLIMIT against expected values on both Ubuntu 22.04 and 24.04 runners.

Code Quality Test coverage is extensive and unusually rigorous for the domain: cgroups_test.go (27KB) and its fixtures exercise v1/v2 mount and hierarchy parsing edge cases, memlimit_test.go/memlimit_linux_test.go cover the Set option surface, provider_test.go covers ratio/fallback edge cases (NaN, zero, truncation), and build-tag-gated *_unsupported_test.go files assert graceful behavior on non-Linux platforms. Errors are sentinel-typed (ErrNoLimit, ErrNoCgroup, ErrCgroupsNotSupported), checked with errors.Is, and wrapped with %w throughout rather than swallowed. There’s no dedicated lint config in the repo, but CI additionally runs real container-based integration tests rather than relying solely on mocks.

API Design The library offers two entry points at different levels of ceremony: a zero-boilerplate blank import (import _ "github.com/KimMachineGun/automemlimit") for the common case, and memlimit.Set(opts ...Option) with idiomatic functional options (WithRatio, WithMin, WithProvider, WithLogger, WithRefreshInterval) for callers who need to customize the ratio, supply a fallback provider chain, or refresh on an interval. The Provider abstraction (func() (uint64, error)) is small and composable — ApplyRatio, ApplyFallback, and Limit all take and return a Provider, so callers can build custom sources without touching the library’s internals. Naming is consistent and documentation is thorough — every exported option and error documents its default and behavior inline via godoc comments — and the README leads with the simplest possible usage before showing the configurable API, minimizing the boilerplate needed to get started.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search