Inside AI OPS: how to replace a background in 20 seconds without breaking the product
A look inside AI OPS: the async pipeline, Supabase, fal.ai, limits, polling and deployment on Caddy + systemd.
Contents
Why this is harder than "just replace the background"
At first glance the task sounds simple: take a product photo, remove the background, generate a new one. In practice, generic generators break logos, seams, textures and small text. For an online store, that is not an "ugly image" — it is a return, a lost customer and lost money.
AI OPS is not a image generator. It is a pipeline where the product stays untouched and only the environment changes. Here is how it works and which decisions mattered.
What the service does
The user uploads a product photo, picks or describes a scene and gets a new background. The demo is free, limited and available in three languages. Generation takes about 20 seconds and costs around $0.02 per run.
Under the hood: Next.js 16, Supabase, fal.ai and Telegram for lead notifications.
Architecture
Browser → Next.js API → Supabase → fal.ai → Telegram
- The browser sends a photo and a scene description.
- Next.js stores the request and the pipeline run in Supabase.
- fal.ai generates the new background.
- The result is saved to
pipeline_runs.result_url. - Telegram receives a new lead notification.
The key idea: the API does not wait for generation. It responds immediately and the work continues in the background.
Decision 1. A queue without extra infrastructure
For an MVP I did not want Redis, workers and a separate queue service. Next.js has after() — a function that runs after the response is sent:
after(async () => {
await processPipelineRun(runId);
});
The user receives a runId immediately while the pipeline keeps working. If generation fails, the status becomes error and the limit is not consumed.
Decision 2. Polling that survives a reload
The client polls /api/relight/[id] every two seconds. The runId is stored in localStorage, so even after a page reload the client can keep waiting for the result.
On a network error the client does not show "everything is broken" — it reconnects. A small detail that makes the product feel reliable.
Decision 3. Limits that do not reset on errors
The demo has limits: two generations per email and three per IP. An unsuccessful run should not consume an attempt. Otherwise a user hits a random error and can no longer try.
Limits are counted atomically through a keyed mutex, which protects against races when several requests arrive at once.
Decision 4. Three languages and production
The interface and legal pages are translated into en, ru and uk. The language is detected from the browser, and the manual choice is saved.
Production lives on the same server: Caddy handles HTTPS, systemd keeps Next.js running, a health check monitors the app, and deploys use an atomic .next swap with rollback on failure.
What I would improve next
- Move the pipeline to a separate worker if load grows.
- Add a queue with retries and deadlines.
- Show generation progress instead of only a spinner.
- Add a before/after comparison directly in the result.
Bottom line
AI OPS is an example of an AI product built not on a single model, but on the engineering around it: limits, failure recovery, result storage, localization and deployment. The model can be replaced. A reliable pipeline is harder.
Related
Automation without magic: 5 processes businesses still do by hand
Five processes you can automate without a huge budget: requests, reports, content, support and integrations.
ReadA CRM people actually use: 7 decisions before the first line of code
What to decide before building a CRM so the team uses it: processes, roles, data, integrations, MVP and metrics.
ReadNeed similar automation?
Message me on Telegram — let's discuss the task and the format.