High-performance media conversion service designed for WhatsApp-optimised audio, images, and S3 uploads. Built with Go, Fiber v3, FFmpeg, and libvips to sustain >1k requests/second with predictable latency.
- WhatsApp Media Converter API
 
- Audio → Opus: Converts any supported format to WhatsApp-compliant Opus containers using FFmpeg.
 - Image Optimisation: Converts still images to high-quality, compressed JPEG via libvips fallback to FFmpeg.
 - S3 Upload Service: Unified upload manager with MinIO, AWS S3, Backblaze, and generic-compatible providers.
 - Worker & Buffer Pools: Deterministic latency under burst loads; configurable via environment variables.
 - Web UI: Static single-page interface for drag & drop conversions and upload monitoring.
 - Observability: Health, statistics, and S3 status endpoints for readiness probes and dashboards.
 - Docker Native: Multi-stage builds and docker-compose profiles for API, monitoring, and development stacks.
 - Automated Releases: Conventional commit driven versioning, changelog generation, GitHub Releases, and Docker pushes.
 
graph TD
    subgraph Clients
        WebUI[Web UI]
        RestClient[REST Clients]
        Automations[Automations]
    end
    subgraph API Layer
        Fiber[Fiber v3 HTTP Server]
        Router[Modular Handlers]
        Middleware[Tracing & Limits]
    end
    subgraph Services
        AudioSvc[Audio Converter]
        ImageSvc[Image Converter]
        UploadSvc[S3 Upload Manager]
    end
    subgraph Infra
        WorkerPool[Worker Pool]
        BufferPool[10MB Buffers]
        FFmpeg[FFmpeg]
        Libvips[libvips]
        S3Provider[S3/MinIO]
    end
    Clients --> Fiber --> Router --> Services
    Services --> WorkerPool
    WorkerPool --> BufferPool
    AudioSvc --> FFmpeg
    ImageSvc --> Libvips
    UploadSvc --> S3Provider
    Key directories:
| Path | Purpose | 
|---|---|
cmd/api | 
Bootstrap Fiber server, inject configuration, start services | 
internal/config | 
Environment parsing and validation | 
internal/handlers | 
REST handlers (conversion, uploads, web assets) | 
internal/services | 
Core conversion flows, S3 service, buffer/work pools | 
internal/providers | 
S3 provider implementations (MinIO, AWS, Backblaze) | 
web | 
Static HTML/CSS/JS front-end distributed with the API | 
scripts | 
Load/stress helpers, benchmarking utilities | 
docker | 
Compose profiles, monitoring stack assets | 
| Method | Endpoint | Description | 
|---|---|---|
POST | 
/convert/audio | 
Base64 or URL input → Opus audio data URI | 
POST | 
/convert/image | 
Base64 or URL input → Optimised JPEG data URI | 
POST | 
/convert/batch/audio | 
Batch audio conversion (max 10 items) | 
POST | 
/convert/batch/image | 
Batch image conversion (max 10 items) | 
POST | 
/upload/s3 | 
Multipart upload to configured S3 bucket | 
POST | 
/upload/s3/base64 | 
Base64 payload upload | 
GET | 
/upload/s3/status/:id | 
Upload status with metrics | 
GET | 
/upload/s3/list | 
Recent uploads (optional status filter) | 
GET | 
/upload/s3/health | 
Provider health check | 
GET | 
/stats | 
Runtime metrics (worker pool, buffer usage, memory) | 
GET | 
/health | 
Readiness / liveness probe | 
GET | 
/ | 
Web console | 
All endpoints return structured JSON with detailed error messages and progress indicators. Responses include fine-grained metadata such as conversion duration, output size, and S3 URLs when applicable.
Create a .env file from the template and adjust according to your environment.
cp .env.example .env| Variable | Default | Description | 
|---|---|---|
PORT | 
8080 | 
HTTP listen port | 
MAX_WORKERS | 
32 | 
Worker pool size | 
BUFFER_POOL_SIZE | 
100 | 
Number of pre-allocated buffers | 
BUFFER_SIZE | 
10485760 (10MB) | 
Size for each buffer | 
REQUEST_TIMEOUT | 
5m | 
Request deadline enforced by handlers | 
BODY_LIMIT | 
524288000 (500MB) | 
Max request body size | 
| Variable | Notes | 
|---|---|
S3_ENABLED | 
true/false toggle | 
S3_PROVIDER | 
minio, aws, backblaze, ... | 
S3_ENDPOINT, S3_REGION, S3_BUCKET | 
Provider connection details | 
S3_ACCESS_KEY, S3_SECRET_KEY | 
Credentials (consider secrets) | 
S3_PATH_STYLE | 
Force path-style URLs for MinIO | 
S3_PUBLIC_READ | 
Automatically set objects to public | 
S3_MAX_CONCURRENT_UPLOADS | 
Cap simultaneous uploads | 
S3_CHUNK_SIZE, S3_MULTIPART_THRESHOLD | 
Multipart tuning | 
The S3 upload handler buffers multipart files in-memory to guarantee deterministic retries and avoid partial uploads when the provider issues retries.
- Docker 24+
 - Docker Compose v2+
 - FFmpeg and libvips when running locally (
make install-deps-macormake install-deps-ubuntu) 
make docker-run
# API available at http://localhost:8080
# Web UI available at http://localhost:8080/make deps
make runcurl http://localhost:8080/health| Command | Description | 
|---|---|
make help | 
List available commands | 
make run | 
Start API locally | 
make dev | 
Run with hot reload (requires air) | 
make test | 
Run unit and integration tests with race detector | 
make docker-build | 
Build container image locally | 
make docker-run | 
Start docker-compose API stack | 
make monitoring-up | 
Bring up Prometheus & Grafana profile | 
The Makefile reads the version from the VERSION file, which is maintained by the release automation.
make test— run Go tests (./...) with-raceand coverage.make lint— executegolangci-lintto enforce formatting and idiomatic Go.go testis executed on CI for every pull request and push tomain.- Optional: 
make benchmark,make load-test, andmake stress-testfor performance validation. - Static analysis (
golangci-lint) is no longer bundled in the Makefile because upstream releases are currently incompatible with Go 1.25. Run it via a pre-built binary or container if needed. 
The repository ships with two primary GitHub Actions workflows:
ci.yml— Lints, tests, and builds the project on every PR and push.release.yml— Runs on pushes tomainusing Semantic Release to:- Infer the next semantic version from commit messages (Conventional Commits).
 - Update 
