{"id":30181084,"url":"https://github.com/jaredlunde/railway-image-service","last_synced_at":"2025-08-12T08:07:29.359Z","repository":{"id":269246359,"uuid":"906846601","full_name":"jaredLunde/railway-image-service","owner":"jaredLunde","description":"Upload, serve, and process images using Railway. Includes on-the-fly image resizing, cropping, automatic AVIF/WebP, and more.","archived":false,"fork":false,"pushed_at":"2024-12-29T21:24:07.000Z","size":299,"stargazers_count":4,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-09T02:20:57.211Z","etag":null,"topics":["blob-storage","image-processor","image-uploader","key-value-store","object-storage","railway","railway-template"],"latest_commit_sha":null,"homepage":"https://railway.com/template/MF8Rcp?referralCode=5hTSOZ","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jaredLunde.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-12-22T04:42:05.000Z","updated_at":"2025-07-09T16:11:11.000Z","dependencies_parsed_at":"2024-12-26T22:45:32.847Z","dependency_job_id":null,"html_url":"https://github.com/jaredLunde/railway-image-service","commit_stats":null,"previous_names":["jaredlunde/railway-images","jaredlunde/railway-image-service"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/jaredLunde/railway-image-service","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredLunde%2Frailway-image-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredLunde%2Frailway-image-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredLunde%2Frailway-image-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredLunde%2Frailway-image-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jaredLunde","download_url":"https://codeload.github.com/jaredLunde/railway-image-service/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jaredLunde%2Frailway-image-service/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270024697,"owners_count":24514054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blob-storage","image-processor","image-uploader","key-value-store","object-storage","railway","railway-template"],"created_at":"2025-08-12T08:06:46.325Z","updated_at":"2025-08-12T08:07:29.345Z","avatar_url":"https://github.com/jaredLunde.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Image Processing Service for Railway\n\nA self-hosted alternative to services like Cloudinary, Imgix, and others that helps you\nmove faster and pay less when you need to manage image content.\n\nUpload, serve, and process images on Railways. Includes on-the-fly image resizing, cropping, automatic AVIF/WebP, and more.\n\n[![Deploy on Railway](https://railway.com/button.svg)](https://railway.com/template/MF8Rcp?referralCode=5hTSOZ)\n\n## Features\n\n- [x] On-the-fly image processing (resize, crop, etc.) from any allowlisted domain or Railway volume\n- [x] Automatic AVIF/WebP conversion\n- [x] Uses [libvips](https://libvips.github.io/libvips/) for fast image processing\n- [x] S3-ish blob storage (PUT, GET, DELETE) protected by an API key\n- [x] Secure image serving with URLs protected by SHA256-HMAC signatures\n- [x] [React components](/js#react-api), [Node.js client](js#node-sdk), [URL builder](js#imageurlbuilder), and [Go client](client/) for easy integration\n\n## API\n\n### Authentication\n\nTo authenticate with your `SECRET_KEY`, use the `x-api-key` header:\n\n```sh\ncurl http://localhost:3000/blob/gopher.png \\\n  -H \"x-api-key: $IMAGE_SERVICE_SECRET_KEY\"\n```\n\nTo authenticate with signed URLs, first create a signed URL with your `SECRET_KEY` then\nuse the signed URL directly. This is extremely useful for allowing users to upload directly\nto your blob storage and to protect against attacks on your image processing endpoint.\n\nTo create the signed URL, prefix the URL you want to allow access to with `/sign/`.\n\n```sh\ncurl http://localhost:3000/sign/blob/gopher.png \\\n  -H \"x-api-key: $IMAGE_SERVICE_SECRET_KEY\"\n# -\u003e http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires=...\n\ncurl http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires=...\n```\n\nThe [Node](js/) and [Go](client/) clients do this for you and the signature\ncan be created locally if you provide the clients your `SIGNATURE_SECRET_KEY`. Again, take\nextra care _not to leak_ this key. For example, keep it and the Node.js client out of your\nfrontend bundle.\n\n### Blob storage API\n\nThis is an API for putting, getting, and deleting images in blob storage. You can let users\ndirectly `PUT` objects via this API, but you should do so with signed URLs and keep your API key\nabsolutely secret.\n\n| Method   | Path              | Description                                        |\n| -------- | ----------------- | -------------------------------------------------- |\n| `PUT`    | `/blob/:key`      | Upload a file                                      |\n| `GET`    | `/blob/:key`      | Get a file                                         |\n| `DELETE` | `/blob/:key`      | Delete a file                                      |\n| `GET`    | `/blob`           | List files with `limit`, `starting_at` parameters. |\n| `GET`    | `/sign/blob/:key` | Get a signed URL for a blob storage operation      |\n\n### Image processing API\n\nThis is your \"public\" API that processes and serves images from either blob storage or the Internet.\n\n| Method | Path                                 | Description                                                                        |\n| ------ | ------------------------------------ | ---------------------------------------------------------------------------------- |\n| `GET`  | `/serve/:operations?/blob/:key`      | Process an image in blob storage on the fly                                        |\n| `GET`  | `/serve/:operations?/url/:url`       | Process an image via HTTP on the fly                                               |\n| `GET`  | `/serve/meta/:operations?/blob/:key` | Get the metadata of an image in blob storage, e.g. dimensions, format, orientation |\n| `GET`  | `/serve/meta/:operations?/url/:url`  | Get the metadata of an image via HTTP, e.g. dimensions, format, orientation        |\n| `GET`  | `/sign/serve/:operations?/blob/:key` | Get a signed URL of an image in blob storage for an image processing operation     |\n| `GET`  | `/sign/serve/:operations?/url/:url`  | Get a signed URL of an image via HTTP for an image processing operation            |\n\n---\n\n## Configuration\n\nThe service can be configured by setting the environment variables below.\n\n| Environment Variable         | Description                                                                                                                                                                         | Default           |\n| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------- |\n| `MAX_UPLOAD_SIZE`            | The maximum size of an uploaded file in bytes                                                                                                                                       | `10485760` (10MB) |\n| `UPLOAD_PATH`                | The path to store uploaded files                                                                                                                                                    | `/data/uploads`   |\n| `LEVELDB_PATH`               | The path to store the key/value database                                                                                                                                            | `/data/db`        |\n| `SECRET_KEY`                 | The secret key used to for accessing the blob storage API                                                                                                                           | `password`        |\n| `SIGNATURE_SECRET_KEY`       | The secret key used to sign URLs                                                                                                                                                    |                   |\n| `SERVE_ALLOWED_HTTP_SOURCES` | A comma-separated list of allowed URL sources for image processing, e.g. `*.foobar.com,my.foobar.com,mybucket.s3.amazonaws.com`. Set to an empty string to disable the HTTP loader. | `*`               |\n| `SERVE_AUTO_WEBP`            | Automatically convert images to WebP if compatible with the requester unless another format is specified.                                                                           | `true`            |\n| `SERVE_AUTO_AVIF`            | Automatically convert images to AVIF if compatible with the requester unless another format is specified.                                                                           | `true`            |\n| `SERVE_CONCURRENCY`          | The max number of images to process concurrently.                                                                                                                                   | `20`              |\n| `SERVE_RESULT_CACHE_TTL`     | The TTL for the image processor result cache as a Go duration.                                                                                                                      | `24h`             |\n| `SERVE_CACHE_CONTROL_TTL`    | The TTL for the cache-control header as a Go duration.                                                                                                                              | `8760h` (1 year)  |\n| `SERVE_CACHE_CONTROL_SWR`    | The stale-while-revalidate value for the cache-control header as a Go duration.                                                                                                     | `24h` (1 day)     |\n| `ENVIRONMENT`                | The environment the server is running in. Either`production`or`development`.                                                                                                        | `production`      |\n\n### Server configuration\n\n| Environment Variable   | Description                                                                                 | Default   |\n| ---------------------- | ------------------------------------------------------------------------------------------- | --------- |\n| `HOST`                 | The host the server listens on                                                              | `0.0.0.0` |\n| `PORT`                 | The port the server listens on                                                              | `3000`    |\n| `REQUEST_TIMEOUT`      | The timeout for requests formatted as a Go duration                                         | `30s`     |\n| `CORS_ALLOWED_ORIGINS` | A comma-separated list of allowed origins for CORS requests, e.g. `https://your-domain.com` | `*`       |\n| `LOG_LEVEL`            | The log level for the server: `debug`, `info`, `warn`, and `error`.                         | `info`    |\n\n---\n\n## Docker Compose\n\nCreate a `docker-compose.yml` file with the following template:\n\n```yaml\nservices:\n  railway-image-service:\n    image: ghcr.io/jaredlunde/railway-image-service:latest\n    ports:\n      - \"8080:8080\"\n    volumes:\n      - ./data:/data\n    environment:\n      - PORT=8080\n      - SECRET_KEY=your_secret_key_here\n      - SIGNATURE_SECRET_KEY=your_signature_secret_key_here\n```\n\n---\n\n## Blob storage API examples\n\n### Upload an image\n\n```bash\ncurl -X PUT -T tmp/gopher.png http://localhost:3000/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n```\n\n### Upload an image using a signed URL\n\n```bash\n# Create a signed URL\ncurl http://localhost:3000/sign/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n# =\u003e http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\n\n# Upload the image\ncurl -X PUT -T tmp/gopher.png \"http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\"\n```\n\n### Get an image\n\n```bash\ncurl http://localhost:3000/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n```\n\n### Get an image using a signed URL\n\n```bash\n# Create a signed URL\ncurl http://localhost:3000/sign/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n# =\u003e http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\n\n# Get the image\ncurl \"http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\"\n```\n\n### Delete an image\n\n```bash\ncurl -X DELETE http://localhost:3000/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n```\n\n### Delete an image using a signed URL\n\n```bash\n# Create a signed URL\ncurl http://localhost:3000/sign/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n# =\u003e http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\n\n# Delete the image\ncurl -X DELETE \"http://localhost:3000/blob/gopher.png?x-signature=...\u0026x-expires==...\"\n```\n\n---\n\n## Image processing API examples\n\nThe image processing API uses [thumbor](https://thumbor.readthedocs.io/en/latest/usage.html#image-endpoint) URL syntax.\nThe endpoint is a series of URL parts that define the processing operations to be performed on the image.\n\n```\n/HASH|unsafe/trim/AxB:CxD/fit-in/stretch/-Ex-F/GxH:IxJ/HALIGN/VALIGN/smart/filters:NAME(ARGS):NAME(ARGS):.../IMAGE\n```\n\nSee the [imagor documentation](https://github.com/cshum/imagor/blob/e8b9c7c731a1ce65368f20745f5064d3f1083ac1/README.md#image-endpoint) for\na comprehensive list of examples.\n\n### Crop and resize an image from blob storage\n\n```bash\n# Create a signed URL\ncurl http://localhost:3000/sign/serve/300x300/blob/gopher.png \\\n  -H \"x-api-key: $API_KEY\"\n# =\u003e http://localhost:3000/serve/300x300/blob/gopher.png?x-signature=...\n\n# Process the image on the fly\ncurl http://localhost:3000/serve/300x300/blob/gopher.png?x-signature=...\n```\n\n### Crop and resize an image from a URL\n\n```bash\n# Create a signed URL\ncurl http://localhost:3000/sign/serve/300x300/url/github.com/railwayapp.png \\\n  -H \"x-api-key: $API_KEY\"\n# =\u003e http://localhost:3000/serve/300x300/url/github.com/railwayapp.png?x-signature=...\n\n# Process the image on the fly\ncurl http://localhost:3000/serve/300x300/url/github.com/railwayapp.png?x-signature=...\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredlunde%2Frailway-image-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjaredlunde%2Frailway-image-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjaredlunde%2Frailway-image-service/lists"}