Environment variables allow you to configure your Edge Compute functions without modifying code. They’re ideal for non-sensitive configuration values like API endpoints, feature flags, and performance settings that you want to manage alongside your function code.
Overview
Environment variables in Edge Compute are:
- Declared in configuration — Defined in your function’s
func.toml
- Injected at deployment — Available when your function starts
- Function-scoped — Specific to each function
- Version controlled — Part of your function’s configuration
Defining Environment Variables
Define environment variables in your func.toml under the [env_vars] section:
[edge_compute]
func_id = "your-function-id"
func_name = "my-function"
[env_vars]
SERVICE_NAME = "data-processor"
VERSION = "1.0.0"
LOG_LEVEL = "info"
DEBUG = "false"
MAX_FILE_SIZE = "10485760"
CACHE_TTL = "3600"
API_BASE_URL = "https://api.example.com"
All values are stored as strings. Parse them to the appropriate type in your code.
Accessing Environment Variables
JavaScript
Go
Python
Java
class Config {
constructor() {
this.serviceName = process.env.SERVICE_NAME || 'unknown-service';
this.apiUrl = process.env.API_BASE_URL;
this.debug = process.env.DEBUG === 'true';
this.cacheTtl = parseInt(process.env.CACHE_TTL || '300', 10);
this.maxFileSize = parseInt(process.env.MAX_FILE_SIZE || '10485760', 10);
}
}
const config = new Config();
export async function handler(request) {
return new Response(JSON.stringify({
service: config.serviceName,
api_configured: !!config.apiUrl,
cache_ttl: config.cacheTtl,
debug: config.debug
}), {
headers: { 'Content-Type': 'application/json' }
});
}
package main
import (
"encoding/json"
"net/http"
"os"
"strconv"
)
func handler(w http.ResponseWriter, r *http.Request) {
// Access environment variables
apiURL := os.Getenv("API_BASE_URL")
serviceName := os.Getenv("SERVICE_NAME")
debug, _ := strconv.ParseBool(os.Getenv("DEBUG"))
cacheTTL, _ := strconv.Atoi(os.Getenv("CACHE_TTL"))
// Provide defaults for optional values
if cacheTTL == 0 {
cacheTTL = 300
}
response := map[string]interface{}{
"service": serviceName,
"api_configured": apiURL != "",
"cache_ttl": cacheTTL,
"debug": debug,
}
json.NewEncoder(w).Encode(response)
}
import os
class Function:
def __init__(self):
# Access environment variables
self.service_name = os.getenv("SERVICE_NAME", "unknown-service")
self.api_url = os.environ.get("API_BASE_URL")
self.debug = os.environ.get("DEBUG", "false").lower() == "true"
self.cache_ttl = int(os.environ.get("CACHE_TTL", "300"))
self.max_file_size = int(os.getenv("MAX_FILE_SIZE", "10485760"))
async def handler(self, request):
return {
"service": self.service_name,
"api_configured": bool(self.api_url),
"cache_ttl": self.cache_ttl,
"debug": self.debug
}
import java.util.Map;
import java.util.Optional;
public class ConfigHandler {
private final String serviceName;
private final String apiUrl;
private final boolean debug;
private final int cacheTtl;
public ConfigHandler() {
this.serviceName = getEnv("SERVICE_NAME", "unknown-service");
this.apiUrl = System.getenv("API_BASE_URL");
this.debug = Boolean.parseBoolean(getEnv("DEBUG", "false"));
this.cacheTtl = Integer.parseInt(getEnv("CACHE_TTL", "300"));
}
private String getEnv(String key, String defaultValue) {
String value = System.getenv(key);
return value != null ? value : defaultValue;
}
public Map<String, Object> handle() {
return Map.of(
"service", serviceName,
"api_configured", apiUrl != null,
"cache_ttl", cacheTtl,
"debug", debug
);
}
}
Environment Variables vs Secrets
Use environment variables for non-sensitive configuration. For sensitive data like API keys and passwords, use Secrets instead.
| Feature | Environment Variables | Secrets |
|---|
| Storage | Plain text in func.toml | Encrypted in Telnyx infrastructure |
| Scope | Function-specific | Organization-wide |
| Version Control | ✅ Yes (in git) | ❌ No (separate secure storage) |
| Use Case | Configuration, feature flags | API keys, passwords, tokens |
When to Use Each
Environment Variables ✅
Secrets 🔒
Perfect for non-sensitive configuration:
- Application settings —
LOG_LEVEL, DEBUG_MODE, PORT
- Feature flags —
ENABLE_CACHING, MAINTENANCE_MODE
- Performance tuning —
MAX_FILE_SIZE, BATCH_SIZE, TIMEOUT
- Public endpoints —
API_BASE_URL, CDN_URL, WEBHOOK_URL
- Environment identifiers —
ENVIRONMENT, VERSION, SERVICE_NAME
Use secrets for sensitive data:
- API keys —
STRIPE_API_KEY, TWILIO_API_KEY
- Database credentials —
DATABASE_PASSWORD, REDIS_PASSWORD
- Authentication tokens —
JWT_SECRET, OAUTH_TOKEN
- Encryption keys —
ENCRYPTION_KEY, SIGNING_KEY
CLI Management
Manage environment variables by editing your func.toml file directly, then redeploy:
# Edit func.toml to add/update env_vars
vim func.toml
# Deploy function with updated environment variables
telnyx-edge ship
Environment variable changes take effect on the next deployment.
Best Practices
Naming Conventions
Use UPPER_SNAKE_CASE for consistency with standard environment variable conventions:
[env_vars]
# ✅ Good: Descriptive and consistent
SERVICE_NAME = "data-processor"
MAX_FILE_SIZE = "10485760"
DATABASE_TIMEOUT = "30"
ENABLE_CACHING = "true"
# ❌ Avoid: Vague or inconsistent naming
name = "processor"
maxsize = "10485760"
Type Safety
Store all values as strings and parse them in your code:
[env_vars]
MAX_CONNECTIONS = "100"
TIMEOUT_SECONDS = "30"
ENABLE_FEATURE = "true"
Validation and Defaults
Always validate environment variables and provide sensible defaults:
JavaScript
Go
Python
Java
function getEnvInt(key, defaultVal) {
const raw = process.env[key];
if (raw === undefined || !/^-?\d+$/.test(raw)) {
return defaultVal;
}
return parseInt(raw, 10);
}
function getConfig() {
return {
maxFileSize: getEnvInt('MAX_FILE_SIZE', 10485760),
batchSize: Math.max(1, getEnvInt('BATCH_SIZE', 100)),
timeout: Math.max(1, getEnvInt('TIMEOUT', 30)),
debug: process.env.DEBUG?.toLowerCase() === 'true',
};
}
package main
import (
"os"
"strconv"
)
type Config struct {
MaxFileSize int
BatchSize int
Timeout int
Debug bool
}
func getConfig() Config {
return Config{
MaxFileSize: getEnvInt("MAX_FILE_SIZE", 10485760),
BatchSize: maxInt(1, getEnvInt("BATCH_SIZE", 100)),
Timeout: maxInt(1, getEnvInt("TIMEOUT", 30)),
Debug: os.Getenv("DEBUG") == "true",
}
}
func getEnvInt(key string, defaultVal int) int {
if val, err := strconv.Atoi(os.Getenv(key)); err == nil {
return val
}
return defaultVal
}
func maxInt(a, b int) int {
if a > b {
return a
}
return b
}
import os
def get_config():
return {
'max_file_size': int(os.getenv('MAX_FILE_SIZE', '10485760')),
'batch_size': max(1, int(os.getenv('BATCH_SIZE', '100'))),
'timeout': max(1, int(os.getenv('TIMEOUT', '30'))),
'debug': os.getenv('DEBUG', 'false').lower() == 'true',
}
public class Config {
private final int maxFileSize;
private final int batchSize;
private final int timeout;
private final boolean debug;
public Config() {
this.maxFileSize = getEnvInt("MAX_FILE_SIZE", 10485760);
this.batchSize = Math.max(1, getEnvInt("BATCH_SIZE", 100));
this.timeout = Math.max(1, getEnvInt("TIMEOUT", 30));
this.debug = "true".equalsIgnoreCase(System.getenv("DEBUG"));
}
private int getEnvInt(String key, int defaultVal) {
String value = System.getenv(key);
if (value == null) return defaultVal;
try {
return Integer.parseInt(value);
} catch (NumberFormatException e) {
return defaultVal;
}
}
}
Environment-Specific Values
Use descriptive variable names and document expected values:
[env_vars]
# Environment identification
ENVIRONMENT = "production" # development, staging, production
LOG_LEVEL = "warn" # debug, info, warn, error
# Feature flags
DEBUG_MODE = "false"
METRICS_ENABLED = "true"
Common Patterns
Feature Flags
[env_vars]
ENABLE_NEW_PROCESSING = "true"
ENABLE_BULK_OPERATIONS = "false"
ENABLE_ADVANCED_LOGGING = "true"
BETA_FEATURES_ENABLED = "false"
Service Configuration
[env_vars]
SERVICE_NAME = "user-auth-service"
SERVICE_VERSION = "2.1.0"
ENVIRONMENT = "production"
API_BASE_URL = "https://api.telnyx.com"
CDN_BASE_URL = "https://cdn.telnyx.com"
[env_vars]
MAX_CONNECTIONS = "100"
CONNECTION_TIMEOUT = "30"
READ_TIMEOUT = "60"
BATCH_SIZE = "50"
MAX_RETRIES = "3"
RETRY_DELAY_MS = "1000"
Debugging
Print environment variables for debugging during development:
JavaScript
Go
Python
Java
function debugEnvVars() {
const keys = ['SERVICE_NAME', 'API_BASE_URL', 'DEBUG', 'LOG_LEVEL'];
console.log('=== Environment Variables ===');
for (const key of keys) {
const value = process.env[key] || '(not set)';
console.log(`${key} = ${value}`);
}
}
func debugEnvVars() {
keys := []string{"SERVICE_NAME", "API_BASE_URL", "DEBUG", "LOG_LEVEL"}
fmt.Println("=== Environment Variables ===")
for _, key := range keys {
value := os.Getenv(key)
if value == "" {
value = "(not set)"
}
fmt.Printf("%s = %s\n", key, value)
}
}
import os
def debug_env_vars():
"""Print all environment variables for debugging"""
print("=== Environment Variables ===")
for key in ['SERVICE_NAME', 'API_BASE_URL', 'DEBUG', 'LOG_LEVEL']:
value = os.getenv(key, '(not set)')
print(f"{key} = {value}")
public void debugEnvVars() {
String[] keys = {"SERVICE_NAME", "API_BASE_URL", "DEBUG", "LOG_LEVEL"};
System.out.println("=== Environment Variables ===");
for (String key : keys) {
String value = System.getenv(key);
if (value == null) {
value = "(not set)";
}
System.out.println(key + " = " + value);
}
}
Remove debug logging before deploying to production to avoid exposing configuration details.
Limits
Environment variables are subject to the following constraints:
- Variable names must contain only letters, numbers, and underscores
- Variable names are case-sensitive
- All values are stored and retrieved as strings