Environment variables & secrets
Set config and secrets for a deployed app — plain variables, sealed secrets, auto-generated keys, and linking a managed database — from the App Settings editor.
Environment variables & secrets
Every app you deploy on HostSSH gets its configuration through environment variables.
Some are harmless (PORT, a public URL); others are secrets (a database password, an API
key). HostSSH treats those two differently so a secret is never exposed where it shouldn't
be — not in your dashboard, not in our database, not in a build log.
You manage all of them in one place: the Environment card on a deployment's page.
Open the editor
Go to Deploy → your app. The Environment card lists the app's current variables. Click Edit to change them. Each row is a key, a value, and two switches:
| Switch | Meaning |
|---|---|
| (none) | A plain variable — the value is stored and shown as-is. |
| secret | The value is sealed (encrypted at rest) and masked everywhere in the UI. |
| generate | HostSSH generates a strong random value on your server — you never type or see it. Implies secret. |
Add rows with Add variable, remove them with the trash icon, then Save.
Saving stores your settings; a redeploy applies them. After saving, click Redeploy to roll the app with the new environment.
Plain variables
For non-sensitive config, just type a key and value and leave both switches off:
PORT 3000
NODE_ENV production
NEXT_PUBLIC_URL https://app.example.com
These are stored and shown in plain text — that's fine, they aren't secrets.
Secrets
For anything sensitive — a database URL, a Stripe key, a JWT secret — turn on secret. When you save:
- The value is encrypted before it's stored. Our database only ever holds ciphertext.
- It's masked everywhere afterward — the card shows
•••• (secret), never the value. - It reaches your app over an encrypted channel and is injected as an environment variable on the server, never printed in a build or deploy log.
Because a secret is masked, you can't see it to re-type it. So when you edit other variables, leave a secret's value blank to keep it — HostSSH preserves the stored value. Only type a new value when you actually want to change it.
Auto-generated secrets
Some apps need a strong random key (a search master key, an admin password) that no human needs to know. Turn on generate and leave the value blank. On the next deploy, your server mints a strong random value, stores it locally, and injects it — the value never touches your dashboard or our control plane.
Generated secrets are stable: once created, the same value is reused on every redeploy,
so it won't rotate out from under a running app. One-click templates that need such a key
(for example Meilisearch's MEILI_MASTER_KEY) set this up for you automatically.
Link a managed database
If you created a database through HostSSH (Deploy → New database), its credentials —
including a ready-to-use DATABASE_URL — already live on your server, under the database's
name. To give an app those credentials, add the database's name under Linked secret
sources in the editor (comma-separated for more than one):
Linked secret sources: app-postgres, cache-redis
On the next deploy, your app receives that database's DATABASE_URL (and its other
credentials) as environment variables — no copy-pasting a connection string, and the secret
never leaves the server. This also means the app's release-phase migration runs with the
right database credentials.
Multi-line values
Some secrets span multiple lines — a PEM private key, a service-account JSON. Paste them straight into the value box; HostSSH delivers multi-line secrets correctly. (They're injected in a way that keeps them out of any log or process listing, just like single-line secrets.)
Build-time vs runtime variables
Some variables have to exist while your app is built, not just while it runs. Next.js
(and Vite, Create React App, …) bake NEXT_PUBLIC_* / VITE_* values into the JavaScript
your visitors download, which happens at build time.
HostSSH treats those two groups differently, and the difference is a security boundary:
| Example | How it reaches your build | |
|---|---|---|
| Public build variables | NEXT_PUBLIC_APP_URL, VITE_API_URL, NODE_VERSION | passed on the build command line — they're already public (the browser downloads them) |
| Everything else | DATABASE_URL, STRIPE_SECRET_KEY, your own API_TOKEN | treated as a secret and kept off the command line |
The split is fail-closed: a variable is only treated as public if it uses a known client-bundle prefix or is a recognized non-secret version pin. Anything HostSSH doesn't recognize is treated as a secret — including every variable you invent. That's the safe default, not a judgement about your naming.
Don't put a secret behind a
NEXT_PUBLIC_(orVITE_,REACT_APP_, …) name. That prefix tells your framework to ship the value to every visitor's browser. HostSSH honours what the prefix means; it can't protect a secret you've asked to be published.
What HostSSH never does with your secrets
- Never stores a secret in plain text. Sealed at rest; generated secrets are minted on your own server and never sent to us.
- Never shows a secret back to you. Once saved, it's masked — there's no "reveal."
- Never prints a secret in a log. Secret values are injected as environment variables, not echoed into build or deploy output.
- Never puts a secret on a command line, where any other user on the box could read it
with
pswhile your app builds. (One exception, being closed: thehostpackbuilder — the zero-config one used when your repo has noDockerfile— still does. It warns you at build time. If your build needs secrets, ship aDockerfileand use thedockerfilebuilder until that's fixed.)
Next steps
- Connections (BYOK) — bring your own storage and provider keys for backups and integrations.
- Backups & recovery — your app's data, captured and provably restorable.
For how this works under the hood — the custody model and the delivery paths — developers can read Environment variables & secrets (dev).