v0 · Plan & Architecture · Ready for review

A $0/month Cloudinary — on your VPS, with SVG & 3D built in.

UQ Cloud is a self-hosted Cloudinary alternative built entirely on free tiers: your Linux VPS runs the API + workers, Supabase handles DB & storage & auth, Cloudflare fronts everything with free CDN, WAF, and unlimited bandwidth.

8
Phases
$0
Monthly cost
1
Linux VPS
CF bandwidth
01 · Research

What Cloudinary actually does

Cloudinary is best understood as five stacked products sold as one. Any credible self-hosted alternative has to cover the same surface area — even if v1 only ships a subset.

Upload & Ingest

  • Signed & unsigned uploads, direct-to-storage, chunked uploads (up to 100GB)
  • Remote fetch by URL, S3/GCS ingest, browser & mobile SDKs
  • Upload presets, eager transformations, auto-tagging on ingest

Media Library (DAM)

  • Folders, tags, contextual metadata, structured metadata schemas
  • Full-text + metadata search, saved searches, collections
  • Access modes: public, authenticated, private (signed URL)

Transformations

  • URL-based: crop, resize, format (f_auto), quality (q_auto), effects
  • AI: background removal, generative fill, upscale, smart crop (g_auto)
  • Video: transcode, adaptive streaming (HLS/DASH), thumbnails, subtitles

Delivery & CDN

  • Multi-CDN (Akamai/Fastly), edge caching, signed URLs, hotlink protection
  • Responsive breakpoints, client hints, Save-Data, DPR-aware URLs
  • SDKs: JS, React, Vue, Next, iOS, Android, Node, Python, PHP, Ruby, .NET

Analytics & Ops

  • Bandwidth, storage, transformation quotas & alerts
  • Access logs, audit trail, webhook notifications
  • Add-ons marketplace (AWS Rek, Imagga, Google Vision, etc.)
02 · Architecture

System design at a glance

Clients
Web / React SDK
Mobile / Node SDK
<model-viewer>
Edge
CDN (Cloudflare / Bunny)
Signed-URL check
Cache · Vary: Accept
API
Upload (TUS / REST)
Transform router
Admin + Auth
Workers
sharp / libvips
SVGO + resvg
gltf-transform + three
ffmpeg (P6)
State
MinIO (originals)
MinIO (derivatives)
Postgres + pgvector
Redis / BullMQ
Request flow: Client → CDN → API → (cache hit? MinIO derivatives) : (Worker → sharp/resvg/gltf → MinIO → return)
LayerPickWhy
Edge / CDNCloudflare Free (proxy + cache + WAF + Bot Fight)Free unlimited bandwidth in front of your VPS; caches derivatives so origin barely gets hit
TLS / Reverse ProxyCaddy on VPS (auto-HTTPS via Cloudflare Origin cert)Zero-config HTTP/2+3, one Caddyfile
Signed URLs at edgeCloudflare Worker (Free: 100k req/day)Verify HMAC signatures before hitting origin — blocks abuse for free
API + WebNode.js (Fastify) on VPS, single Docker containerStreaming + TUS uploads on a $5 VPS; add workers later
Object StorageSupabase Storage (Free: 1 GB) → Cloudflare R2 (Free: 10 GB + zero egress) once you outgrow itR2's zero egress is the killer feature for a media host — no bandwidth bill ever
Metadata DBSupabase Postgres Free (500 MB, pg_trgm + pgvector included)Managed Postgres, auth, and RLS in one — no DB to babysit on the VPS
AuthSupabase Auth (email + OAuth) for dashboard; HMAC API keys for programmaticFree, RLS-enforced; API keys stored in a Supabase table
Queue / CachePostgres-backed queue (pg-boss) + in-memory LRU on VPSSkip Redis entirely on free tier — pg-boss uses your existing Supabase DB
Image Pipelinelibvips via sharp on VPS worker10× faster + lower memory than ImageMagick; runs fine on 1 vCPU
SVG PipelineSVGO (sanitize) + resvg-js (rasterize)Pure npm, no native deps beyond prebuilt binaries
3D Pipelinegltf-transform + Draco/Meshopt + KTX2; thumbnail via headless-gl or screenshot from <model-viewer>All Node/WASM — runs on the same VPS worker
AnalyticsCloudflare Web Analytics (Free) + Supabase logs tableBandwidth + hit counts without paying for a metrics stack
PackagingSingle docker-compose.yml on the VPS (api + worker + caddy)One `docker compose up -d` on a fresh Ubuntu box
03 · Budget

