App Creation Flow
This page walks through what actually happens between submitting the Deploy Application Wizard and having a live, reachable app. An earlier version of this page described a much more elaborate flow — per-app provisioning of a new AWS EKS/GCP GKE/OCI OKE cluster, Cloud Native Buildpacks, Trivy image scanning, ArgoCD, multi-region active-active failover — none of which exists in KubeOpera today. The real platform is simpler and more specific than that: every tenant deploys into their own vCluster, a lightweight virtual Kubernetes control plane sharing one real, already-provisioned host cluster — there's no per-app cloud infrastructure to stand up, which is exactly what makes a deploy take minutes rather than the 5–15 the old version of this page described.
From Deploy to live
The four stages the Deploy Application wizard shows. Select a stage for details.
Two sources, one deploy path after that
The wizard supports two ways to get an image running: an existing container image in a registry (public or private), or a GitHub repository, which triggers a real build before deploying. Both converge on the same mechanism from that point on — see Basic Operations for the tenant-facing walkthrough of this.
Container image. You supply a registry image reference (and, for a private registry, credentials) and KubeOpera deploys it directly — no build step.
GitHub repository. build-service runs a Kaniko build Job in the host cluster's dedicated kubeopera-builds namespace, using the Dockerfile path and build context you specify, then pushes the resulting image to KubeOpera's own self-hosted, per-tenant-isolated registry. Once the push succeeds, the wizard hands the resulting image reference into the exact same deploy path a registry-image deploy uses — a build never gets its own separate deployment mechanism. See Build Service for how the build itself works, and KubeOpera API for the registry-credential isolation model.
Deploying: what actually runs
- Write desired state. The app's configuration (image, port, environment variables, resource requests/limits, autoscaling, domain, TLS) is written to the
KubeOperaAppcustom resource. - Generate manifests.
app-controllerreconciles the CR: it callsapp-serviceto turn the schema into real Kubernetes YAML (Deployment, Service, Ingress, Namespace, and anything else the schema calls for), then pushes those manifests to a Git repository — the fleet repo that GitOps reconciles from. - Flux syncs inside your vCluster — not the host cluster. A
GitRepository/Kustomizationpair inside your own vCluster picks up the commit and applies it there. This is why deleting an app later genuinely tears down everything it created, rather than just removing a database row — and why a tenant's Pods/Deployments live inside their own vCluster, never the shared host cluster's namespaces. - Ingress and TLS. When TLS is enabled (the default), the generated Ingress carries a
cert-manager.io/cluster-issuerannotation forletsencrypt-prod-dns— a DNS-01 challenge against Route53, not HTTP-01. Because this Ingress is inside the vCluster, vCluster's ownsync.toHost.ingressesconfig carries it to the host cluster automatically, where the real, already-runningletsencrypt-prod-dnsClusterIssuer and external-dns take over with no per-app infrastructure work. - Subdomain. The app's hostname is computed server-side, not client-supplied:
<app>-<cloudspace-slug>.apps.kubeopera.io, a subdomain of the already-DNS-01-enabledkubeopera.iozone.
The wizard's progress view is a real reachability check, not a timer
The wizard polls GET /apps/{id}/deploy-status, which composes three real signals — GitOps sync phase, live Pod/Deployment readiness (read from inside your vCluster), and, only once both look healthy, an actual outbound HTTPS request from kubeopera-api against the app's own computed subdomain. The "View live app" link only appears once that request genuinely succeeds — the wizard doesn't guess.
In practice, the dominant source of wait time on a fresh TLS-enabled deploy is cert-manager's own DNS-01 challenge — Route53 TXT record propagation plus Let's Encrypt's own validation step — measured at roughly 150–170 seconds from "Deployment ready" to "reachable," comfortably inside the wizard's poll timeout. A registry-only deploy with TLS disabled reaches "reachable" much faster, since there's no certificate to issue.
What the wizard collects
| Field | Notes |
|---|---|
| Name | Used to derive the app's namespace and computed subdomain |
| Source | Container image, or a GitHub repo (Dockerfile path + build context) |
| Registry credentials | Optional — only needed for a private image or base image |
| Target vCluster | Only shown when a Cloud Space owns more than one vCluster; auto-selected otherwise |
| Port | Container port the app listens on |
| Environment variables | Plain values or references to an existing Secret |
| Resources | CPU/memory requests and limits |
| Autoscaling | Optional min/max replicas and CPU threshold |
| TLS | On by default; when enabled, the generated Ingress requests a real cert as described above |
API reference
The application endpoints described above (POST/GET/PUT/DELETE /apps, GET /apps/{id}/deploy-status, /logs, /pods, /events) are documented in full, with real request/response shapes, on the KubeOpera API page — this page intentionally doesn't duplicate that table, to avoid the two drifting apart.
What this page no longer claims
For anyone who read an earlier version of this page: KubeOpera does not provision a new cloud cluster per app, does not use Cloud Native Buildpacks or Trivy scanning, does not offer SAML or a choice between Flux and ArgoCD, and doesn't publish a disaster-recovery SLA (RTO/RPO/MTTR figures). Those described a different, more generic platform than the one that exists today. If any of that functionality gets built for real, this page is the right place to document it.