
Deploy Openinary | (Just Updated) Cloudinary Alternative, Image and Video API with Admin Login, Data Survives Redeploys
Openinary: self-hosted image and video transform API, admin login preset
openinary
Just deployed
/data
Deploy and Host Openinary on Railway
Built for an LLM agent to deploy and use. There is no setup form and no /setup page to open: the admin account is created from generated variables on first boot, and the contract below lists every address, variable name and one script that signs in, mints an API key, uploads an image and fetches a transformed copy.
Openinary is an open source, self-hosted alternative to Cloudinary. You upload an image or video once and request any size, crop, format or quality by editing the URL (/t/w_800,f_webp/photo.jpg); results are cached, and videos are transcoded through a background queue with ffmpeg.
About Hosting Openinary
This template runs one service: the official openinary/openinary v0.7.0 full-stack image, pinned by digest, with a volume on /data. The volume holds the SQLite database (users, sessions, API keys), the uploaded originals and the transformation cache, so all three survive redeploys. The image's nginx listens on a fixed port 3000; the start command moves it to Railway's injected PORT so the healthcheck and the public domain reach it.
The dashboard is behind a login. The admin user is admin@openinary.example.com and the password is OPENINARY_ADMIN_PASSWORD, generated per deploy. The account is created on the first boot only, while the user table is empty, so changing the variable later does not change the password; change it in the dashboard instead. A first API key is also printed once in the deploy logs, but you do not need to read logs: the script below signs in and mints a key over HTTP.
Files are stored on the volume. To use S3-compatible storage instead, add the STORAGE_* variables from the Openinary documentation to the service.
Agent contract
- Public URL:
OPENINARY_PUBLIC_URL(https://plus the service'sRAILWAY_PUBLIC_DOMAIN). The dashboard is at/, the API under/api. - Private URL:
http://openinary.railway.internal:8080, exposed asOPENINARY_PRIVATE_URL. - Credentials:
OPENINARY_ADMIN_PASSWORD(password of the admin useradmin@openinary.example.com),BETTER_AUTH_SECRETandAPI_SECRET(server-side signing secrets; clients never send them). - Auth: sign in with
POST /api/auth/sign-in/email(JSONemail,password, headerOriginequal to the public URL) to get a session cookie; with that cookiePOST /api/api-keys/createreturns an API key once. Every upload call then sendsAuthorization: Bearer API_KEY. Reads of transformed files (/t/...) are public. - Health:
GET /healthreturns 200 without credentials. - API:
POST /api/upload(multipartfiles, optionaltransformations),GET /t/TRANSFORMS/PATH,GET /api/storage,GET /api/api-keys/list,POST /api/invalidate.
# run with OPENINARY_PUBLIC_URL and OPENINARY_ADMIN_PASSWORD set
U="$OPENINARY_PUBLIC_URL"
curl -s -o /dev/null -w "health: %{http_code}\n" "$U/health"
curl -s -c /tmp/oi.jar -o /dev/null -w "sign-in: %{http_code}\n" -H "Origin: $U" -H "Content-Type: application/json" \
-d "{\"email\":\"admin@openinary.example.com\",\"password\":\"$OPENINARY_ADMIN_PASSWORD\"}" "$U/api/auth/sign-in/email"
KEY=$(curl -s -b /tmp/oi.jar -H "Origin: $U" -H "Content-Type: application/json" -d '{"name":"agent"}' "$U/api/api-keys/create" | sed -n 's/.*"key":"\([^"]*\)".*/\1/p')
printf '%s' 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==' | base64 -d | tee /tmp/oi.png | wc -c
curl -s -H "Authorization: Bearer $KEY" -F "files=@/tmp/oi.png;filename=probe.png" "$U/api/upload"; echo
curl -s -o /dev/null -w "upload without key: %{http_code}\n" -F "files=@/tmp/oi.png;filename=probe.png" "$U/api/upload"
curl -s -o /tmp/oi.webp -w "transform: %{http_code} %{content_type}\n" "$U/t/w_64,f_webp/probe.png"
A fresh deploy prints health: 200, sign-in: 200, 70 (the probe image size), an upload response with "success":true, upload without key: 401 and transform: 200 image/webp.
Why Deploy Openinary on Railway?
Railway is a singular platform to deploy your infrastructure stack. Railway will host your infrastructure so you don't have to deal with configuration, while allowing you to vertically and horizontally scale it.
By deploying Openinary on Railway, you are one step closer to supporting a complete full-stack application with minimal burden. Host your servers, databases, AI agents, and more on Railway.
- Agent-ready - generated admin password, contract block and one working script; no setup page.
- Login on from the first request - the dashboard and the upload API are never public and unauthenticated.
- State survives redeploys - users, API keys, originals and the cache live on one volume.
- A working port - the image's fixed port 3000 is moved to Railway's injected port, so the healthcheck passes.
- No per-image bill - transformations run on your own service instead of a metered media API.
Common Use Cases
- Image CDN for an app - upload once, request resized and reformatted copies by URL.
- Video thumbnails and transcodes - queue ffmpeg variants for uploaded videos.
- Cloudinary migration - keep the URL-parameter model on infrastructure you own.
Dependencies for Openinary Hosting
- One Railway volume on
/data, created by the template.
Deployment Dependencies
Implementation Details
| Variable | Purpose |
|---|---|
OPENINARY_ADMIN_PASSWORD | Password of the admin user, read on the first boot only. Generated per deploy. |
BETTER_AUTH_SECRET | Signs sessions and tokens. Generated per deploy. |
API_SECRET | Signs signed URLs and presigned uploads. Generated per deploy. |
BETTER_AUTH_URL | Public base URL used for auth callbacks and CORS. |
OPENINARY_PUBLIC_URL | HTTPS address for clients outside Railway. |
OPENINARY_PRIVATE_URL | Address on the private network, for services in the same project. |
The start command folds /app/data, /app/apps/api/public and /app/apps/api/cache onto the volume with symlinks, moves nginx to PORT, sets the admin email and asks for an initial API key, then runs the image's own start sequence.
Template Content
openinary
openinary/openinary:v0.7.0