Skip to content
Build and shipProduct pagePricing

Environments and GitOps

An environment is a folder in a Git repository, connected to a Kubernetes cluster. Your CI writes each new image digest into the folder, the cluster’s own Frazer GitOps syncs it, and admission checks the digest’s signature. Staging moves on a commit, production on a pull request you approve. One cluster can run any number of environments from any number of repositories, each inside its own fence.

Product overview: Environments and GitOps.

Keep code and deployment configuration in separate repositories. A code repository holds source, tests and the CI workflow; a commit there builds, signs and pushes an image to Frazer Registry. An environment repository holds what runs where: Helm values, image digests, databases and raw manifests, one folder per environment. A commit there is synced by the cluster.

acme/storefront ──CI: test, build, sign──► Frazer Registry api@sha256:…
│
└──CI commits the digest──► acme/platform-envs envs/staging/apps/core/api/images.yaml
│ webhook
▼ cluster acme-staging-1: GitOps syncs; admission checks the signature

The console follows each image by its digest through five stages: Built, Written to Git, Synced, Rolled out, Verified. Nobody presses “new app”: a new service is a new folder in the environment repository.

Frazer reads your repositories through the read-only Frazer Reader GitHub App, installed on the repositories you pick. Each cluster gets a 60-minute read token, refreshed every 20 minutes, that covers only the repositories connected to it.

  • Environment: a repository, a revision (branch, tag or commit), a path, a set of namespaces, a permission profile, the gateways and hostnames its routes may use, and a sync policy. An API resource on a cluster.
  • Namespace ownership: a namespace belongs to exactly one environment. The plan fails if two environments claim the same one.
  • Fence: each environment gets its own GitOps namespace env-<name> and AppProject env-<name>, which see only its repository and Frazer Registry, and only its namespaces.
  • Permission profile: what kinds of objects the environment may create: standard, platform or preview.
  • Mode: managed (Frazer reads the folder convention) or repo-defined (your own ApplicationSets, applied inside the fence).
Layout Good for
One repository, a folder per environment (envs/staging, envs/production) Most teams, up to dozens of services. The default.
One repository per team (payments-envs, data-platform-envs) Several teams with their own owners and approval rules.
One repository per environment (envs-staging, envs-production) A hard production boundary, where only release managers merge to production.

Environments are folders on one branch, not branches. Branches drift through merge order, cherry-picks and fixes that land in one branch only; a pull request between folders shows exactly what changes in production. You can still pin an environment’s revision to a tag or a commit, but Frazer never creates environment branches. A deploy/ folder inside a code repository also works for a team with one service.

You need a cluster (create one) and an environment repository on GitHub.

  1. Install the Frazer Reader GitHub App on the environment repository. In the console, open your organization’s Settings › Integrations, choose GitHub and select the repositories Frazer may read. It asks for Contents: read and Metadata: read, nothing else.

  2. Lay out the environment’s folder:

    envs/production/ # the path you connect
    ├── environment.yaml # optional: labels, namespace defaults
    ├── _shared-values.yaml # environment-wide Helm values
    ├── apps/<family>/<app>/
    │ ├── values.yaml # written by people
    │ └── images.yaml # written by CI only
    ├── databases/<name>/database.yaml # Postgres, with backups
    └── manifests/<name>/ # raw YAML or a Kustomization: operators, tools
  3. Connect the path to the cluster. Frazer computes a plan from the repository’s current contents.

    Terminal window
    frazer releases environments connect data --project acme-production --cluster acme-production-1 \
    --repo acme/data-platform-envs --path envs/production \
    --namespaces data-airflow,data-clickhouse --profile platform --dry-run
    Output
    Plan: connect environment "data" to cluster acme-production-1 (acme-production)
    + Namespaces env-data, data-airflow, data-clickhouse
    + AppProject env-data source acme/data-platform-envs + Frazer Registry · profile platform (elevated)
    cluster-scoped allowed: CRDs clickhouse.altinity.com
    + Applications 4 from commit 3f9c2e1: clickhouse-operator, clickhouse, airflow, superset
    ~ Git token acme-production-1 also reads acme/data-platform-envs
    ✓ Namespaces no conflict (environment "platform" owns checkout-api, web)
    ✓ Policy Policy dry run: 0 violations · schemas valid
    Cost (expected) +6 vCPU, +24 GiB requested on acme-production-1; no fee (own servers)
    Approval org admin (profile platform)

    Read the plan, then run the command again without --dry-run to connect.

  4. If the plan says Approval: org admin, an organization admin approves it. The platform profile is elevated; standard needs no approval.

  5. Watch the first sync under Engineering › Environments. Each app folder becomes a GitOps Application in the environment’s AppProject.

