Secrets

Store credentials once for your organization, attach them to the apps that need them, and read them as environment variables at runtime.

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.

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.

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.

{
  "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.

Secrets and plain 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.

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, 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.

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.