Environment Variables
Env vars hold secrets and config your app reads at boot. BTS encrypts every value at rest with AES-256-GCM and injects them into every preview environment.
DATABASE_URL, REDIS_URL, PORT, S3 credentials). Both appear in process.env.Auto-injected variables
Set automatically when the corresponding service is enabled. Do not define these yourself.
| Variable | When it's set | Example |
|---|---|---|
PORT | Always | 3000 |
DATABASE_URL | Postgres enabled | postgresql://user:pass@host:5432/app |
REDIS_URL | Redis enabled | redis://host:6379 |
MONGODB_URL | MongoDB enabled | mongodb://user:pass@host:27017/app |
ELASTICSEARCH_URL | Elasticsearch enabled | http://host:9200 |
S3_ENDPOINT, S3_ACCESS_KEY, S3_SECRET_KEY, S3_BUCKET | MinIO enabled | http://host:9000, … |
S3_PUBLIC_ENDPOINT, MINIO_PUBLIC_ENDPOINT | MinIO enabled | https://s3-….your-domain — the browser-reachable address. Sign presigned object URLs against this one; S3_ENDPOINT above is an in-network hostname that only resolves inside the environment, so a URL signed against it can never be fetched by a browser. |
SMTP_HOST, SMTP_PORT | MailHog enabled | host, 1025 |
Setting your own variables
Three ways to set them:
- Dashboard — Project Settings → Environment Variables.
- nexos.yaml —
env:block. Good for non-secret config. Seenexos.yaml. - Bulk .env import — paste or upload a
.envfile in the dashboard. Existing keys are updated; new keys created.
Listed values are masked — only the first four characters are shown. For real secrets, prefer the dashboard over committed YAML.
Mark credentials as secret. A project that deploys fork pull requests without secrets withholds exactly the variables marked secret — a plain variable still reaches a fork's code, whatever it holds.
Private base images
A FROM on a private registry (a private GHCR package, a company registry) needs the secret BTS_REGISTRY_AUTH, one registry per line:
ghcr.io <user>:<token>
registry.example.com:5000 <user>:<token>BTS hands it to the node's Docker client for the build's pulls only: it never reaches a container, a build argument, a build secret or a workflow job. GHCR takes a classic token with read:packages. It is used on private nodes only: an image pulled on a shared node would stay readable to other workspaces' builds.
A pulled image stays in the node's build cache, where a Dockerfile can reach it by digest without a login. So once a node a project builds on has used these logins, fork pull requests of that project are not deployed without secrets: they get a neutral check instead, as with fork previews off. The Nodes page labels such a node. Before sharing it, moving it to another workspace, or clearing the label once you stop using the logins, archive the previews on it (their images carry the private base) and clear its build cache and images (docker builder prune -af && docker image prune -af). docker images 'nexos-*' should then list nothing.
Encryption at rest
Every value is AES-256-GCM encrypted with a unique random IV before it lands in the database. The plaintext is decrypted only at deploy time, when the value is injected into the container. The encryption key is configured on the API server via ENCRYPTION_KEY (a 32-byte hex string).
API
# Create or update
curl -X PUT https://api.test.buildingswell.com/api/projects/:id/env-vars \
-H "Authorization: Bearer nxs_..." \
-H "Content-Type: application/json" \
-d '{"key": "STRIPE_SECRET_KEY", "value": "sk_test_..."}'
# List (values masked)
curl https://api.test.buildingswell.com/api/projects/:id/env-vars \
-H "Authorization: Bearer nxs_..."
# Delete
curl -X DELETE https://api.test.buildingswell.com/api/projects/:id/env-vars/:varId \
-H "Authorization: Bearer nxs_..."