> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://rivet.chrisboulton.com/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://rivet.chrisboulton.com/_mcp/server.

# Releases and environments

> Publish immutable configuration snapshots to S3, GCS, or a directory, and promote them through environments.

A **source release** is an immutable snapshot of your configuration directory in object storage. An **environment** is a named pointer, such as `staging` or `production`, to one release. Hosts running [`rivet run`](/operations/agent) follow an environment. Promoting a release changes what they apply on their next run.

Hosts get a catalog from a release in one of two ways:

| Mode                                           | How it works                                                                       | Host credentials                                                                                         |
| ---------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
| Source                                         | The host reads the release and compiles it locally.                                | Read access to the release storage and any cloud assets.                                                 |
| [Compile service](/operations/compile-service) | The host sends its facts over HTTPS; the service compiles and returns the catalog. | An identity the service accepts, plus storage read access unless the service uses signed asset delivery. |

If acquisition fails, the run stops. Rivet never switches modes or silently uses an older catalog.

## Publish and promote

```sh
# Upload a snapshot. This prints the release digest and changes no environment.
rivet release publish ./site --store gs://example-rivet-config/site

RELEASE='sha256:<64 lowercase hex characters>'   # the digest printed above

# Create the staging environment (generation 0 means it must not exist yet).
rivet environment promote staging --release "$RELEASE" \
  --store gs://example-rivet-config/site --expected-generation 0

# After checking staging, promote the same release to production.
rivet environment promote production --release "$RELEASE" \
  --store gs://example-rivet-config/site

rivet environment show production --store gs://example-rivet-config/site \
  --cache-dir /tmp/rivet-publisher-cache
```

Stores are `gs://bucket/prefix`, `s3://bucket/prefix`, or `file:///absolute/directory` for local testing. Cloud stores use the standard [credential chains](/language/assets#credentials). Give publishers write access and give hosts and the compile service separate read-only identities.

Keep agent and server configuration files out of the published directory: every top-level `*.hcl` file there is compiled as configuration.

**Publishing** copies regular files, skips `.git`, and refuses symlinks and special files. Objects are written only if absent, and the release index goes last, so a half-finished upload never looks like a release. Publishing identical content again gives the same digest.

**Promoting** first checks that every file in the release is present with the right size and SHA-256, then updates the environment pointer conditionally. If another promotion happened since you read the pointer, yours fails rather than overwriting it. `--expected-generation N` adds an explicit check: `0` requires a new environment, and a positive value requires that exact current generation. Each run reads the pointer once, so a promotion during a run doesn't affect it.

**Rolling back** is promoting an older release. That changes what future runs apply; it doesn't undo changes already made to hosts.

Environment names are 1 to 128 ASCII letters, digits, underscores, or hyphens.

## Check a release before promoting

```sh
# Compile against representative facts without downloading asset contents.
rivet release validate --store gs://example-rivet-config/site \
  --environment staging --cache-dir /tmp/rivet-publisher-cache --facts facts.json

# Fetch and verify every asset, and write a standalone bundle.
rivet release compile --store gs://example-rivet-config/site \
  --environment staging --cache-dir /tmp/rivet-publisher-cache \
  --facts facts.json --out staging.bundle
```

Without `--facts`, `release validate` uses the built-in Ubuntu baseline. Neither command touches a managed host.

## What a release can contain

Releases include the top-level `*.hcl` files and the resource assets they reference. They don't support:

* Modules, local or remote.
* Other files read by `file()` or `templatefile()`.
* `--vars-file` or data hierarchies, so every variable needs a default.

Inline templates and functions that don't read other files work as usual. Use [direct compilation](/reference/cli#compile) when you need the unsupported features.

| Limit                  | Value   |
| ---------------------- | ------- |
| Files per release      | 10,000  |
| Total release size     | 256 MiB |
| Combined top-level HCL | 16 MiB  |
| Facts snapshot         | 1 MiB   |

## Storage layout

For a store ending in `/site`:

```text
environments/production.json
environments/staging.json
releases/<index-sha256>/release.json
releases/<index-sha256>/main.hcl
releases/<index-sha256>/files/demo.conf
```

`release.json` lists every file with its SHA-256 and size, and its own digest identifies the release. An environment file holds a release digest and a promotion generation. Hosts read files through the index rather than listing the bucket.

Digests prove the files match the index, but not who wrote the index. Anyone who can write to the store can publish and promote. Releases aren't signed, so treat storage write access as the trust boundary.

## How hosts prepare a catalog

Before the runner starts, the host fetches every asset the catalog needs, verifies each size and SHA-256, and caches them under its private cache directory. A missing or mismatched asset stops the run before any change. The runner itself never downloads anything; apt repositories are the only network dependency during apply.

| Asset                                                              | Where the host gets it                                  |
| ------------------------------------------------------------------ | ------------------------------------------------------- |
| A file from the configuration directory, including `.deb` archives | The release object                                      |
| An S3 or GCS file source with a checksum                           | That object, at the version recorded during compilation |
| Inline or template-rendered content                                | Included in the catalog                                 |

The cache reuses unchanged files across releases. Releases and cached files are kept forever; there's no garbage collection yet.

See [agent runs](/operations/agent) to set up hosts, and [testing](/contributing/testing#distribution-checks) for how releases are tested.