---
title: Secrets | Runware Docs
url: https://runware.ai/docs/serverless/secrets
description: Store credentials once for your organization, attach them to the apps that need them, and read them as environment variables at runtime.
relatedDocuments:
  - https://runware.ai/docs/serverless/environment-variables
  - https://runware.ai/docs/serverless/deploying
  - https://runware.ai/docs/serverless/writing-a-model
---
## Introduction

A secret is **stored once for your organization and attached to the apps that need it**. The value stays out of your source, your image, and the definition you deploy.

At runtime it arrives as **an environment variable**, so your code reads it the way it reads any other.

```python
import os

@serve
class FluxDev:
    def load(self) -> None:
        token = os.environ["HF_TOKEN"]
```

## Two steps, deliberately

Creating a secret and using it are separate operations, because **one secret usually serves several apps**.

```bash
runware serverless secrets set HF_TOKEN
runware serverless secrets attach my-app HF_TOKEN
```

The first stores it against your organization. The second records that **this app may read it** and, on the next rollout, delivers it.

Detaching is the same in reverse, and it removes the app's access without touching the secret or any other app using it.

## The name it lands under

An attachment resolves to **the environment variable name the value is injected as**. By default that is the secret's own name, and `envVarName` overrides it when the two should differ.

```json
{
  "secretName": "hf-token-prod",
  "envVarName": "HF_TOKEN"
}
```

That indirection is what lets the same code run against a production and a staging credential: **the app always reads `HF_TOKEN`**, and the attachment decides which stored secret fills it.

> [!WARNING]
> Secrets and plain [environment variables](https://runware.ai/docs/serverless/environment-variables) **share one namespace on the worker**. A resolved secret name that collides with an existing variable is rejected with a `422`, and so is the reverse. There is no last-one-wins, on purpose: a silent override of a credential is not a failure mode worth having.

**Two attachments on the same app cannot resolve to the same name either.** That returns `409`, as does attaching a secret the app already has.

## Attaching reaches a running worker

This is where secrets differ from most configuration. **Attaching one rolls the app's live deployment in place**, so a worker that is already serving picks up the value from the attach alone.

> [!NOTE]
> Two edges are worth knowing. If a rollout is **already in progress** when the attach commits, it may not land on that one and arrives on a later redeploy instead. And an app that is **not live** records the attachment only, because the next resume reads the attachment set fresh anyway.

## What secrets are not for

Only the environment-variable kind exists. **Registry credentials for pulling a private image are not secrets** in this sense: they are consumed before any container starts, which is earlier than the point where a secret is unsealed, so they take a different route.

**Secrets are for sensitive values.** Anything else belongs in an [environment variable](https://runware.ai/docs/serverless/environment-variables), which is simpler and visible when you are debugging.

## Keeping the value out of your shell

`--value` takes the secret on the command line, and **you should assume anything passed that way is recorded**. It appears in the process list while the command runs and in your shell history afterwards. **`--value-file` reads it from a file instead, and `--value-file -` from standard input.**

```bash
runware serverless secrets set HF_TOKEN --value-file ./token.txt

printf '%s' "$HF_TOKEN" | runware serverless secrets set HF_TOKEN --value-file -
```

Reading the value from a file avoids both. **`.env` files and anything you keep the value in are never uploaded with your source**, so one sitting in your project directory does not travel to the build.