forge env render, and you can reproduce any of it on your own project, because rendering is read-only and needs no cluster.
On Reliant hosting
A hosted environment is a small set of resources that the platform runs for you. You declare four kinds of thing, and the platform supplies the rest around them. Look at the arrow from the browser to/_api/*. The frontend and the API share one origin, because the platform serves the API workload at /_api/* on every hostname of the site, its generated one and your custom domains alike. Sign-in’s session cookie is therefore first-party to the site and no CORS is involved. The scaffold wires this for you by giving the frontend an API_URL that references the api workload.
What forge env render prod prints for the scaffolded project is four resources, each a forge.dev/v1alpha1 record the platform admits and renders itself: the migrate job, the api workload, the web static site and the demo database. Here are the last three, trimmed:
preflight.forge.invalid). A render that has no credential and no release prints a deliberately unusable address so it can never be deployed by accident; a real deploy resolves it under your organization’s registry path and pins it by digest.
Three things in that output are worth knowing:
- References, not values. The database URL appears as
databaseRef: {name: demo}and the identity settings asmanagedSecretreferences. A rendered environment never contains a credential, so it is safe to print, diff and attach to a pull request. - Probes are written out. A service forge builds gets
/readyzand/healthzon itshttpport, explicitly, on every runtime. The platform, the CLI and a human reading the record all see the same probe. - The workload is restricted. Hosting shares machines between customers, so it admits only what is safe there and refuses cron kinds, operators, RBAC, sidecars, volumes and pod overrides. Hosting on Reliant has the table, and the refusal at render names the workload and the field.
runtimeConfig is how one built bundle serves every environment, because the platform writes the site’s config.js after each sync from this block, resolving API_URL to the right api workload. Secrets covers the write-only store, and Custom domains the hostname side.
On a cluster you operate
Bind a workload toforge.OnCluster and forge renders ordinary Kubernetes objects and applies them to the kubectl context your KCL names. It never uses whichever context your shell has selected. The objects depend on the workload’s kind.
The renders below use a scratch project with a cluster binder that sets a replica floor of three and declares
network_policy. Those two lines come from the scaffolded _on_cluster binder and from the environment, not from forge’s defaults, and they are why you see a PodDisruptionBudget and NetworkPolicies. A workload with one replica gets neither a PDB nor topology spread.One service, in full
Read the pod from top to bottom: the migration init container runs first and must succeed, so no new pod serves traffic against an old schema, and a failed migration stalls the rollout while the old pods keep serving. The Service, PDB, ServiceAccount and NetworkPolicy attach to the pod by label.forge env render cloud --list for a project like this prints one line per object:
replicas = 1 does not mean one process during a rollout. The default RollingUpdate surges a new pod before the old one stops, so two run for a moment. When the count is a correctness bound, such as a sweeper that is not safe to run twice, declare strategy = "Recreate" and accept the gap.
What each workload kind renders
I rendered one workload of every kind on a cluster. The objects forge produced:
A workload that declares
storageGiB adds a PersistentVolumeClaim and switches the Deployment to Recreate, because a ReadWriteOnce volume would deadlock a surge pod. It also requires replicas = 1; a binder that floors replicas at three must be bypassed for it, and forge’s refusal says so.
The scaffolded migrate workload is special. It declares before = [fw.BEFORE_ALL], and on a cluster that does not render a Job of its own: it becomes the init container on every workload it gates. In the full listing there is no migrate Job, only a migrate init container in each pod template.
Namespace-wide objects
When the environment declarescluster_target and a network_policy, forge also renders a default-deny posture for the namespace:
egress_ports defaults to 443 (HTTPS to JWKS endpoints, object storage, third-party APIs) and 5432 (a managed Postgres outside the cluster); add any other external port your code dials. A telemetry policy appears only when you name the collector’s namespace.
Gateway API objects (a Gateway, HTTPRoute, GRPCRoute) appear only when you declare them in deploy/kcl/ingress.k and wire them in. The scaffolded file ships with the routes commented out, so nothing is exposed until you choose a hostname.
A database on your cluster
forge.ManagedDatabase bound OnCluster renders a forge record that expands into a CloudNativePG Cluster, and a workload reaches it with DATABASE_URL = forge.DatabaseRef {name = "…"}. Your cluster must already run the CloudNativePG operator; forge never installs it. The default deletionPolicy is retain, because deleting a declaration is not a statement about whether the data should exist.