$0/month build — the free-tier stack

Everything below stays inside free tiers of Cloudflare, Supabase, and your existing Linux VPS. No credit card required, no egress surprises.

ServiceWhat you get freeFree-tier limit
Cloudflare FreeUnlimited bandwidth, CDN, WAF, Bot Fight, Web Analytics100k Worker req/day, 3 Page Rules
Supabase FreePostgres 500 MB, Auth, Storage 1 GB, 2 GB egress/mo, pgvectorPauses after 7 days of inactivity — cron a keep-alive
Your Linux VPSAPI + workers + Caddy (sharp / SVGO / gltf-transform / ffmpeg)1 vCPU / 1 GB RAM is enough for P1–P3; swap on for safety
Cloudflare R2 (optional)10 GB storage + zero egress fees, S3-compatible1M Class-A + 10M Class-B ops/mo free
GitHub Actions FreeCI + SSH deploy to VPS2000 min/mo on public repos = unlimited
UptimeRobot Free50 monitors, 5-min checks, email alertsNo SMS on free
Bandwidth trick: serve every derivative through Cloudflare's cache with Cache-Control: public, max-age=31536000, immutable. After the first request, your VPS never sees the traffic again — Cloudflare eats it for free.
04 · Roadmap

Eight phases, twenty weeks

