Skip to main content
Environment variables are how you feed config into your app - API keys, database URLs, feature flags, anything that shouldn’t be hardcoded. Nubo encrypts variables at rest and injects them into your app’s runtime environment. They are not available during the build step.

Where variables live

Variables are scoped to either a Project or a Frame:
  • Project variables apply to every Frame in the Project. Good for things shared across apps (SENTRY_DSN, DATABASE_URL).
  • Frame variables apply to one Frame only. Good for app-specific settings (PORT, NODE_ENV).
If the same key exists at both levels, the Frame value wins.
When you wire a managed database to an app (via nubo.toml), Nubo auto-injects the connection string as a Frame variable named DATABASE_URL (the key is configurable with inject_as in nubo.toml). If you also set DATABASE_URL yourself, the platform-managed value can override it.

Add variables

From the dashboard, open the Frame and go to Frame Settings to manage its variables. The dashboard manages variables at the Frame level only; to set project-scoped variables, use the API (POST /v2/projects/<id>/variables). Keys must start with a letter or underscore and contain only letters, numbers, and underscores ([A-Za-z_][A-Za-z0-9_]*). Via the API (note: the body is a JSON array, so you can add multiple at once):
# project-scoped
curl -X POST https://shuttle.withnubo.com/v2/projects/<project_id>/variables \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '[{"key": "DATABASE_URL", "value": "postgres://..."}]'

# frame-scoped
curl -X POST https://shuttle.withnubo.com/v2/projects/<project_id>/spaces/<space_id>/frames/<frame_id>/variables \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '[{"key": "LOG_LEVEL", "value": "debug"}, {"key": "API_KEY", "value": "..."}]'

Using variables in your app

Variables are available exactly like any other env var - no special SDK or prefix.
// Node.js
const dbUrl = process.env.DATABASE_URL;
# Python
import os
db_url = os.environ["DATABASE_URL"]
// Go
import "os"
dbURL := os.Getenv("DATABASE_URL")
They’re injected into your app’s runtime environment and handle the usual connection strings and secrets. They are not available during the build step.

Update or remove

Changing a variable doesn’t automatically redeploy - trigger a new deploy from the dashboard or push a commit to apply the change. To change a value (the rotate-a-secret flow), PATCH the variable by its ID:
curl -X PATCH https://shuttle.withnubo.com/v2/variables/<variable_id> \
  -H "Authorization: Bearer <your-token>" \
  -H "Content-Type: application/json" \
  -d '{"value": "new-value"}'
To delete:
curl -X DELETE https://shuttle.withnubo.com/v2/variables/<variable_id> \
  -H "Authorization: Bearer <your-token>"

Secrets

Values are encrypted at rest. Listing variables on a Project or Frame returns the keys and metadata only. To see a single value, use the reveal endpoint:
curl https://shuttle.withnubo.com/v2/variables/<variable_id> \
  -H "Authorization: Bearer <your-token>"
Rotate a secret by setting a new value; the old one is overwritten.
Avoid logging environment variables in your app. Runtime logs stream to the dashboard, and accidental secret leakage there is easy to do and hard to undo.

The port your app should listen on

Nubo doesn’t set PORT for you. The port configured on your Frame is the source of truth for routing, so your app must listen on exactly that port. Setting a PORT variable is just a convenience your app can read: it does not change the routed port, and its value must match the Frame’s configured port.