Skip to main content

Rules for Deploying Applications

How an application gets onto UIS. For how a platform service gets packaged, see Rules for UIS Deployment System — that is a different thing with a different lifecycle, and conflating them is the mistake this document exists to prevent.

Decided: 2026-09-07 by Terje, with the UIS maintainer. Supersedes nothing; this is the first time the question has been answered in writing.


The rule

uis provisions. ArgoCD deploys. The seam is uis configure, which writes a Kubernetes Secret that the application's own manifest reads.

Both mechanisms are permanent. This is not a transition where one replaces the other later.

OwnerOwnsWhy it cannot be the other one
uisdatabases, roles, secrets, and platform-service configuration — a Dagster code location, a PostgREST instanceNone of these is a Kubernetes manifest in the application's repository. A code location is a Helm values overlay; a PostgREST instance is a generated password. ArgoCD has no object to reconcile
ArgoCDthe workloadDeployment, Service, Ingress, in the application's own repo under manifests/Reconciling a running workload against git is exactly what it is for, and uis argocd register already does it

The test for any application

Does it bring its own workload, or does it configure platform services to run its code?

⚠️ The answer is per-object, not per-application. One application can legitimately need both, and the first real one does.


Worked example: Atlas uses both, and that is correct

Atlas is a data platform: an ingest pipeline plus a read-only API plus a frontend.

PartPathMechanism
database + migrationsuisuis configure postgresql --app atlas --init-file -
secret in the dagster namespaceuissame call, --namespace dagster --secret-name-prefix atlas-database
ingest pipelineuisa Dagster code location.uis.extend/dagster-code-locations.yaml + uis deploy dagster
read-only APIuisuis configure postgrest --app atlas + uis deploy postgrest --app atlas
frontendArgoCDuis argocd register atlas-frontend <repo>

The pipeline has no workload of its own — Atlas ships an image and Dagster launches it. There is nothing for ArgoCD to deploy. The frontend is an ordinary Deployment, so uis should not be involved.

This follows directly from the requirement Atlas was given: "atlas must use the dagster and postgres services that are in UIS." An application told to use platform services is, by definition, on the uis path for those parts.


What ArgoCD already gives you, and what it does not

uis argocd register creates an Application (argocd-register-app.yml:285-311):

source:      { repoURL: <repo>.git, targetRevision: HEAD, path: manifests }
syncPolicy:
automated: { prune: true, selfHeal: true }
syncOptions: [ CreateNamespace=true ]

Drift detection and self-heal are on by default. A hand-edited cluster object is reverted to what git says. You do not have to ask for this.

No image-tag following. There is no argocd-image-updater in this repository. targetRevision: HEAD follows a git ref, not a registry tag — so a new image means a commit to the application's repo, not an automatic rollout.

No application secrets. The register path creates a GitHub credentials secret for private repos and nothing else. An application's own secrets come from uis configure — see the limit below.


🔴 The limit, stated plainly

A cluster rebuilt from git alone would come up with no application secrets.

uis configure mints a password that UIS deliberately does not store (configure-postgresql.sh:237"UIS does not store this"). That step is imperative and lives outside any repository. So the GitOps story is partial by construction, and nobody should be promised otherwise.

Closing this is the point of per-workload named secrets — item 1 of ANALYSIS-nais-uis §4. Until it lands, "it is all in git" is false for anything with a credential.


Why UIS is not growing an Application type

NAIS answers this question a third way that neither mechanism above matches: the developer declares provisioning and workload in one manifest, and an operator (naiserator) reconciles both. That collapses the seam entirely, and it is the reason NAIS can rebuild from a declaration.

It is deferred, and the reasoning is not new — ANALYSIS-nais-uis §4 ranks a UIS Application manifest last of thirteen, at L effort, behind items 1, 3 and 5, with the note that "NAIS built nais.yaml on top of capabilities it already ran; UIS would be building the declaration first."

⚠️ And it would not have solved the first real application. NAIS has two workload kinds, Application and Naisjob, and both run the application's own pods. There is no NAIS concept for "hand my image to a shared orchestrator that launches it." Atlas's shape exists because UIS made Dagster a shared platform service with tenants — a UIS invention with no NAIS analogue. So an Application type modelled on NAIS would not cover Atlas-shaped applications, and the template mechanism is needed either way.


Sequence

Decided 2026-09-07. Items 1 and 2 are independent and both proceed now.

WorkEffortState
1Atlas via templates — ordering (PLAN-templates-000), then the missing config: fieldsMin flight
2Atlas frontend via uis argocd registerfreeready, nothing blocks it
3Retract SCRIPT_CONFIGURABLE where no handler exists — see belowSfiled
4Per-workload named secrets (ANALYSIS-nais-uis §4 item 1) — closes the seam above; the real prerequisite for both external developers and whole-lifecycle GitOpsMnot started
5Then reconsider a UIS Application typeLdeferred

Item 4 is the gate, and it is not the one people assume. "We need GitOps before we onboard developers" is the wrong dependency: ArgoCD already works. What a developer cannot get today is a database with a credential in a declaration — and that is item 4.

Item 3 — advertise only what exists

Eight services declare SCRIPT_CONFIGURABLE="true"; two have handlers:

declared : elasticsearch mysql mongodb qdrant postgresql redis authentik postgrest
handlers : postgresql postgrest
the rest : "Handler not yet implemented." (configure.sh:234)

The two that work are exactly the two the first application needs — so the provisioning half of this rule works for Atlas and would fail for application #2.

Decision: retract the six, build none for now. Two honest handlers beat eight of which six lie, and a stub advertised as a capability is the same defect that cost the first tenant two of its four install steps. Retracted is not cancelled: re-declaring is a one-line change the day a handler exists.