Environment Variables & Configuration
Learn how to configure your applications using environment variables, secrets, and runtime configuration.
Environment Variable Types
The Strongly platform supports three types of environment variables:
- Regular Variables: Standard configuration values
- Secrets: Encrypted sensitive data (passwords, API keys)
- Build-time Variables: Values injected during Docker build
Defining Variables in Manifest
Regular Environment Variables
Standard configuration values accessible at runtime:
env:
- name: API_URL
value: "https://api.example.com"
required: false
description: Backend API endpoint
- name: LOG_LEVEL
value: "info"
required: false
description: Application log level (debug, info, warn, error)
Secrets
Sensitive data stored as Kubernetes Secrets and encrypted at rest:
env:
- name: DATABASE_PASSWORD
value: "" # Provided by user during deployment
required: true
secret: true
description: PostgreSQL database password
- name: JWT_SECRET
value: ""
required: true
secret: true
description: Secret for JWT token signing
- name: API_KEY
value: ""
required: true
secret: true
description: Third-party API key
- Never commit secrets to version control
- Always mark sensitive values with
secret: true - Leave
valueempty for secrets - users provide them during deployment - Rotate secrets regularly
Build-time Variables
Values passed to Docker build as --build-arg:
env:
- name: NODE_ENV
value: "production"
buildtime: true
description: Node.js environment
- name: REACT_APP_VERSION
value: "1.0.0"
buildtime: true
description: Application version displayed in UI
- name: REACT_APP_API_URL
value: "https://api.acme.com"
buildtime: true
description: Baked into React build
Using in Dockerfile:
ARG NODE_ENV
ARG REACT_APP_VERSION
ARG REACT_APP_API_URL
ENV NODE_ENV=${NODE_ENV}
ENV REACT_APP_VERSION=${REACT_APP_VERSION}
ENV REACT_APP_API_URL=${REACT_APP_API_URL}
RUN npm run build
Variable Precedence
Environment variables can come from multiple sources. The platform applies them in this order (last wins):
- Manifest defaults (
strongly.manifest.yaml) - Platform injected (
STRONGLY_SERVICES, etc.) - User provided (during deployment)
- .env file (if included in archive)
Platform-Injected Variables
The platform automatically injects these variables into all app pods:
Core Platform Variables
| Variable | Description | Example |
|---|---|---|
STRONGLY_URL | Relative proxy path for the app (no domain) | /api/proxy/app-xyz123 |
STRONGLY_HOST | Platform host URL | https://app.strongly.ai |
STRONGLY_APP_ID | Unique Kubernetes-friendly app identifier | app-xyz123 |
STRONGLY_SERVICES | JSON with all connected services | See STRONGLY_SERVICES |
STRONGLY_CPU | CPU allocation specification | 0.5 |
STRONGLY_MEMORY | Memory allocation specification | 1GB |
STRONGLY_DISK | Disk allocation specification | 5GB |
STRONGLY_URL (Critical for Frontend Apps)
STRONGLY_URL is a relative path (e.g., /api/proxy/app-xyz123), not a full URL. It provides the base proxy path through which requests reach your app.
When your app is displayed in the platform's iframe, frontend code needs to make API calls through the proxy. STRONGLY_URL provides the correct base path.
Why this matters: Apps are accessed via /apps/app-xyz/view. Without STRONGLY_URL:
- API calls go to
/api/...(wrong - hits the platform, not your app) - With
STRONGLY_URL, API calls go to/api/proxy/app-xyz/api/...(correct - proxied to your app)
Server-side: Inject runtime config into HTML
// In your Express server's static file handler
app.get('*', (req, res) => {
const indexPath = path.join(__dirname, '../client/dist/index.html');
fs.readFile(indexPath, 'utf8', (err, data) => {
if (err) return res.status(500).send('Server error');
// Build runtime config from platform environment variables
const runtimeConfig = {
STRONGLY_URL: process.env.STRONGLY_URL || '',
STRONGLY_HOST: process.env.STRONGLY_HOST || '',
STRONGLY_APP_ID: process.env.STRONGLY_APP_ID || '',
API_URL: '/api'
};
// Inject into HTML
const modifiedHtml = data.replace(
'</head>',
`<script>window.__RUNTIME_CONFIG__ = ${JSON.stringify(runtimeConfig)};</script></head>`
);
res.send(modifiedHtml);
});
});
Client-side: Use runtime config for API calls
// In your API service (e.g., api.ts)
function getApiBaseUrl() {
const config = window.__RUNTIME_CONFIG__ || {};
if (config.STRONGLY_URL) {
// STRONGLY_URL = /api/proxy/app-xyz (relative path)
// Append /api for your backend API routes
return `${config.STRONGLY_URL.replace(/\/$/, '')}/api`;
}
// Fallback for local development
return '/api';
}
const api = axios.create({
baseURL: getApiBaseUrl(),
timeout: 30000,
});
STRONGLY_SERVICES
JSON object containing all connected service credentials. See STRONGLY_SERVICES Integration for the complete structure and usage examples.
const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
// Access addons (grouped by type)
const mongoAddons = services.services?.addons?.mongodb || [];
const firstMongo = mongoAddons[0];
// Access AI models
const aiGateway = services.services?.aigateway;
const models = aiGateway?.available_models || [];
Authentication Headers
The platform injects authentication headers on all proxied requests to your app:
| Header | Description | Example Values |
|---|---|---|
X-Auth-User-Id | Unique user identifier | FsbHXu34LHShtmb9L |
X-Auth-User-Email | User's email address | user@example.com |
X-Auth-User-Name | User's display name | John Doe |
X-Auth-App-Role | User's role for this app | owner, admin, user, viewer, anonymous |
X-Auth-Platform-Role | Platform-wide role | admin, user |
X-Auth-Authenticated | Authentication status | true or false |
App Role Values:
owner: The user who created/owns the appadmin: User with admin-level access to the appuser: User with standard access to the appviewer: Authenticated user accessing a public appanonymous: Unauthenticated user accessing a public app
Using auth headers in Express:
// Middleware to extract user from headers
app.use((req, res, next) => {
req.user = {
id: req.headers['x-auth-user-id'] || 'anonymous',
email: req.headers['x-auth-user-email'] || null,
name: req.headers['x-auth-user-name'] || 'Anonymous',
appRole: req.headers['x-auth-app-role'] || 'anonymous',
platformRole: req.headers['x-auth-platform-role'] || null,
authenticated: req.headers['x-auth-authenticated'] === 'true'
};
next();
});
// Use in routes
app.get('/api/profile', (req, res) => {
if (!req.user.authenticated) {
return res.status(401).json({ error: 'Not authenticated' });
}
res.json({ user: req.user });
});
// Check for owner/admin access
app.delete('/api/settings', (req, res) => {
if (!['owner', 'admin'].includes(req.user.appRole)) {
return res.status(403).json({ error: 'Admin access required' });
}
// ... handle admin action
});
Using .env Files
Include a .env file in your archive for additional variables:
.env:
# Development settings
DEBUG=true
CACHE_TTL=300
# Feature flags
FEATURE_NEW_UI=enabled
FEATURE_BETA_API=disabled
.envfiles are NOT encrypted- Don't put secrets in
.envfiles - Use manifest
secret: truefor sensitive data .envvalues override manifest defaults
Accessing Variables in Code
Node.js
// Access individual variables
const nodeEnv = process.env.NODE_ENV;
const apiKey = process.env.API_KEY;
// Parse STRONGLY_SERVICES
const services = JSON.parse(process.env.STRONGLY_SERVICES || '{}');
// Access a MongoDB addon
const mongoAddons = services.services?.addons?.mongodb || [];
if (mongoAddons.length > 0) {
const mongo = mongoAddons[0];
const connectionString = mongo.connection?.connection_string;
}
Python
import os
import json
# Access individual variables
flask_env = os.environ.get('FLASK_ENV', 'development')
api_key = os.environ.get('API_KEY')
# Parse STRONGLY_SERVICES
services = json.loads(os.environ.get('STRONGLY_SERVICES', '{}'))
# Access a MongoDB addon
mongo_addons = services.get('services', {}).get('addons', {}).get('mongodb', [])
if mongo_addons:
mongo = mongo_addons[0]
connection_string = mongo.get('connection', {}).get('connection_string')
React (Build-time)
Variables prefixed with REACT_APP_ are embedded at build time:
// Access in React components
const apiUrl = process.env.REACT_APP_API_URL;
const version = process.env.REACT_APP_VERSION;
console.log('API URL:', apiUrl);
React environment variables must be prefixed with REACT_APP_ and marked as buildtime: true in the manifest.
Runtime Configuration Injection
For React/static apps, inject runtime configuration without rebuilding:
Manifest Configuration
type: react
static:
root: /app/build
config_file: /app/build/config.js
config_placeholder: __APP_CONFIG__
Template File
Create public/config.js with placeholder:
window.__APP_CONFIG__ = __APP_CONFIG__;
HTML Reference
Include in public/index.html:
<!DOCTYPE html>
<html>
<head>
<script src="%PUBLIC_URL%/config.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
Access in React
const config = window.__APP_CONFIG__;
function App() {
const apiUrl = config.REACT_APP_API_URL;
return <div>API: {apiUrl}</div>;
}
At runtime, the platform replaces __APP_CONFIG__ with actual values:
window.__APP_CONFIG__ = {
"REACT_APP_API_URL": "https://api.acme.com",
"REACT_APP_VERSION": "1.0.0"
};
Updating Variables
During Deployment
- Navigate to Apps and deploy your application
- Configure environment variables in the form
- Provide values for required secrets
- Deploy application
After Deployment
- Navigate to app details page
- Click Configuration tab
- Update variable values
- Redeploy to apply changes
Environment variable changes require redeployment. The app will restart with new values.
Validation
The platform validates environment variables:
- Required variables must have values
- Secret variables are encrypted before storage
- Build-time variables are passed to Docker build
- Invalid names (spaces, special chars) are rejected
Best Practices
Naming Conventions
env:
# Good: Uppercase with underscores
- name: DATABASE_URL
- name: JWT_SECRET
- name: MAX_CONNECTIONS
# Bad: Lowercase or mixed case
- name: databaseUrl # Avoid
- name: jwtSecret # Avoid
Secret Management
env:
# Store as secret
- name: DB_PASSWORD
secret: true
required: true
# Store as regular variable
- name: DB_HOST
value: "postgres.example.com"
secret: false
Documentation
env:
- name: CACHE_TTL
value: "300"
description: Cache time-to-live in seconds
- name: MAX_RETRIES
value: "3"
description: Maximum number of retry attempts for failed requests
Troubleshooting
Variable Not Found
Problem: Application can't find environment variable
Solutions:
- Check variable is defined in manifest
- Verify variable name matches exactly (case-sensitive)
- Ensure app was redeployed after adding variable
- Check pod logs for startup errors
Secret Not Decrypted
Problem: Secret value appears as *** or is empty
Solutions:
- Verify secret value was provided during deployment
- Check secret is marked with
secret: truein manifest - Redeploy application if secret was added later
Build-time Variable Not Available
Problem: Variable not available during Docker build
Solutions:
- Ensure
buildtime: trueis set in manifest - Add
ARGdirective in Dockerfile - Check build logs for variable injection