High-performance content delivery for automatic image and video optimization, accelerating load times and lowering resource use.
Find a file
maxpeterkaya 01e9a4038d
Some checks failed
Lint / vet (push) Successful in 34s
Lint / golangci-lint (push) Failing after 45s
Merge pull request 'chore(deps): update docker/login-action action to v4.2.0' (#135) from renovate/docker-login-action-4.x into master
Reviewed-on: #135
2026-05-27 19:01:40 -04:00
.forgejo/workflows
api feat: respond upload with struct response 2026-05-27 15:48:39 -04:00
config
docker
file
watcher
.gitignore
.golangci.yml
.goreleaser.yaml
artifacthub-repo.yml
FAQ.md
go.mod Merge pull request 'chore(deps): update module github.com/go-chi/chi/v5 to v5.3.0' (#136) from renovate/github.com-go-chi-chi-v5-5.x into master 2026-05-27 19:01:29 -04:00
go.sum Merge pull request 'chore(deps): update module github.com/go-chi/chi/v5 to v5.3.0' (#136) from renovate/github.com-go-chi-chi-v5-5.x into master 2026-05-27 19:01:29 -04:00
LICENSE
main.go
README.md
renovate.json

Amur - Automatic Media Optimization Server


This is a self-hosted media server that automatically processes and serves optimized images and videos. When a user uploads any file, the server saves it and, for images, creates thumbnail, AVIF, and WebP versions automatically. For videos, it generates HLS adaptive bitrate streams. When serving, it intelligently selects the best format (AVIF > WebP > original) based on what's available, without the client specifying an extension. This means front-end developers can request /XYge6W8BSXYhGUhczLqyK and always get the smallest, fastest-loading image with zero extra code.

Without this system, a content team would manually convert images and write complex client-side logic. This automates the entire pipeline, cutting page load times and bandwidth costs dramatically.

Demo


How to Run

curl -o https://vc.maxkaya.com/maxpeterkaya/amur/raw/branch/master/docker/docker-compose.yml && docker compose up -d

The server is now accepting uploads on http://{{DOMAIN}}/upload and serving files on http://{{DOMAIN}}/.

Uploading

Request: curl -X POST -H "Authorization: {{UPLOAD_KEY}}" http(s)://{{DOMAIN}}/upload

Response:

{
  "id": "XYge6W8BSXYhGUhczLqyK",
  "domain": "{{DOMAIN}}",
  "url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK",
  "original_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK.{{png|jpg|jpeg|mp4|.etc}}",
  "avif_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK.avif",
  "webp_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK.webp",
  "thumbnail_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK_thumb",
  "thumbnail_avif_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK_thumb.avif",
  "thumbnail_webp_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK_thumb.webp",
  "thumbnail_original_url": "http(s)://{{DOMAIN}}/XYge6W8BSXYhGUhczLqyK.{{png|jpg|jpeg|mp4|.etc}}"
}

Serving

Pick a URL from the response for directly referencing a file extension or just use the extension-less URL to serve whichever file format is most optimized and available.

Functionality


  • Re-Process failed optimizations
  • File watching, optimize on new uploads
  • Migrate non-sharded folders
  • Process files on cron job
  • Upload files via REST
  • Complete structured logs (compatible with alloy, promtail, etc.)
  • Healthcheck
  • Graceful shutdown
  • Prometheus metrics
  • Avif optimizations
  • WebP optimizations
  • HLS conversions & resolution resizing via FFmpeg

ENV


These are all default values so you don't have to define them all yourself.

Variable Default Value Data type Description
PORT 3000 *int Port that the server runs on.
DOMAIN localhost string Domain the server runs on, useful for custom CORS configurations and cookies.
PUBLIC_FOLDER /public *string Folder with all files to be served by server.
UPLOAD_KEY *string Upload key for upload authentication.
PROMETHEUS_USERNAME admin *string Username to use with prometheus
PROMETHEUS_PASSWORD *string Password for prometheus, if left empty then prometheus is disabled
TZ UTC *string Set timezone of server.

* Purely optional variables that aren't needed for basic functionality.

To properly work the following are REQUIRED:

  • DOMAIN MUST be set to your serving domain for security header purposes.
  • UPLOAD_KEY MUST be over 8 characters to unlock upload capabilities.
  • PUBLIC_FOLDER If you are running on host, make sure to define something like ./public/, otherwise leave the default in the docker container.

Docker Images


The binary for amur comes with avif support via a WASM bundle.

Bare

vc.maxkaya.com/maxpeterkaya/amur:bare OR vc.maxkaya.com/maxpeterkaya/amur:latest

This image does NOT include webp or ffmpeg, only avif support which is already part of the binary.

AIO - All In One

vc.maxkaya.com/maxpeterkaya/amur:aio

This image includes the webp and ffmpeg libraries.

Webp

vc.maxkaya.com/maxpeterkaya/amur:webp

This image includes the webp library.

FFmpeg

vc.maxkaya.com/maxpeterkaya/amur:ffmpeg

This image includes the ffmpeg library.

Credits