Environment Variables & Configuration
Set secrets and config at the project or app level, understand how precedence works, and redeploy cleanly every time.
What you'll be able to do after this module
Manage environment variables across projects and individual apps — in development, staging, and production — without hardcoding anything or opening the web console.
Prerequisites
- Modules 1–3 complete
- At least one app in your linked project
Why this matters
Environment variables are how you separate configuration from code. Your code stays the same across environments. The variables change. A DATABASE_URL that points to a local Postgres instance in development should point to your managed production database in production — but your code doesn't know or care which one it gets.
The Skytells CLI lets you manage this from the terminal, with a clear distinction between project-level and app-level variables.
List variables
All variables in the project
skytells env ls┌──────────────────┬───────────────────────────┐
│ Key │ Value │
├──────────────────┼───────────────────────────┤
│ NODE_ENV │ production │
│ PORT │ 3000 │
│ DATABASE_URL │ postgres://... │
└──────────────────┴───────────────────────────┘Variables for a specific app
skytells env ls --app abc123Set variables
skytells env set <KEY>=<value>Set one variable
skytells env set NODE_ENV=productionSet several at once
skytells env set API_KEY=abc123 NODE_ENV=production PORT=3000Set for a specific app
skytells env set --app abc123 DATABASE_URL=postgres://user:pass@host:5432/dbValues with spaces
Wrap the value in quotes:
skytells env set APP_NAME="My Application"Project-level vs app-level variables
This distinction trips people up. Here's how to think about it:
| Scope | What it is | When to use it |
|---|---|---|
| Project-level | Available to every app in the project | Shared database URL, shared secret, region config |
| App-level | Scoped to one specific app | Different port numbers, per-app feature flags, app-specific keys |
App-level takes precedence. If PORT is set at the project level to 3000 and at the app level to 8080, the app sees 8080.
Typical configuration pattern
# Shared config for all apps — set at project level
skytells env set \
NODE_ENV=production \
DATABASE_URL=postgres://user:pass@host:5432/mydb \
REDIS_URL=redis://host:6379
# App-specific overrides — set at app level
skytells env set --app api-server PORT=3000 LOG_LEVEL=info
skytells env set --app worker PORT=3001 LOG_LEVEL=debugEnvironment variables don't apply until you redeploy
Setting a variable does not restart your app. The new value takes effect on the next deployment.
# 1. Update the variable
skytells env set --app my-api API_KEY=new-key-here
# 2. Redeploy to apply it
skytells apps redeploy my-api
# 3. Confirm the deploy succeeded
skytells logs my-api --type deployment --followForgetting to redeploy is the most common reason a "fixed" variable change appears to have no effect. Always redeploy after setting variables.
Configuration precedence
The CLI itself also reads configuration from the environment. When you're running in CI or on a server, you can override stored credentials without touching the disk:
| Priority | Source |
|---|---|
| 1 (highest) | Command-line flags |
| 2 | Environment variables (SKYTELLS_TOKEN, SKYTELLS_ACCESS_KEY) |
| 3 (lowest) | Stored credentials (~/.config/skytells/credentials.json) |
This means a CI pipeline can export SKYTELLS_TOKEN and SKYTELLS_ACCESS_KEY and the CLI will use them automatically, ignoring whatever is stored on disk.
export SKYTELLS_TOKEN=sk_pat_your_token_here
export SKYTELLS_ACCESS_KEY=sk_proj_your_key_here
# Now all commands use these, no skytells login needed
skytells deploy my-apiUseful configuration variables
| Variable | What it does |
|---|---|
SKYTELLS_TOKEN | Personal access token — authenticates as your user |
SKYTELLS_ACCESS_KEY | Project access key — scopes commands to a specific project |
SKYTELLS_API_URL | Override the API base URL (rarely needed) |
SKYTELLS_CONFIG_DIR | Use a different directory for credential storage |
Common patterns
Configure a brand new app in one go
APP_ID="abc123"
skytells env set --app "$APP_ID" \
NODE_ENV=production \
PORT=3000 \
DATABASE_URL=postgres://user:pass@host:5432/appdb \
REDIS_URL=redis://host:6379 \
SECRET_KEY=a-very-long-random-string
skytells deploy "$APP_ID"
skytells logs "$APP_ID" --type deployment --followRotate a secret
# 1. Generate a new secret (openssl is a good source of random strings)
NEW_SECRET=$(openssl rand -hex 32)
# 2. Update it
skytells env set --app my-api SECRET_KEY="$NEW_SECRET"
# 3. Update whatever external service also needs the new value first
# (for zero-downtime secret rotation)
# 4. Redeploy
skytells apps redeploy my-apiAudit what an app currently has
skytells env ls --app my-api --jsonReview the JSON output to confirm all required keys are present without running skytells env set unnecessarily.
Security habits
- Never hardcode secrets in code. Use
skytells env setto put them in the variable store. - Use
--appto scope sensitive credentials. A secret only used by one app should be set at app level, not project level. - Rotate secrets by updating the variable, then redeploying — not by pushing a new commit.
- In CI, set
SKYTELLS_TOKENvia your CI platform's secrets manager, not in the repository. See Module 5 for the GitHub Actions pattern.
Quick reference
| What you want | Command |
|---|---|
| List project variables | skytells env ls |
| List app variables | skytells env ls --app <id> |
| Set one variable | skytells env set KEY=value |
| Set multiple variables | skytells env set K1=v1 K2=v2 K3=v3 |
| Set for specific app | skytells env set --app <id> KEY=value |
| Apply changes | skytells apps redeploy <id> |
Up next: Module 5 — Scripting & CI/CD →