Serve 30,000×50,000 Images Without OOM: Go + libvips Tile Streaming
For Western developers building medical imaging, satellite imagery, or high-res map viewers in Go, this pattern solves the fundamental memory wall that standard image libraries hit. The static compilation trick also removes the biggest deployment headache of libvips — making it viable for edge devices, Docker scratch images, and air-gapped servers.
Loading a 30,000×50,000 RGBA image with Go's standard library requires ~5.6GB of contiguous memory — a guaranteed OOM under concurrency. A new open-source approach replaces full-image decoding with a tiled architecture built on libvips/govips.
The service exposes two endpoints: a metadata API that returns the image's max zoom level and grid dimensions, and a tile API that extracts 256×256 blocks on demand. libvips's `AccessRandom` mode reads only the file blocks covering the requested region, while JPEG shrink-on-load decodes at 1/2, 1/4, or 1/8 resolution for lower zoom levels — cutting data volume from 5.6GB to a few KB per tile.
Concurrency is managed via a buffered semaphore capped at `runtime.NumCPU()*2`, and libvips is initialized lazily on the first request. The entire stack is statically compiled with CGO, producing a single binary that bundles libvips and all its codec dependencies — no server-side package installation required.
The real bottleneck in serving large images isn't disk I/O or network — it's the assumption that you must decode the whole file before you can read any pixel.
libvips's shrink-on-load is a rare example of a library-level optimization that changes the asymptotic complexity of a common operation.
Static compilation of C libraries via CGO is underused in the Go ecosystem; this pattern proves it can eliminate entire classes of deployment friction.
The semaphore-based concurrency control is a pragmatic middle ground between full thread-pool management and naive goroutine spawning.
Lazy initialization of heavy native libraries is a good pattern for microservices where the feature may not be used on every request path.