What Frazer reads in managed mode:

  • A family is a chart. Each apps/<family>/<app>/ folder becomes one Application from the family’s chart. Frazer ships a general app chart, and you can push your own chart families to Frazer Registry.
  • Values are layered, lowest first: the chart’s defaults, then _shared-values.yaml, then the app’s values.yaml, then images.yaml. Each app is an Application with two sources: the chart from Frazer Registry and your repository as $values.
  • images.yaml is CI’s. People edit values.yaml; CI writes only image digests. (Writing the digest into values.yaml also works.)
  • Databases and manifests. databases/<name>/database.yaml creates a Postgres database with backups through Frazer’s database chart. manifests/<name>/ holds raw YAML or a Kustomization for operators and extra tools.

In repo-defined mode, the repository’s own ApplicationSets are applied into env-<name> instead, and any Application that names another GitOps project is refused.

Your CI builds once, pushes to Frazer Registry with a short-lived token (how), and Frazer signs the digest on push. Then CI writes the digest into the environment repository: to staging by commit, to production by pull request.

envs/staging/apps/microservice-grpc/billing/images.yaml
# written by CI only; production gets the same digest by pull request
app:
image:
repository: registry.frazer.ai/acme-staging/build-images/billing
tag: v0.1.46
digest: sha256:c3c9…

Frazer GitOps syncs on the repository’s webhook, and admission verifies the signature before a pod starts. The rollout appears under Engineering › Rollouts, joined to the commit, the digest and whoever wrote it. Syncs and rollouts also reach your webhooks, for example frazer.rollout.succeeded.v1; the full list is under Reference.

Promotion is a pull request that copies a digest, or a values change, from envs/staging to envs/production. The diff is exactly what changes in production.

Engineering › Rollouts shows staging and production side by side for each service. When staging is ahead, Promote opens that pull request for you. Opening pull requests needs the opt-in Frazer writer GitHub App; without it, open the pull request yourself.

To roll back, revert the commit or the pull request. Git moves forward, Frazer GitOps follows, and the rollback is in the audit log like any other change.

Profile What the environment may create Who chooses it
standard (default) Namespaced kinds in its own namespaces. No cluster-scoped kinds, no Gateways, no network-plugin, gateway-controller or GitOps kinds. Developer
platform Also an allowlist of cluster-scoped kinds: CRDs, ClusterRoles bound to its namespaces, its operators’ webhooks. Components Frazer guarantees stay blocked. Organization admin; the plan marks it elevated
preview What a preview policy runs: one namespace per pull request, deleted on merge, close or TTL. See Templates and previews. Developer

On every pull request that touches a connected path, the Frazer Reviewer GitHub App posts one check per environment, such as Frazer / acme-production / data. Frazer asks the target cluster’s own Frazer GitOps to render the pull request’s head commit and compares it with the live state, so the check uses the same chart versions and plugins as the real sync. The check shows:

  • the objects added, changed and deleted, per Application, with the full diff in the details;
  • anything the environment may not do: a Gateway, another environment’s namespace, a cluster-scoped kind under standard;
  • a Secret with data in it;
  • Frazer Policy dry-run results;
  • whether each new image digest is signed;
  • the change in requested resources and cost.

Make the check required in your repository’s branch rules to stop a change that fails it from merging. A changed source policy runs in evaluate mode first: for a set time it lists the deploys it would have refused, then it enforces.

Git holds ExternalSecret objects only. The values live in Frazer Secrets and reach the cluster through its own identity, with the frazer-secrets ClusterSecretStore:

