Secrets
One place for your credentials — database passwords, API keys, licence files — read by your game servers and sandboxes at launch.
A secret holds a credential: a database password, an API key, an RCON password, a licence file. Your game servers and sandboxes reference it by name and read the value when they start, so the value never sits in a config file, a launch command or your Terraform variables in plain sight — and rotating it once reaches everything that uses it.
Values go in and do not come out. Creating, rotating and attaching a secret never show you the value; reading one back is a separate permission that most people on a project do not need.
Secrets and versions
projects/{project}/locations/{location}/secrets/{secret}projects/{project}/locations/{location}/secrets/{secret}/versions/{version}A secret is the name, a description and who may use it. It holds no value itself. A version is one value, and cannot be changed once added. Versions are numbered 1, 2, 3… and a number is never reused.
A reference to a secret without a version means the latest enabled version. That is what makes rotation work: add a new version and everything that references the secret picks it up the next time it starts, with nothing to edit.
A secret belongs to one project and one region, like the workloads that read it. A value can be up to 64 KiB.
Creating a secret
Security → Secrets → New secret → name it, say what it is for, paste the value → Create secret--value also works, but puts the value in your shell history; prefer --from-file
or --stdin.
What you can read about a secret without its value:
| Field | Tells you |
|---|---|
latest_version | The version a reference without a number gets. |
payload_fingerprint | A short hash of the current value. Two secrets with the same fingerprint hold the same value — so "did I paste the same key into both?" has an answer without anybody looking. |
last_access_time | When a workload or a person last read it. A secret nothing has read in months is the one worth revoking. |
managed_by | Set when Plany stored the secret on your behalf. |
Rotating
Rotating is adding a new version. By default the version it replaces is disabled in the same step, so there is never a moment with no working value and never a forgotten old one left live.
Security → Secrets → open the secret → Rotate → paste the new value--keep-previous leaves the old version enabled while you check nothing breaks; then
disable it yourself.
A running game server or sandbox instance keeps the value it read when it started. The new one reaches it on its next start.
Managing versions
| State | Meaning |
|---|---|
STATE_ENABLED | Can be read. |
STATE_DISABLED | Kept, but refused to every reader. Can be enabled again. |
STATE_DESTROYED | The value is gone for good. The version's number, size and fingerprint are kept as a record that it existed. |
plany secrets versions db-password # newest firstplany secrets versions disable db-password 3plany secrets versions enable db-password 3plany secrets versions destroy db-password 3 # irreversibleUsing a secret in a game server
Attach a secret to a game server as an environment variable. The name is yours to choose — upper case, digits and underscores:
Servers → New server (or a server's settings) → Secrets → choose the secret and the variable nameThe variable can also be used in the game's config files and launch command as
${VAR}, so an RCON password is one attachment and a line in server.properties:
rcon.password=${RCON_PASSWORD}Add a path after a colon — --secret LICENCE_KEY=licence:licence.key, or
file_path — and the value is also written to that file, relative to the game's
directory, readable only by the game. For the credentials a game reads from a file
rather than a variable: a licence, a TLS key, a service account JSON. The file is
written fresh on every start, so a rotation never leaves a stale copy behind.
A server that cannot read one of its secrets refuses to start and says which, rather than starting without it and failing quietly later.
A value used as an environment variable must be text. A binary value — a key file, say — goes in a file instead.
See Game servers for the rest of a server's settings.
Using a secret in a sandbox
A sandbox takes secrets the same way, as environment variables and, optionally, files at an absolute path inside the container:
plany sandbox sandboxes deploy api --secret DATABASE_URL=database-url \ --secret TLS_KEY=tls-key:/etc/api/tls.keyplany sandbox sandboxes deploy api --remove-secret TLS_KEYChanging a sandbox's secrets deploys a new revision. See Deploying a sandbox.
Who can read a value
Reading about a secret and reading its value are different permissions.
| Role | See and manage secrets | Read a value |
|---|---|---|
roles/account.viewer, roles/project.viewer | See them | No |
roles/project.editor | Create, rotate, attach, disable, destroy | No |
roles/project.owner, roles/account.admin, roles/account.owner | Everything | Yes |
roles/secretmanager.accessor | Nothing | Yes, on the one secret it is granted on |
An editor never needs to see a value. Attaching a secret to a server is a reference by name; the server reads the value itself, as its own identity, when it starts. So the people who run a project can configure and rotate credentials without ever holding them.
roles/secretmanager.accessor is what a workload holds. When you attach a secret,
Plany grants it to that game server's or sandbox's identity on that secret
alone — it can read the value it was given, and cannot even list what else is in
the project. If the workload runs as a service account of your own, grant it
yourself, on the secret:
resource "plany_secret_iam_member" "backend_reads_database_url" { secret = plany_secret.database_url.name role = "roles/secretmanager.accessor" member = plany_service_account.backend.name}When a person who can read values needs one:
Security → Secrets → open the secret → Reveal on a version → RevealEvery read updates the secret's last_access_time.
Deleting a secret
Deleting a secret removes it and every version it holds. While a game server or a
sandbox still uses it, the delete is refused and names what is using it — detach it
there first. plany secrets delete --force deletes it anyway.
A secret Plany stored on your behalf cannot be deleted directly; delete the resource that created it.
Pricing and limits
| Price | $0.05 a secret a month, charged by the hour |
| Free | 10 secrets, every month |
| Secrets Plany stores for you | Free, and not counted |
| Limit | 50 secrets per project and region on Hobby, 500 on Pro — ask for more |
The price is per secret, not per version or per read: rotate as often as you like.