Secrets
Every app needs values it can't commit — a database password, an SMTP login, an API key. This page is the whole story for a Rask app: where they live, how they reach the server, and what Rask deliberately does not do with them.
The short version. Put them in a
.env.productionfile that git ignores, deploy withrask deploy --env-file .env.production, and read them throughIConfiguration. Rask remembers the names of the variables your app needs and refuses to deploy without them.
Reading a secret in your app
Secrets arrive as environment variables, which ASP.NET Core's configuration system already reads. Nothing Rask-specific is involved:
// A nested key uses a double underscore in the variable name:
// Stripe__ApiKey → Configuration["Stripe:ApiKey"]
var stripeKey = builder.Configuration["Stripe:ApiKey"];
Environment variables override anything in appsettings.json, which is why the same key can hold a
harmless default in the committed file and the real value in production.
Rask's own settings need no reading at all. Every one lives under Rask and is read by the registration
that owns it, so Rask__Auth__BearerSigningKey in the environment reaches AuthOptions.BearerSigningKey
with no code — see Configuration for every section.
Getting them onto the server
rask deploy --env "Rask:Mail:Smtp:Password=…" --env "Stripe__ApiKey=…" # one-offs
rask deploy --env-file .env.production # a file of KEY=VALUE lines
--env-file takes the format you'd expect — KEY=VALUE per line, # comments and blank lines ignored.
Add it to .gitignore. Rask reads it on your machine and hands the values to Docker over the SSH
connection; the file itself never leaves your laptop.
Values are passed to docker run through a file rather than as -e KEY=VALUE arguments, so they don't
appear in your machine's process table where any other local user could read them with ps.
Rask remembers the names, never the values
.rask/deploy.json is committed — it's how CI knows which host to deploy to — so no secret value is
ever written to it. What is recorded is the list of variable names the app was last deployed with:
{
"host": "deploy@box.example.com",
"domain": "app.example.com",
"envKeys": ["Rask__Mail__Smtp__Password", "Stripe__ApiKey"] // names only
}
That exists to prevent one specific, nasty failure. Without it, a bare rask deploy — or the CI workflow,
which passes no --env of its own — would start your app without its database password. The app boots,
answers its health check, takes traffic, and is quietly misconfigured. So a deploy that doesn't supply a
remembered variable fails:
This app was last deployed with Rask__Mail__Smtp__Password, which isn't set now.
Deploying without it would start the app misconfigured, so this is a refusal rather than a warning.
• pass it again: rask deploy --env Rask:Mail:Smtp:Password=…
• or from a file: rask deploy --env-file .env.production
• deploying from CI? add it to the deploy step in .github/workflows/deploy.yml
• no longer needed? remove it from "envKeys" in .rask/deploy.json
Deploying from CI
rask deploy --github-actions writes a workflow that needs two repository secrets of its own (an SSH key
and the host's fingerprint). Your app's secrets are separate — add them to the deploy step:
- name: Deploy
env:
SMTP_PASSWORD: ${{ secrets.SMTP_PASSWORD }}
run: rask deploy --no-setup-host --env "Rask:Mail:Smtp:Password=$SMTP_PASSWORD"
If that job starts failing after you deploy a new variable from your own machine, that's the check above
doing its job: add the same --env to the workflow.
What Rask doesn't do
Being explicit, because "secret management" means a lot of different things:
- No secret store. Values live in your
.env.production(or your CI's secret store) and become plain environment variables on the container. There's no vault, no encryption at rest, and no rotation. - No protection from anyone with Docker access on the box.
docker inspectshows a container's environment. Anyone in the host'sdockergroup is effectively root there anyway — which is whyrask deploycreates a dedicated deploy login rather than sharing one. - No masking of what your app prints. If your app logs its own configuration, it logs its secrets. Rask masks the values it passed when it dumps a failed container's logs, but it can only mask what it knows.
For a single-operator product this is usually the right amount of machinery. If you outgrow it, the environment-variable interface is the standard one — any secret store that can populate env vars at deploy time drops in without changing your app.
See also
- Deployment — what else
rask deploysets on the container. - Configuration — the non-secret half.
- The
raskCLI — every deploy option.