envs/production/manifests/checkout-api-secrets/stripe.yaml
apiVersion: external-secrets.io/v1
kind: ExternalSecret
metadata:
name: stripe
namespace: checkout-api
spec:
secretStoreRef:
kind: ClusterSecretStore
name: frazer-secrets
target:
name: stripe
data:
- secretKey: STRIPE_SECRET_KEY
remoteRef:
key: payments/stripe-secret-key

The pull request check and an admission policy both reject a Secret with data or stringData; empty placeholders on an allowlist pass. Frazer does not keep encrypted secrets in Git: encrypted values would stay in Git history forever, and every cluster would need one more key. Git, registry and image pull tokens are minted per cluster and never stored in Git.

  • Automated sync with prune and self-heal, Server-Side Apply and server-side diff.
  • FailOnSharedResource, so no two Applications own the same object.
  • Namespaces, PersistentVolumeClaims, CRDs and databases are never pruned or deleted without a confirm (Prune=confirm, Delete=false).
  • Replicas owned by an autoscaler are ignored in the diff.
  • Charts and remote bases are pinned, never latest.
  • Sync waves: manifests/ first, then databases/, then apps.
  • Production can add a sync window that refuses automated syncs outside the maintenance window; a manual sync still works.
  • Developer: connects an environment with the standard or preview profile.
  • Organization admin: approves the platform profile.
  • CI: writes image digests into images.yaml, nothing else.
  • Frazer: reads the selected repositories through the read-only Frazer Reader App. Writing pull requests (promote and restore) takes the separate Frazer App; agent branches and pull requests take the Frazer Agents App; checks and review comments come from Frazer Reviewer. You install each one only if you want it.

Frazer logs connecting, changing and disconnecting an environment, each as a plan, and every rollout with its digest, commit and author. Git itself records every change to what runs.

Setting Default
Environments per cluster Any number, from any number of repositories
Owners per namespace Exactly one environment
Cluster Git read token 60 minutes, refreshed every 20 minutes, limited to connected repositories
Prune of namespaces, volume claims, CRDs, databases Waits for a confirm
Sync window Off; production can add one

The plan shows the vCPU and memory an environment adds to the cluster, and its quota impact, before you apply.

Environments have no fee of their own: every cluster comes with Frazer GitOps, gateways, DNS and certificates. On servers you bring, what your environments run is part of the per-core server fee.

KubernetesOn your serversOn the Frazer fleet
Price$0 per cluster$0 per cluster

Every line, with the estimator: pricing.

Symptom Cause Fix
The connect plan fails with a namespace conflict Another environment already owns one of the namespaces. Pick other namespaces, or move the objects and disconnect the other environment first.
The repository is not in the connect list The Frazer Reader App is not installed on it. Extend the installation to the repository under Settings › Integrations.
The pull request check flags a Gateway or a cluster-scoped kind The environment’s profile is standard. Remove the object, or have an organization admin approve the platform profile.
An Application is refused in repo-defined mode It names a GitOps project other than the environment’s. Set its project to the environment’s own AppProject, env-<name>.
A pod is refused at admission The image digest is not signed by an accepted signer, or the deploy policy blocks a fixable Critical. Check the digest with frazer registry images describe. See Registry.
A sync does not start in production A sync window refuses automated syncs outside maintenance. Wait for the window, or start a manual sync.
A deletion is pending Namespaces, volume claims, CRDs and databases wait for a confirm. Confirm it in the console if the deletion is intended.
A Secret in the repository is rejected Secret values are not allowed in Git. Store the value in Frazer Secrets and commit an ExternalSecret.

CLI

CommandWhat it does
frazer releases environments connectConnect a repository path to a cluster as an environment, with its namespaces, permission profile and sync policy, as a plan.

All of them: CLI reference.

Events

Event typeWhen it is sent
frazer.environment.synced.v1An environment finished a sync.
frazer.environment.sync_failed.v1An environment's sync failed.
ai.frazer.releases.rollout.v1.startedA rollout started in an environment.
ai.frazer.releases.rollout.v1.succeededA rollout finished successfully.
ai.frazer.releases.rollout.v1.failedA rollout failed.
ai.frazer.releases.rollout.v1.rolledbackA rollout was rolled back.

All of them: Events reference.