forge env render on a scratch project, so the behaviour described is what forge printed. The one rule that holds throughout: change the input, never the output. Generated files are overwritten on the next forge generate, and a hand-edited render is overwritten on the next deploy.
Change one field: overrides
If a field of a rendered object is wrong for your environment and forge has no property for it, name the object and the field in Bundle.overrides. Forge renders its own object and then patches it.
Kind/name, with an optional namespace (Deployment/demo-prod/billing) and an optional cluster (Deployment/billing@gke_acme_prod) when the short form is ambiguous. Forge refuses an ambiguous key and tells you which qualifier to add. forge env render prints what applied:
None deletes it. An override that does not resolve fails the render with no manifests, because a silently ignored override would deploy an environment that quietly lacks the change. It also cannot rename or retarget an object: metadata.name and apiVersion are off limits.
Overrides apply to what you render, so the shape, the release and the bundle all carry the patched object. They do not work on a hosted workload, because the platform renders those objects, not your environment. Set the field on the workload declaration, or bind the workload to a cluster you run.
Before reaching for an override, check whether the field is a property of fw.Workload already. Replicas, resources, probes, env, ports, strategy, priorityClassName, storageGiB, sidecars, volumes and tolerations are, and declaring them there is better because the check that “a hosted workload may not set this” then runs for you.
Add objects forge does not render
Anything that is not a workload goes in the bundle, and there are three ways to put it there. All three are placed the same way: on a cluster and namespace you choose, stampedforge.dev/workload: <name> so --target <name> selects them, and visible to forge env render and forge env shape.
forge.dev/workload: extra on it. Use forge.Manifests for a CRD, a ClusterIssuer or a DaemonSet.
For an application that already ships a Helm chart, set delivery = "bundle" on a forge.HelmChart. Forge runs helm template --skip-crds at build time and ships the output inside the release. The default, delivery = "bootstrap", is for platform charts such as cert-manager, which are installed with forge env deploy <env> --target <name> and kept out of the bundle so that a reconciler’s prune can never delete their CRDs.
kustomize build or jsonnet, use forge.Generated:
apiVersion and kind fails the render naming the generator. The command must be deterministic: the bundle’s digest is its identity, so a timestamp or random value would cut a new bundle on every build.
A hosted environment accepts none of these. The control plane admits only workloads, managed databases and static sites there, so a bundled chart or a Generated is refused at render. Bind the workload that needs raw objects to a cluster you run.
Deploy code forge never compiled
A workload’sbuild says how its artifact is produced, and the field is optional. Everything forge does after the build, the ledger, plans, promotion by digest, the rendered Deployment with its hardening and probes, is the same whatever produced the image. So forge deploys code in any language.
There are four ways to declare it. Pick by what you already have.
- An existing image
- Your own Dockerfile
- Any command
- A prebuilt binary
Name the image and declare no Every image on a cluster or compose workload must name its registry host, because there is no environment registry to complete a bare name from. A third-party image with ports gets a TCP probe on its first port, since forge cannot know it serves
build. Forge builds nothing and deploys what is there./readyz./readyz and /healthz probes unless you declare probes, and GOMEMLIMIT is derived only for GoBuild workloads (set go_memory_limit_percent for others). It does not read forge’s typed config unless you project environment variables into it yourself, and its logs and traces appear in Observability only if it exports OpenTelemetry. Its deploy, its plan, its release and its rollout wait are forge’s, and are identical.
On Reliant hosting, a workload’s image must live in the platform’s registry. A bare image = "billing" resolves to your organization’s path and forge pushes it there; an image that names another registry host is reported at render as outside the subtree the platform will admit. A hosted workload that needs to pull from your own registry belongs on a cluster you run.
forge.RemoteBuild is in the schema as a way to build from a pinned git source on a hosted build service, but this forge cannot submit one yet, and the build refuses it with an explanation. Do not rely on it.Opt a workload back into service links
Kubernetes injects an environment variable for every Service in a namespace into every pod (<NAME>_SERVICE_HOST, <NAME>_PORT=tcp://…). Forge renders enableServiceLinks: false on every pod it produces, because those names collide with typed configuration: a Service called temporal injects TEMPORAL_PORT, which an integer port field then fails to parse. Use DNS names or explicit environment variables instead.
If a legacy workload really reads the injected names, opt that one workload back in. This works on a cluster you run, not on hosting:
enableServiceLinks: true, and every other pod in the same render still shows false.
Change who owns a file
The generated-versus-yours split is explicit, and you can move a file across it.forge project scaffolded lists what forge has written once and which of those are absent.
Adopt an app that already exists
A Vite or Next.js app becomes a forge project without moving a file:forge.yaml (with no Go module, since a site has none) and deploy/kcl/{dev,staging,prod}/main.k, each declaring the app at the project root and binding it to its dev server in dev and to hosted static hosting in staging and prod. Your own files are untouched and .gitignore is merged. Build-time keys such as VITE_* and NEXT_PUBLIC_* become env_vars on the frontend, baked in per release; values that must change without a rebuild belong in runtime_config, which the platform writes into config.js after each deploy. Hosting on Reliant covers what static hosting serves.
A server that already has its own protos and handlers adopts in place, one service per pull request, with // forge:unmanaged. Agents can run forge skill load migration for that path.
Turn features off
Forge derives its subsystems from what is in the repository:.proto files turn on codegen, db/migrations/ turns on migrations and the ORM, a frontend turns on the frontend tooling, a forge.Gateway in deploy/kcl turns on ingress, internal/operators/<name>/ turns on operators. When a derived default is wrong for your project, pin it in forge.yaml:
codegen, migrations, ci, build, contracts, frontend, observability, hot_reload, deploy, ingress and operators. A pinned feature stays pinned: no later change to the tree turns it back on. The ORM has no key. A project with a hand-written data layer opts out by putting //forge:no-orm: <reason> in the doc comment of its internal/db package, and one table opts out with COMMENT ON TABLE t IS 'forge:no-entity'. forge project features prints what is on and why.
Observability has its own switch for the local stack. Setting _observability = False in deploy/kcl/dev/main.k starts no container and exports no endpoint, and processes run as they did before. Pointing _destination at your own OTLP endpoint swaps the local ClickStack for a small forwarder instead.
Leaving hosting is the same kind of edit. Rebind a workload from _hosted(...) to _on_cluster(...), or drop control_plane, the managed database and forge.HostedSecrets and use forge.ExternalSecrets {}, and forge deploys the same releases and plans to your infrastructure. Environments covers rebinding.
Related topics
What gets deployed shows what an override or a raw manifest lands in. Self-hosted delivery covers delivering to a cluster you run. Agents can runforge skill load deploy/overrides, deploy/byo-yaml and deploy/build-contexts for the full reference.