Stevora

Environment Variables

Complete environment variable reference

Environment Variables

Stevora uses a Zod-validated environment configuration loaded at startup. If any required variable is missing or invalid, the server will refuse to start and print a clear error message indicating which variables need attention.

All environment variables are defined in src/config/env.ts and validated against a strict schema.

Quick Start

Copy the example file and fill in your values:

cp .env.example .env

For production deployments:

cp .env.production.example .env.production

At minimum, you need to set DATABASE_URL to a valid PostgreSQL connection string. Everything else has sensible defaults for local development.

Full Reference

Application

VariableDescriptionDefaultRequired
NODE_ENVRuntime environment. Must be development, production, or test.developmentNo
PORTPort the API server listens on.3000No
HOSTHost address to bind. Use 0.0.0.0 to accept connections from any interface.0.0.0.0No
LOG_LEVELPino log level. One of: fatal, error, warn, info, debug, trace.infoNo

Database

VariableDescriptionDefaultRequired
DATABASE_URLPostgreSQL connection string. Must be a valid URL. Example: postgresql://user:password@host:5432/dbname?schema=public--Yes

DATABASE_URL is the only strictly required variable. The server will not start without it. For managed databases (RDS, Neon, Supabase), use the connection string provided by your provider. Always include ?schema=public at the end.

Redis

Redis is used by BullMQ for job queues. The worker requires a Redis connection to process workflow steps.

VariableDescriptionDefaultRequired
REDIS_HOSTRedis server hostname.localhostNo
REDIS_PORTRedis server port.6379No
REDIS_PASSWORDRedis authentication password. Leave empty for no auth.--No
REDIS_DBRedis database index (0-15).0No

If you are using Upstash Redis, set REDIS_HOST to your Upstash endpoint (e.g., your-instance.upstash.io) and REDIS_PASSWORD to your Upstash token. The default port 6379 works for most providers.

API Rate Limiting

The API server applies per-key rate limiting to protect against abuse.

VariableDescriptionDefaultRequired
API_RATE_LIMIT_MAXMaximum number of requests per window per API key.100No
API_RATE_LIMIT_WINDOW_MSRate limit window duration in milliseconds.60000 (1 minute)No

For example, the defaults allow 100 requests per minute per API key. To allow higher throughput for production workloads:

API_RATE_LIMIT_MAX=1000
API_RATE_LIMIT_WINDOW_MS=60000

LLM Providers

LLM API keys are optional at the infrastructure level. They are only needed if your workflows use llm type steps. Stevora supports both OpenAI and Anthropic models.

VariableDescriptionDefaultRequired
OPENAI_API_KEYOpenAI API key for GPT models (gpt-4o, gpt-4o-mini, etc.).--No
ANTHROPIC_API_KEYAnthropic API key for Claude models (claude-sonnet-4-20250514, etc.).--No

You only need the keys for the providers your workflows actually use. If all your workflows use Claude, you only need ANTHROPIC_API_KEY. If a workflow specifies a model from a provider whose key is not configured, the step will fail with a clear error.

Dashboard and Admin

VariableDescriptionDefaultRequired
DASHBOARD_URLURL where the Stevora dashboard is hosted. Used for CORS and approval links.http://localhost:3001No
ADMIN_TOKENToken for admin-level API operations (managing workspaces, viewing all runs).--No

Observability

VariableDescriptionDefaultRequired
SENTRY_DSNSentry Data Source Name for error tracking. When set, unhandled errors and failed workflow steps are reported to Sentry.--No
OTEL_EXPORTER_OTLP_ENDPOINTOpenTelemetry collector endpoint for distributed tracing. Example: http://localhost:4318.--No

When OTEL_EXPORTER_OTLP_ENDPOINT is set, the server exports traces in OTLP format. This works with any OpenTelemetry-compatible backend: Jaeger, Grafana Tempo, Honeycomb, Datadog, etc.

Example Configurations

Local Development

.env
NODE_ENV=development
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=debug

DATABASE_URL=postgresql://stevora:stevora@localhost:5432/stevora?schema=public

REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=
REDIS_DB=0

OPENAI_API_KEY=sk-your-dev-key
ANTHROPIC_API_KEY=sk-ant-your-dev-key

Production (Managed Services)

This example uses AWS RDS for PostgreSQL and Upstash for Redis:

.env.production
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info

DATABASE_URL=postgresql://stevora:YOUR_PASSWORD@your-rds-instance.us-east-1.rds.amazonaws.com:5432/stevora?schema=public

REDIS_HOST=your-instance.upstash.io
REDIS_PORT=6379
REDIS_PASSWORD=your-upstash-token
REDIS_DB=0

API_RATE_LIMIT_MAX=500
API_RATE_LIMIT_WINDOW_MS=60000

OPENAI_API_KEY=sk-prod-key
ANTHROPIC_API_KEY=sk-ant-prod-key

ADMIN_TOKEN=your-secure-admin-token
DASHBOARD_URL=https://dashboard.yourdomain.com

SENTRY_DSN=https://examplePublicKey@o0.ingest.sentry.io/0
OTEL_EXPORTER_OTLP_ENDPOINT=https://otel-collector.yourdomain.com:4318

Production (Self-Hosted Everything)

When running PostgreSQL and Redis in Docker alongside Stevora (see Docker Deployment):

.env.production
NODE_ENV=production
PORT=3000
HOST=0.0.0.0
LOG_LEVEL=info

DATABASE_URL=postgresql://stevora:stevora_secure_pw@postgres:5432/stevora?schema=public

REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD=stevora_redis_pw
REDIS_DB=0

OPENAI_API_KEY=sk-prod-key
ANTHROPIC_API_KEY=sk-ant-prod-key

ADMIN_TOKEN=your-secure-admin-token

When using Docker Compose, use the service name as the hostname (postgres, redis) instead of localhost. Docker's internal DNS resolves service names to container IP addresses.

Validation

Environment variables are validated at startup using Zod. The validation schema enforces types, formats, and constraints:

src/config/env.ts
const envSchema = z.object({
  NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
  PORT: z.coerce.number().int().positive().default(3000),
  HOST: z.string().default('0.0.0.0'),
  LOG_LEVEL: z.enum(['fatal', 'error', 'warn', 'info', 'debug', 'trace']).default('info'),

  DATABASE_URL: z.string().url(),

  REDIS_HOST: z.string().default('localhost'),
  REDIS_PORT: z.coerce.number().int().positive().default(6379),
  REDIS_PASSWORD: z.string().optional(),
  REDIS_DB: z.coerce.number().int().min(0).default(0),

  API_RATE_LIMIT_MAX: z.coerce.number().int().positive().default(100),
  API_RATE_LIMIT_WINDOW_MS: z.coerce.number().int().positive().default(60000),

  OPENAI_API_KEY: z.string().optional(),
  ANTHROPIC_API_KEY: z.string().optional(),

  DASHBOARD_URL: z.string().default('http://localhost:3001'),
  ADMIN_TOKEN: z.string().optional(),

  SENTRY_DSN: z.string().optional(),
  OTEL_EXPORTER_OTLP_ENDPOINT: z.string().optional(),
});

If validation fails, you will see a startup error like this:

Error: Invalid environment variables:
  DATABASE_URL: Required
  REDIS_PORT: Expected number, received string

Fix the reported variables and restart the server.

Next Steps