50mm: Why This S3-Backed Photo Gallery Ditches Databases and Preprocessing
Hook
Most photo gallery software spends 90% of its complexity managing state between your storage and your database. 50mm asks: what if you just eliminated that entire layer?
Context
Traditional photo gallery software follows a familiar pattern: upload images through a web interface, store metadata in a database, generate thumbnails during preprocessing, and serve everything through a content management system. This architecture made sense when storage was expensive and API calls were slow, but it creates a frustrating workflow for photographers who already have their images in cloud storage.
The modern photography workflow looks different. You shoot RAW files, edit them in Lightroom or Capture One, export JPEGs, and sync them to S3 using dedicated clients or CLI tools. By the time you want those photos on the web, they're already optimized and organized in buckets. Why should you re-upload them through a clunky admin panel? Why maintain a database that mirrors information already implicit in your S3 bucket structure? 50mm was built to bridge this gap—a Go web server that treats your S3 bucket as the single source of truth and generates galleries on-demand.
Technical Insight
The architectural decision that defines 50mm is its refusal to cache or preprocess anything. When a request comes in for a gallery page, the server makes an S3 ListObjectsV2 API call, retrieves the current bucket contents, and renders HTML on the fly. No database sync jobs, no background workers, no stale content.
The INI-based configuration system maps domains and URL paths to S3 prefixes. Here's how you'd configure two albums under different paths:
[site:example.com]
name = My Photography
[album:example.com/wedding]
bucket = my-photos
prefix = 2024/wedding/
title = Sarah & Mike's Wedding
[album:example.com/portfolio]
bucket = my-photos
prefix = portfolio/favorites/
title = Portfolio Highlights
When someone requests example.com/wedding, 50mm calls S3 to list all objects under my-photos/2024/wedding/, filters for image extensions, and renders a gallery template. The beauty is in what doesn't happen: no database INSERT statements, no Redis cache invalidation, no webhook handlers. Add a photo to S3? It appears in the gallery on the next page load.
The image serving strategy reveals another pragmatic choice. Instead of implementing its own thumbnail generation, 50mm integrates with existing image optimization services. You can configure Imgix or Thumbor URLs, and the server generates markup pointing to those services:
// Pseudo-code showing the image URL generation logic
func (a *Album) ImageURL(key string, width int) string {
if a.ImgixDomain != "" {
return fmt.Sprintf("https://%s/%s?w=%d&auto=format",
a.ImgixDomain, key, width)
}
if a.ThumborURL != "" {
return fmt.Sprintf("%s/unsafe/%dx0/%s",
a.ThumborURL, width, url.PathEscape(key))
}
// Fallback to direct S3 URL
return a.S3Client.PresignedGetObject(key, 3600)
}
This delegation strategy keeps 50mm's codebase minimal—there's no ImageMagick bindings, no format conversion logic, no disk I/O for thumbnail caching. The server is purely concerned with HTML generation and S3 orchestration.
The multi-tenancy support works through domain-based routing. A single 50mm instance can serve multiple domains, each with its own albums and S3 credentials. The HTTP request's Host header determines which site configuration to load. This makes it viable to run one binary serving galleries for multiple clients or projects, reducing operational overhead.
One subtle but important detail: 50mm expects you to run it behind nginx or Caddy for SSL termination. The server itself only speaks HTTP. This isn't a limitation—it's an architectural choice that keeps the Go code focused. SSL certificate management, rate limiting, and static asset caching belong at the reverse proxy layer. The binary stays small and deployable anywhere you can run a Linux process.
Gotcha
The on-demand architecture has a performance ceiling that's lower than cached alternatives. Every gallery page request triggers an S3 API call, and AWS charges per request. For a portfolio site with 100 visitors per day, this is negligible. For a high-traffic gallery getting thousands of hits, you're paying for redundant ListObjects calls that return the same data. The lack of any caching layer—not even a simple TTL-based in-memory cache—means response times depend entirely on S3 latency and your image service's speed.
The authentication story is equally bare-bones. 50mm supports HTTP Basic Auth configured per-album, which means browser password prompts and credentials sent with every request (over your reverse proxy's HTTPS, hopefully). There's no session management, no user database, no way to have different permission levels. For private family albums or client proofs, Basic Auth suffices. For anything requiring granular access control, user registration, or time-limited sharing links, you'll need to layer additional tooling on top or choose different software entirely. The deployment process also requires comfort with Go toolchains—there are no prebuilt binaries, Docker images, or one-click installers. You're cloning the repo, running go build, and figuring out systemd or supervisor configuration yourself.
Verdict
Use if: You're a photographer or creative who already uses S3 clients (Transmit, Cyberduck, AWS CLI) for managing images and want a gallery that reflects your bucket structure instantly. You value deployment simplicity over features, understand reverse proxies, and don't expect high traffic volumes. You're comfortable running Go binaries and value being able to read and modify 300 lines of straightforward Go code over configuring a complex CMS. Skip if: You need user accounts, comments, EXIF metadata display, or any social features. You're serving high-traffic galleries where per-request S3 calls become expensive. You require a polished admin interface for non-technical users to manage content. You can't deploy custom binaries (shared hosting, locked-down corporate environments). You need mobile apps or advanced organization features like facial recognition or geotagging—basically anything beyond "show my S3 images as HTML."