CHANGELOG.mdandVERSION, committing them back tomain. 
- Create a Git tag (
vX.Y.Z) and GitHub Release with generated notes. - Build and push multi-architecture Docker images tagged with the version and 
latestto:ghcr.io/<owner>/<repo>(using the defaultGITHUB_TOKEN).- Docker Hub (optional) when 
DOCKERHUB_USERNAME&DOCKERHUB_TOKENsecrets are provided. 
 
| Secret | Description | 
|---|---|
DOCKERHUB_USERNAME | 
(Optional) Docker Hub account name for publishing images. | 
DOCKERHUB_TOKEN | 
(Optional) Docker Hub personal access token. | 
No additional configuration is required for GHCR — the workflow authenticates with GITHUB_TOKEN.
To trigger a release, merge commits into main following the Conventional Commits specification (feat:, fix:, chore:, etc.). Semantic Release determines the bump (major/minor/patch) automatically.
Images are built from the provided Dockerfile and contain:
- Distroless-alpine runtime stage
 - Statically compiled Go binary (
media-converter) - Pre-bundled static web assets (
web/*) 
Tags published:
ghcr.io/guilhermejansen/whats-convert-api:latestghcr.io/guilhermejansen/whats-convert-api:vX.Y.Zguilhermejansen/whats-convert-api:latest(when Docker Hub secrets configured)guilhermejansen/whats-convert-api:vX.Y.Z
| Symptom | Resolution | 
|---|---|
http: ContentLength ... Body length 0 during uploads | 
Ensure MinIO/S3 credentials are valid; the upload manager buffers and retries with deterministic readers | 
libvips or ffmpeg missing | 
Install via make install-deps-mac or make install-deps-ubuntu, or rely on Docker runtime | 
| High latency under load | Tune MAX_WORKERS, BUFFER_POOL_SIZE, and BUFFER_SIZE; monitor /stats | 
| Release workflow fails on Docker push | Verify Docker Hub secrets and that the account has repository permissions | 
- Fork the repository & clone locally.
 - Create a feature branch (
git checkout -b feat/add-awesome-thing). - Follow the coding standards (Go fmt, idiomatic naming, contextual logging).
 - Write tests alongside features or fixes when feasible.
 - Commit using Conventional Commits.
 - Open a pull request using the provided template — include test output and reproduction steps.
 
Issue templates for bug reports and feature requests are located in .github/ISSUE_TEMPLATE/.
Released under the MIT License © 2025 Guilherme Jansen.
Guilherme Jansen Github