Environment Variables
Comprehensive guide to all environment variables for configuring Aya’s backend and frontend.
Configuration Hierarchy
Aya uses a hierarchical configuration system:
Base config (config.json)
Environment-specific overrides (.env.local, .env.production)
Environment variables (highest priority)
Environment variables use double underscore (__) as the delimiter for nested keys.
Example
{
"auth" : {
"jwt_secret" : "default-secret"
},
"conn" : {
"targets" : {
"default" : {
"protocol" : "postgres" ,
"dsn" : "postgres://localhost:5432/aya"
}
}
}
}
Override with environment variables:
AUTH__JWT_SECRET = production-secret
CONN__targets__default__dsn = postgres://prod-host:5432/aya
Backend Environment Variables
Core Configuration
# Environment (development, production, test)
ENV = production
# Server port
PORT = 8080
# Site URI (for CORS, redirects)
SITE_URI = https://aya.is
Authentication
JWT
GitHub OAuth
Apple OAuth
# JWT signing secret (REQUIRED)
AUTH__JWT_SECRET = your-secret-key-here
# JWT expiration (default: 24h)
AUTH__JWT_EXPIRATION = 86400
Database
# Database protocol
CONN__targets__default__protocol = postgres
# Full connection string
CONN__targets__default__dsn = postgres://user:password@host:5432/database? sslmode = require
# Connection pool settings
CONN__targets__default__max_open_conns = 25
CONN__targets__default__max_idle_conns = 10
CONN__targets__default__conn_max_lifetime = 3600
Always use sslmode=require in production for secure connections.
External Services
Email (Resend)
S3 Storage
Telegram Bot
AI Services (OpenAI)
# Resend API key for transactional emails
RESEND__API_KEY = re_123456789
# From email address
RESEND__FROM_EMAIL = [email protected]
RESEND__FROM_NAME = Aya Community
Workers
# Background workers configuration
WORKERS__ENABLED = true
# Telegram bot worker
WORKERS__TELEGRAM_BOT__ENABLED = true
WORKERS__TELEGRAM_BOT__INTERVAL = 5s
# Email worker
WORKERS__EMAIL__ENABLED = true
WORKERS__EMAIL__INTERVAL = 30s
# Resource sync worker (GitHub, etc.)
WORKERS__RESOURCE_SYNC__ENABLED = true
WORKERS__RESOURCE_SYNC__INTERVAL = 1h
Features
# Feature flags
FEATURES__DUMMY = true
FEATURES__TELEGRAM = true
FEATURES__APPLE_AUTH = false
Frontend Environment Variables
Frontend uses VITE_ prefix for variables bundled at build time.
Security : NEVER put secrets in VITE_ variables - they’re exposed in the client bundle!
Build-Time Variables
# Backend API URL (client-side)
VITE_BACKEND_URI = https://api.aya.is
# Frontend host URL
VITE_HOST = https://aya.is
# Telegram bot username (for deep links)
VITE_TELEGRAM_BOT_USERNAME = aya_is_bot
# Environment mode
VITE_MODE = production # or development
Runtime Variables (SSR)
# Backend URI for server-side requests
BACKEND_URI = http://services:8080
# Node environment
NODE_ENV = production
Feature Flags
# Enable/disable authentication providers
VITE_AUTH_GITHUB_ENABLED = true
VITE_AUTH_APPLE_ENABLED = false
Upload Validation
# Allowed URI prefixes for story uploads (comma-separated)
VITE_ALLOWED_URI_PREFIXES_STORIES = https://objects.aya.is/
# Allowed URI prefixes for profile pictures
VITE_ALLOWED_URI_PREFIXES_PROFILES = https://objects.aya.is/,https://avatars.githubusercontent.com/
Development vs Production
Development (.env.local)
# Minimal config for local development
AUTH__JWT_SECRET = dev-secret-not-for-production
CONN__targets__default__dsn = postgres://postgres:s3cr3t@localhost:5432/postgres? sslmode = disable
# Optional: Enable Telegram bot in dev
TELEGRAM__ENABLED = true
TELEGRAM__BOT_TOKEN = your-dev-bot-token
TELEGRAM__USE_POLLING = true
WORKERS__TELEGRAM_BOT__ENABLED = true
Production
# Production secrets (NEVER commit to git)
ENV = production
PORT = 8080
SITE_URI = https://aya.is
LOG__LEVEL = WARN
LOG__PRETTY = false
# Strong JWT secret (use: openssl rand -hex 32)
AUTH__JWT_SECRET = f3a8b7c2d1e9f6a4b8c7d2e1f9a6b3c8d7e2f1a9b6c3d8e7f2a1b9c6d3e8f7a2
# Production database with SSL
CONN__targets__default__protocol = postgres
CONN__targets__default__dsn = postgres://aya:[email protected] :5432/aya? sslmode = require
# External services
RESEND__API_KEY = re_prod_key
S3__ACCESS_KEY_ID = AKIAIOSFODNN7EXAMPLE
S3__SECRET_ACCESS_KEY = wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
S3__BUCKET = aya-production-uploads
S3__PUBLIC_URL = https://objects.aya.is
# Telegram webhook (not polling)
TELEGRAM__ENABLED = true
TELEGRAM__BOT_TOKEN = prod-bot-token
TELEGRAM__WEBHOOK_URL = https://api.aya.is/telegram/webhook
TELEGRAM__WEBHOOK_SECRET = webhook-secret-key
# Workers
WORKERS__ENABLED = true
WORKERS__TELEGRAM_BOT__ENABLED = false # Using webhook, not polling
WORKERS__EMAIL__ENABLED = true
WORKERS__RESOURCE_SYNC__ENABLED = true
Docker Compose Integration
compose.yml (Development)
services :
services :
environment :
ENV : development
PORT : 8080
LOG__LEVEL : INFO
LOG__PRETTY : true
CONN__targets__default__protocol : postgres
CONN__targets__default__dsn : postgres://postgres:s3cr3t@postgres:5432/postgres?sslmode=disable
env_file :
- apps/services/.env.local # Secrets not in compose.yml
compose.production.yml
services :
services :
environment :
ENV : production
PORT : 8080
LOG__LEVEL : WARN
LOG__PRETTY : false
env_file :
- .env # All secrets from .env file
Secrets Management
Using Docker Secrets
services :
services :
secrets :
- jwt_secret
- db_password
- s3_secret_key
environment :
AUTH__JWT_SECRET_FILE : /run/secrets/jwt_secret
CONN__targets__default__dsn : postgres://aya:$(cat /run/secrets/db_password)@postgres:5432/aya
secrets :
jwt_secret :
external : true
db_password :
external : true
s3_secret_key :
external : true
Create secrets:
echo "my-jwt-secret" | docker secret create jwt_secret -
echo "db-password" | docker secret create db_password -
Using Kubernetes Secrets
apiVersion : v1
kind : Secret
metadata :
name : aya-secrets
type : Opaque
stringData :
jwt-secret : "your-jwt-secret"
db-password : "your-db-password"
s3-secret-key : "your-s3-secret"
apiVersion : apps/v1
kind : Deployment
metadata :
name : aya-backend
spec :
template :
spec :
containers :
- name : backend
env :
- name : AUTH__JWT_SECRET
valueFrom :
secretKeyRef :
name : aya-secrets
key : jwt-secret
- name : CONN__targets__default__dsn
value : "postgres://aya:$(DB_PASSWORD)@postgres:5432/aya"
envFrom :
- secretRef :
name : aya-secrets
Environment Variable Validation
The backend validates required environment variables on startup:
pkg/ajan/configfx/validator.go
type Config struct {
Env string `required:"true" validate:"oneof=development production test"`
Port int `required:"true" validate:"min=1,max=65535"`
SiteURI string `required:"true" validate:"url"`
Auth struct {
JWTSecret string `required:"true" validate:"min=32"`
}
Conn struct {
Targets map [ string ] struct {
Protocol string `required:"true" validate:"oneof=postgres mysql"`
DSN string `required:"true"`
}
}
}
Missing or invalid values will cause startup to fail with clear error messages.
Best Practices
Use .env.local for secrets
NEVER commit secrets to git:.env
.env.local
.env.production
Use .env.example as a template: AUTH__JWT_SECRET = change-me
CONN__targets__default__dsn = postgres://user:password@localhost:5432/aya
S3__ACCESS_KEY_ID = your-key-id
S3__SECRET_ACCESS_KEY = your-secret-key
Generate strong JWT secrets
Use cryptographically secure random strings: # Generate 32-byte hex string
openssl rand -hex 32
# Or base64
openssl rand -base64 32
Use different secrets per environment
NEVER use the same JWT secret in development and production.# Development
AUTH__JWT_SECRET = dev-only-secret
# Production
AUTH__JWT_SECRET = $( openssl rand -hex 32 )
Enable SSL for production databases
Always use sslmode=require in production: # Development (OK)
CONN__targets__default__dsn = postgres://localhost:5432/aya? sslmode = disable
# Production (REQUIRED)
CONN__targets__default__dsn = postgres://host:5432/aya? sslmode = require
Troubleshooting
Check environment variable names (case-sensitive, use __ not .): # WRONG
auth.jwt_secret =secret
AUTH.JWT_SECRET =secret
# CORRECT
AUTH__JWT_SECRET = secret
VITE_ variables not updating
Rebuild frontend after changing VITE_ variables: cd apps/webclient
deno task build
Or restart dev server:
Docker secrets not working
Ensure secrets are created before starting services: docker secret ls
# Should show: jwt_secret, db_password, etc.
# Create if missing
echo "secret-value" | docker secret create jwt_secret -
Quick Reference
Essential Variables
Minimal config to get Aya running:
# Backend
AUTH__JWT_SECRET = your-secret-here
CONN__targets__default__dsn = postgres://user:pass@host:5432/db
# Frontend
VITE_BACKEND_URI = http://localhost:8080
VITE_HOST = http://localhost:3000
Full Production Config
See the production example above for a complete configuration.
Next Steps
Docker Deployment Deploy with environment variables in Docker
Nix Deployment Use Nix modules for configuration
Development Setup Configure local environment
Installation Initial setup guide