Auto-scaling SVT Encore on Open Source Cloud
SVT Encore is a solid open source transcoding service, and it runs well on Open Source Cloud. The standard OSC deployment is a single Encore instance — straightforward to set up and fine for steady, sequential workloads. When demand is more variable you want capacity that scales with the queue: more instances when jobs pile up, fewer when they clear, without you having to manage any of it by hand.
Open Videocore is an open source, API-first media asset management middleware that runs on Open Source Cloud. One of the things it does is add an auto-scaler on top of the standard Encore OSC deployment, handling the instance lifecycle and callback-listener wiring automatically. This post is about that piece specifically: what it does, how to get it running, and how to point an existing Encore integration at it without rewriting anything.
What the auto-scaler actually does
It polls the job queue every 10 seconds. When there’s pending work and every running instance is busy, it spawns one new Encore instance. One per tick, not a stampede — the pool grows gradually as demand holds, which keeps you from firing up ten instances to clear a queue that a second instance would have drained on its own.
Each Encore instance it spawns gets its own paired eyevinn-encore-callback-listener, configured with exactly that instance's URL. So callbacks route to the right place without any manual wiring.
When an instance goes idle and stays idle past the timeout — 5 minutes by default — the scaler tears it down. The pool has an upper bound, ENCORE_MAX_INSTANCES, which defaults to 3. You can change all of this at runtime without a restart, which we'll get to below.
That’s the whole idea: queue grows, capacity grows; queue empties, capacity goes away.
Getting it running on OSC
The fastest way to get started is through an AI agent connected to OSC via MCP. The agent handles service provisioning through natural language so you don’t have to look up service IDs, thread URLs between steps, or touch the OSC CLI at all.
Step 1 — Connect your agent to OSC
For Claude Code or Claude Desktop, add the OSC MCP server:
claude mcp add --transport http osc https://mcp.osaas.io/mcpFor Cursor, VS Code, or other MCP-compatible tools, add https://mcp.osaas.io/mcp as an MCP server and set your OSC Personal Access Token (from app.osaas.io/dashboard/profile/api) as the Bearer token. Full setup guides for each tool are at osaas.io/mcp.
Step 2 — Set up a parameter store
Open Videocore uses a parameter store to keep track of the backing services it provisions. Ask your agent to create one:
Set up an app-config parameter store called
ovcconfigfor my Open Videocore deployment.
The agent provisions Valkey and the config service, then returns a config API key. Keep it — you’ll need it in the next step.
Step 3 — Deploy Open Videocore
Create a Personal Access Token for the Open Videocore instance, then create an Open Videocore instance called
ovctest. Connect it to the parameter store namedovcconfigusing the API key from the previous step. Use the Personal Access Token as the OSC access token. Generate strong passwords forMinioRootPasswordandCouchdbAdminPassword.
The agent provisions the instance and returns its public URL.
Step 4 — Provision a media stack
A single API call stands up the backing services for a workspace: MinIO for object storage, CouchDB for asset metadata, Valkey for the job queue, and a packager. Encore instances are not created here — the auto-scaler spins them up on demand when the first jobs arrive.
curl -X POST https://<your-instance>/api/v1/provision \
-H "Content-Type: application/json" \
-d '{"name": "mystack"}'Provisioning runs asynchronously. The response gives you an operation ID you can poll:
{"operationId": "01KWYG...", "name": "mystack", "status": "pending"}curl https://<your-instance>/api/v1/provision/operations/<operationId>When status reaches "done" the stack is ready.
Step 5 — Bootstrap transcoding profiles
curl -X POST https://<your-instance>/api/v1/profiles/bootstrapThis seeds the profile store from the default Eyevinn Encore test profiles. Once it’s done, GET /api/v1/profiles lists what you have.
That’s the setup. From here you can send jobs.
Moving an existing Encore integration over
If you already POST jobs straight to an Encore OSC instance, you can repoint at Open Videocore with a base URL change and nothing else. There’s a compatibility endpoint that accepts the native Encore payload and returns Encore-shaped responses.
Here’s the only thing that changes:
# Before: direct to Encore
POST https://your-encore-instance.encore.prod.osaas.io/encoreJobs# After: via Open Videocore (same payload)
POST https://<your-instance>/api/v1/encore/encoreJobsThe request body is the same as native Encore:
{
"externalId": "my-job-001",
"inputs": [{ "uri": "s3://my-bucket/source.mp4" }],
"outputFolder": "s3://my-bucket/output/",
"profile": { "name": "program" }
}And the response is the shape you already parse:
{
"id": "default__job-abc123",
"externalId": "my-job-001",
"status": "QUEUED"
}You poll status the same way, too:
GET https://<your-instance>/api/v1/encore/encoreJobs/default__job-abc123Status values follow Encore’s conventions: QUEUED, IN_PROGRESS, SUCCESSFUL, FAILED. Your existing polling loop keeps working.
The difference is what happens behind that URL. Instead of one Encore instance taking your jobs in order, the scaler is watching the queue and adding capacity when your submissions outpace it. Your code doesn’t know or care — it’s still talking to something that looks like Encore.
The native asset-based API
If you’re building something new rather than migrating, there’s an asset-oriented API that treats a video as a first-class thing you ingest once and act on.
Ingest a video from a URL:
curl -X POST https://<your-instance>/api/v1/assets/ingest-url \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/clip.mp4", "name": "my-clip"}'That returns an asset record:
{"id": "01KWYGGAFB0NJCC6ZGY00K5C9B", "name": "my-clip", "status": "ready"}Transcode it by referencing the asset id and a profile:
curl -X POST https://<your-instance>/api/v1/assets/01KWYGGAFB0NJCC6ZGY00K5C9B/transcode \
-H "Content-Type: application/json" \
-d '{"profile": "program"}'That returns a job reference:
{"jobId": "01KWZX5HBA0NJCC6ZGY00K5C9B", "encoreJobId": "default__job-abc123"}Check the job using the jobId from that response:
curl https://<your-instance>/api/v1/jobs/01KWZX5HBA0NJCC6ZGY00K5C9BSame scaler underneath. Whether you come in through the Encore-compatible endpoint or the asset API, jobs land on the same queue and the same pool works through them.
There is also a basic operations UI at /ui on any running instance — useful for browsing assets, inspecting job state, and watching the scaler during development. It is intentionally minimal. Open Videocore is designed as an API-first building block: the right place to invest your UI effort is in whatever product you are building on top of it, not here.
Watching and tuning the pool
You can see what the scaler is doing at any time:
curl https://<your-instance>/api/v1/scaler/status{
"scalerActive": true,
"maxInstances": 3,
"idleTimeoutMs": 300000,
"workspaces": [{
"workspaceId": "default",
"queueDepth": 0,
"inflightDepth": 0,
"instances": [{
"instanceId": "scalerdefaultabc123",
"url": "https://scalerdefaultabc123.encore.prod.osaas.io",
"activeJobs": 0,
"lastIdleAt": 1783439226375
}]
}]
}queueDepth is what's waiting, inflightDepth is what's running, and each entry under instances is a live Encore instance with its current job count and the timestamp it last went idle.
If the defaults don’t fit your workload, change them at runtime — no restart:
curl -X PATCH https://<your-instance>/api/v1/scaler/config \
-H "Content-Type: application/json" \
-d '{"maxInstances": 5, "idleTimeoutMs": 120000}'The config accepts maxInstances, minInstances, and idleTimeoutMs. Raise maxInstances before a known busy period and lower it after. Shorten idleTimeoutMs if you'd rather reclaim capacity aggressively, or lengthen it if your jobs arrive in bursts with gaps and you don't want to pay the cold-start cost each time. The minimum for idleTimeoutMs is 10000 (10 seconds).
This is an early version
Open Videocore is young. The auto-scaler works and we run it, but the project is still finding its edges. The asset API is smaller than we want it to be, some of the OSC integrations have rough spots we’re still smoothing out, and there are parts of the roadmap — richer metadata, search, delivery — that aren’t built yet.
We’re putting this out now because it already does something useful, and because the fastest way to learn what’s missing is to have people use it and tell us. Expect things to change. If you build on it, pin a version and read the release notes before you upgrade.
Where to go from here
The project is open source — the code, the service definitions, and the setup are all in the repo, and you can deploy it from the OSC service page. If you’re already running Encore on OSC, the migration is a base URL change; try it against a test job and watch the scaler status while you submit a batch. That’s the fastest way to see whether it fits how you work.
If you hit something that doesn’t behave the way this post describes, open an issue. We’d rather hear about it.
What is Open Source Cloud?
Managed open-source cloud for developers who want infrastructure, not DevOps
Open source as a service: run 183+ unmodified open-source services. Deploy your own code alongside them as a My App. Spin up Postgres, Valkey, and S3-compatible storage, push your Next.js app, done. No Kubernetes, no cloud config.
