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 abc123

Set variables

skytells env set <KEY>=<value>

Set one variable

skytells env set NODE_ENV=production

Set several at once

skytells env set API_KEY=abc123 NODE_ENV=production PORT=3000

Set for a specific app

skytells env set --app abc123 DATABASE_URL=postgres://user:pass@host:5432/db

Values 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:

ScopeWhat it isWhen to use it
Project-levelAvailable to every app in the projectShared database URL, shared secret, region config
App-levelScoped to one specific appDifferent 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=debug

Environment 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 --follow

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:

PrioritySource
1 (highest)Command-line flags
2Environment 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-api

Useful configuration variables

VariableWhat it does
SKYTELLS_TOKENPersonal access token — authenticates as your user
SKYTELLS_ACCESS_KEYProject access key — scopes commands to a specific project
SKYTELLS_API_URLOverride the API base URL (rarely needed)
SKYTELLS_CONFIG_DIRUse 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 --follow

Rotate 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-api

Audit what an app currently has

skytells env ls --app my-api --json

Review the JSON output to confirm all required keys are present without running skytells env set unnecessarily.


Security habits

  1. Never hardcode secrets in code. Use skytells env set to put them in the variable store.
  2. Use --app to scope sensitive credentials. A secret only used by one app should be set at app level, not project level.
  3. Rotate secrets by updating the variable, then redeploying — not by pushing a new commit.
  4. In CI, set SKYTELLS_TOKEN via your CI platform's secrets manager, not in the repository. See Module 5 for the GitHub Actions pattern.

Quick reference

What you wantCommand
List project variablesskytells env ls
List app variablesskytells env ls --app <id>
Set one variableskytells env set KEY=value
Set multiple variablesskytells env set K1=v1 K2=v2 K3=v3
Set for specific appskytells env set --app <id> KEY=value
Apply changesskytells apps redeploy <id>

Up next: Module 5 — Scripting & CI/CD →

On this page