Each phase ends in a shippable milestone. You can stop after P4 and have a real Cloudinary alternative for images, SVG, and 3D — the later phases are additive.

  1. P0
    Week 0
    Foundations (Free-tier setup)
    • Create Supabase project (Free): DB + Auth + Storage bucket 'originals'
    • Point domain to Cloudflare (Free plan), enable Full-Strict TLS + Origin cert on VPS
    • Ubuntu VPS: install Docker + Caddy; open only 80/443, harden SSH
    • Monorepo (pnpm): apps/api, apps/web, apps/worker, packages/sdk
    • Supabase migrations: users (auth), api_keys, assets, folders, tags, derived_assets, jobs — with RLS policies + GRANTs
    • GitHub Actions: build & push single image, SSH deploy to VPS (docker compose pull && up -d)
    Ship: Domain → Cloudflare → Caddy → API container talking to Supabase; $0/month
  2. P1
    Weeks 1–3
    Ingest + Media Library MVP
    • Direct-to-Supabase-Storage uploads via signed upload URLs (bypasses your VPS bandwidth)
    • TUS resumable proxy for large files; MIME sniffing + content-hash dedupe
    • Folders, tags, contextual metadata, JSON schema for structured metadata
    • Dashboard: upload, grid/list library, search, rename, move, delete, restore
    • API keys (HMAC-signed requests, Cloudinary-compatible signature) stored in Supabase with RLS
    Ship: Upload a file → it lands in Supabase Storage → public URL served via Cloudflare cache
  3. P2
    Weeks 4–6
    URL-based Image Transformations
    • Parser for /image/upload/<transforms>/<publicId>.<ext> (Cloudinary-compatible subset)
    • sharp pipeline: w,h,c_fill,c_fit,c_pad,g_auto (smart crop), r_ (radius), e_blur/sharpen, f_auto, q_auto
    • Derived-asset cache: write to Supabase Storage bucket 'derivatives' + Cloudflare edge cache (Cache-Control: public, immutable, max-age=31536000)
    • Stampede protection with in-process singleflight (no Redis needed)
    • Signed transformations, allow-listed transforms per API key
    • Responsive breakpoint generator + srcset helper in SDK
    Ship: /t/w_400,c_fill,f_auto,q_auto/photo.jpg — first hit renders on VPS, every hit after is a Cloudflare cache HIT (free bandwidth)
  4. P3
    Weeks 7–8
    SVG + 3D First-Class Support
    • SVG: SVGO sanitize on upload (strip <script>, event handlers, external refs), CSP-safe serving
    • SVG → PNG/WebP raster via resvg for og:image, thumbnails, favicons
    • 3D upload: .glb, .gltf(+bin+textures), .usdz, .fbx (convert to glb), .obj
    • gltf-transform pipeline: Draco / Meshopt compression, KTX2/Basis texture transcoding
    • Auto thumbnail: headless-gl + three.js on VPS worker → PNG poster stored in Supabase
    • Delivery: /model/<publicId>.glb?compress=draco&textures=ktx2 + <model-viewer> embed snippet
    Ship: Drop a 40MB glTF → get an 8MB Draco/KTX2 GLB + poster + embeddable viewer URL
  5. P4
    Weeks 9–11
    Cloudflare Edge, Signed URLs & SDKs
    • Cloudflare Cache Rules: cache /t/* + /model/* aggressively, bypass /api/*
    • Cloudflare Worker (Free tier): verify HMAC signed URLs at the edge before request reaches VPS
    • Cloudflare WAF rules + Bot Fight Mode; per-key rate limit via Worker + Supabase counter
    • Hotlink protection (Referer allowlist per API key) — enforced in Worker
    • SDKs: @uqcloud/js (framework-agnostic URL builder), @uqcloud/react (<Image>, <Model>), Node server SDK, Python
    • Next.js image-loader adapter for drop-in migration
    Ship: npm i @uqcloud/react → <Image src='photo.jpg' width={800} /> serves optimized from your domain
  6. P5
    Weeks 12–14
    AI & Advanced Editing (still free)
    • Background removal via @imgly/background-removal-node (ONNX, CPU) as e_bg_removal
    • Upscale (Real-ESRGAN ONNX, CPU) as e_upscale — slow but free
    • Auto-tag & caption via CLIP + BLIP ONNX; embeddings in Supabase pgvector for semantic search
    • Smart crop (saliency map) for g_auto
    • Optional generative fill via pluggable provider (bring-your-own API key)
    Ship: Search 'red running shoes on grass' returns matching assets by embedding
  7. P6
    Weeks 15–17
    Video Pipeline (optional)
    • ffmpeg on VPS worker, per-preset transcode profiles, poster + sprite thumbnails
    • HLS/DASH packaging with shaka-packager, ABR ladder
    • Store HLS segments in Supabase Storage (or R2 when you migrate); Cloudflare caches .ts segments for free
    • URL params: so_ (start), du_ (duration), w/h/c, f_auto (mp4/webm)
    Ship: Upload MP4 → HLS master playlist + posters; playback via Cloudflare cache
  8. P7
    Weeks 18–20
    Ops, Hardening & Scale-up path
    • Multi-workspace, roles (owner/admin/dev/viewer) via Supabase RLS + user_roles table
    • Usage metering (storage, bandwidth from CF Analytics API, transforms), quota alerts, webhooks
    • Backup: nightly Supabase pg_dump → encrypted upload to R2; Storage snapshot job
    • Migration guide: Supabase Storage → Cloudflare R2 (Free 10GB, zero egress) when >1GB
    • Threat model: SSRF on remote-fetch, SVG XSS, ImageTragick-class fuzz, path traversal
    • Uptime: UptimeRobot Free + Cloudflare Health Checks
    Ship: Production-hardened single-VPS deploy with backups, quotas, and a clear path to R2 when free tier fills up
05 · Risks

What will bite you

SVG XSS via inline scripts / foreign objects
Sanitize with SVGO + DOMPurify server-side; serve with strict CSP and Content-Disposition when needed
SSRF via remote-fetch uploads
Resolve DNS in-app, block RFC1918/link-local, allowlist schemes, cap size & time
Zip-bomb / decompression on 3D files
Stream + size caps, sandboxed worker (nsjail/gVisor), reject nested archives
Derived-asset cache blowup
Signed & allow-listed transform params; LRU eviction on MinIO tier + Redis TTL
Bandwidth abuse / hotlinking
Referer allowlist per key, signed URLs, per-key bandwidth quota + 429
06 · Next

Ready to build P0?

When you say go, I'll scaffold the monorepo, docker-compose stack, and the database schema from P0 — then move into the P1 upload + media library MVP.