Summary#
A deploy is a handover, not a compile — the only deploy there is, through the platform it names (osy deploy --env prod --server <platform>; without --server it is refused, and osy launch runs the app here instead). It builds your
application on your machine into a bundle — the app blob, carrying your sources, tests and every package pinned by its
hash, plus the package archives themselves — and hands it to the zone your environment runs on. The zone rebuilds
it under its own platform release and serves its own build. The control plane authorizes the deploy, records the version
and follows it; it never compiles your application and never receives its sources or its package bytes.
Description#
A deploy goes through these steps, each shown by osy deploy as it happens and recorded on the application's platform page:
| step | where it runs | what happens |
|---|---|---|
| build | your machine | the project is compiled exactly as the zone will compile it; a compile error stops here, before anything is sent |
| ask | control plane | your right to deploy is checked, the version is recorded, the zone chosen, and an upload grant for that zone's store issued — with the archives the zone does not hold yet |
| upload | the zone | the bundle, and only the archives the zone lacks, go to the zone's gateway under the grant |
| dry run | the zone | the zone rebuilds the version and plans its environment's database — nothing is written. A version that would leave a live workflow run with nowhere to go (its state renamed or removed, and no workflow migration saying where it goes) is refused here, naming each run and its state: add a Migrating runs that are still in flight to the deploy's --migration file, or end those runs with osy cancel-runs |
| backup | the zone | the environment's database is backed up, when it has one |
| move | the zone | the zone rebuilds again, stores its build as the environment's model, and brings the environment's database to it — creating that database, and every table the application stands on, when this is its first deploy |
| deploy | the zone | the version is deployed on the environment's platform release and served once the zone runs it |
A failure at any step leaves the environment serving what it served, and says why — on osy deploy, osy status, the
platform page, and in an email to the application's team.
On a terminal, osy deploy draws these as rows that update in place — Build the bundle here, Ask the platform to deploy it,
Upload the bundle to the zone, The zone builds and rolls it out (the dry run, backup, move and deploy, in the zone's own words),
and Its address serves the new version — each with what it is doing and its own clock, and its last word once it ends. In a pipe or
CI it prints one line per finished step instead. Either way every line is kept in a log under ~/.osyrin/logs/, whose path it
prints first. --json prints the progress on standard error and the result as one JSON line.
Why does the zone build it, and not my machine or the platform?#
Your machine's build is never trusted. It is a convenience that catches mistakes early. The zone compiles your application again from the sources and pins in the bundle, under the platform release the environment runs on, and serves that build. The bundle's model is recorded only by its hash.
The control plane never builds a user application. A platform that runs its applications in zones refuses
osy compile, osy test and the debugger for them by name, pointing at osy deploy. So an application's sources and its
native DLLs are only ever held by the zone it runs in — for a private zone, the organization's own. A platform with no zones
(osyrin dev, a self-hosted install) still compiles there while you develop — but no platform compiles an application to
deploy it: every deploy is this handover, and a platform with no zone to hand it to refuses the deploy by name.
The two hashes usually differ, and that is expected. Every deploy records the hash of the blob your machine built and the hash of the zone's build. They differ whenever your machine and the zone run different platform releases, or the zone compiled into the model your environment already serves. The difference is recorded and logged; it is never a refusal.
What happens on the first deploy to an environment?#
An environment has no database until something deploys to it, and that something is always the zone — the platform names the database when the first deploy reaches the environment, and never creates or migrates it itself. The first deploy's move creates the environment's database on the zone's data zone and applies the whole schema its version declares — the application's own tables and the platform's baseline ones (files, workflows, documents and the rest) — then records every statement in that database's own migration ledger. Later deploys apply only what changed, against that ledger.
Until that first build finishes the environment serves nothing, and the platform page says what it is waiting for. A first
deploy that fails leaves no half-made database: the move is one transaction, and what it had begun is rolled back. A
database the zone cannot create — a data zone whose credential may not create databases, say — fails the deploy like any
other step: the environment keeps serving nothing and says why, the operators are alerted, the team is emailed, and
osy deploy prints the zone's own words.
What does the upload grant allow?#
Exactly one upload, of one blob, for one version of one application, into one zone's store, for fifteen minutes:
| the zone refuses an upload when | answered |
|---|---|
| the token is not the grant's | 403 |
| the grant is for another zone | 403 |
| the grant expired, or was used | 403 |
| the bundle's blob is not the one the grant names | 422 |
| the bundle carries a package the version does not pin, or at another version | 422 |
| an archive's bytes do not hash to its pin | 422 |
| a package the version pins is neither carried nor already held by the zone | 422 |
The grant is spent before anything is stored, so a second upload under it stores nothing, and a refused upload leaves it usable. A grant nobody uses expires, and the deploy fails with that reason. Nobody but the platform writes a grant — an operator cannot extend one or reset a used one.
Where is my deploy built?#
In a build pod of its own. The zone starts a builder of the release your environment runs — or keeps a few warm, when its operators chose to — and every build runs there in a fresh process, started for that build and stopped when it answers. A build that runs out of memory fails only itself, with that reason; it never takes down the worker your applications' background work runs on, and nothing of one build is in memory for the next. A builder that has finished lingers a few minutes for the next build in line, then goes.
A version is built once per platform release. When the platform releases a new version, the zone builds every active application for it in the background — compiling it and planning its schema change from the two models alone, without touching your database — and keeps that build: the move to the release is served it and compiles nothing. A version that does not build on the new release stays on the one it runs, and the operators are told why.
Why is my deploy waiting?#
A zone builds a limited number of deploys at once — its builders, or on a zone without them its compilation slots,
derived from the memory its workers have or set by a platform operator. A deploy past that number waits in an ordered
queue rather than overloading the zone, and osy deploy and the platform page show its place:
billing 1.4.0 → prod: dry run — queued on zone eu-north-1 at position 2 for one of 1 compilation slot(s)
billing 1.4.0 → prod: dry run — building on zone eu-north-1, one of 1 compilation slot(s)The queue has three classes, served in order: the platform's own work first (an operator's move), then what people asked for (your deploys and moves), then the background (a release's pre-builds). Your deploy never waits for a pre-build: a background build that is running is stopped to make room, and built again once the zone is quiet. A build that writes — a move migrating a database — is never stopped.
A deploy that waits longer than twenty minutes for a slot fails with that reason; deploy it again once the zone is less busy, or ask an operator for more slots on the zone.
Can one deploy move my other environments too?#
Yes — that is deploy-follow. An environment can be set to follow the deploys of its application's other environments, so a single
osy deploy reaches it without a second command:
| the environment follows | what a deploy elsewhere does to it |
|---|---|
Never (the default) | nothing; only a deploy of its own moves it |
PatchVersions | a deploy of a patch version moves it too; a minor or major version still needs osy deploy --env |
EveryVersion | every version a deploy brings is deployed here too |
A follower is moved from the bundle already in its zone's store — nothing is uploaded again, and nothing is built on the control
plane. It gets its own move, so it is dry-run, backed up, built into the model it serves and migrated against its own database, and
it can fail on its own without touching the environment you deployed to. Its deployment says what moved it ("follows the deploy of
1.4.0 to Production") on the application's page and in osy status, and only a failure is emailed: you watched the deploy that
started it succeed.
An environment that needs an approval is never moved by a follow, whatever its setting says.
A follower that cannot be moved says so. The bundle lives in one zone's store, so an environment placed on another zone has
nothing to build from — and neither has the control plane, which holds no copy of it. That environment is not silently left behind: it
raises an operator alert ("Environment did not follow a deploy") naming the environment, why, the version it goes on serving, and the
command that fixes it — osy deploy --env <that environment> from the machine that built it. The same happens when a move of that
environment is already in flight. The alert closes as soon as a deployment into that environment converges.
What about native packages?#
A package that ships native .NET code runs only on dedicated hosting — an organization's private zone. The zone checks this when it rebuilds: on a shared zone the deploy fails naming the package and shared tenancy, however the bundle was built. On a private zone the package's assemblies are checked against this platform's seams before anything runs.
What does the control plane keep about my application?#
Its version, both hashes, the diagnostics the zone reported, the packages it pins by hash, and a short summary of what the zone's build declares — its entity, function and page names — so the platform's pages and alerts can say what failed. It keeps no source, no package archive and no model.
See also#
Deploying your app to an environment — the osy deploy and osy status verbs.
Upgrading an application to a newer platform release — moving an environment to a newer platform release, which the zone also builds.