Environments in Applad allow you to run the same application configuration with different settings for development, staging, and production. Environment-specific overrides are supported throughout the configuration system.
Defining Environments
Environments are defined in project.yaml:
environments :
- name : "development"
url : "http://localhost:8080"
infrastructure :
type : "local"
- name : "staging"
url : "https://staging-api.myapp.com"
infrastructure :
type : "vps"
host : "staging.acme-corp.com"
user : "applad"
ssh_key : "ci-github-actions"
- name : "production"
url : "https://api.myapp.com"
infrastructure :
type : "vps"
host : "prod-01.acme-corp.com"
user : "applad"
ssh_key : "ci-github-actions"
Each environment specifies:
Name : Environment identifier (e.g., development, staging, production)
URL : Base URL where the environment is accessible
Infrastructure : Where and how to deploy this environment
Environment Overrides
Many configuration files support an environment_overrides section that allows per-environment customization.
Database Overrides
Use different database adapters or connection settings per environment:
# database/database.yaml
connections :
- id : "primary"
default : true
adapter : "postgres"
url : ${DATABASE_URL}
pool :
min : 2
max : 20
environment_overrides :
development :
primary :
adapter : "sqlite"
path : "./data/dev.db"
staging :
primary :
adapter : "postgres"
url : ${STAGING_DATABASE_URL}
pool :
min : 1
max : 5
How Database Overrides Work
Base connections configuration is loaded
If running in a specific environment (e.g., development), the override for that environment is deep-merged
Environment-specific settings take precedence over base settings
Only specified fields are overridden — others inherit from base config
In the example above:
Development : Uses SQLite with a local file
Staging : Uses Postgres with reduced connection pool
Production : Uses base Postgres config with full pool size
Storage Overrides
Use different storage backends per environment:
# storage/storage.yaml
adapter : "s3"
config :
bucket : ${S3_BUCKET}
region : ${S3_REGION}
access_key : ${S3_ACCESS_KEY}
secret_key : ${S3_SECRET_KEY}
ssl : true
environment_overrides :
development :
adapter : "local"
config :
path : "./data/storage"
staging :
adapter : "s3"
config :
bucket : ${STAGING_S3_BUCKET}
Development : Uses local filesystem for fast iterationStaging : Uses dedicated staging S3 bucketProduction : Uses base S3 configuration
Messaging Overrides
Use different email providers per environment:
# messaging/messaging.yaml
email :
enabled : true
provider : "resend"
config :
api_key : ${RESEND_API_KEY}
from : "[email protected] "
from_name : "My App"
environment_overrides :
production :
email :
provider : "ses"
config :
access_key : ${AWS_SES_ACCESS_KEY}
secret_key : ${AWS_SES_SECRET_KEY}
region : "eu-west-1"
from : "[email protected] "
Environment Variables
Each environment can have its own set of environment variables:
Instance Level
# .env (instance root)
APPLAD_SECRET = ...
OTEL_ENDPOINT = ...
Organization Level
# orgs/acme-corp/.env
ORG_SPECIFIC_VAR = ...
Project Level
# orgs/acme-corp/mobile-app/.env
DATABASE_URL = postgres://user:pass@localhost:5432/dev
STAGING_DATABASE_URL = postgres://user:[email protected] :5432/staging
S3_BUCKET = my-app-production
STAGING_S3_BUCKET = my-app-staging
Applad auto-generates .env.example files with annotations showing which config files reference each variable and which environment they’re used in.
Running Specific Environments
Deploy to a specific environment:
# Run development (default)
applad up
# Run staging
applad up --env staging
# Run production
applad up --env production
Feature Flags Per Environment
Feature flags can have different values per environment:
# flags/new-dashboard.yaml
key : "new-dashboard"
type : "boolean"
default : false
description : "New dashboard UI redesign"
environments :
development : true
staging : true
production : false
A/B testing flags can have different variants per environment: key : "checkout-flow"
type : "multivariate"
default : "control"
variants : [ "control" , "variant-a" , "variant-b" ]
environments :
development : "variant-a"
staging : "control"
production : "control"
Cloud Adapters Per Environment
Cloud resources can be environment-specific:
# project.yaml
environments :
- name : "production"
url : "https://api.myapp.com"
infrastructure :
type : "vps"
host : "prod-01.acme-corp.com"
cloud_adapters :
- provider : "aws"
region : "eu-west-1"
credentials : "aws-production"
services :
- s3
- ses
- rds
Development typically has no cloud adapters (uses local resources), while staging and production use cloud services.
Observability Per Environment
Configure different logging levels and sampling rates:
# observability/observability.yaml
logging :
level : "info"
structured : true
tracing :
enabled : true
sampling_rate : 0.1
environment_overrides :
development :
logging :
level : "debug"
tracing :
sampling_rate : 1.0
production :
logging :
level : "warn"
tracing :
sampling_rate : 0.05
Environment-Specific Permissions
Some permissions are scoped to specific environments:
# org.yaml roles
- name : "developer"
permissions :
- "db:migrate:staging"
- "db:shell:development"
- "infrastructure:apply:development"
- "infrastructure:apply:staging"
# No infrastructure:apply:production
This allows developers to apply infrastructure changes to dev and staging, but not production.
Best Practices
Use Local Resources in Development
Configure development to use local resources (SQLite, local storage) for fast iteration without cloud dependencies: environment_overrides :
development :
primary :
adapter : "sqlite"
path : "./data/dev.db"
Match Staging to Production
Keep staging as close to production as possible to catch issues before they reach users. Use the same adapters and providers, but with dedicated staging resources: environment_overrides :
staging :
primary :
adapter : "postgres" # Same as production
url : ${STAGING_DATABASE_URL} # But different database
pool :
max : 5 # Can be smaller
Use Environment-Specific Secrets
Never share secrets between environments. Use different API keys, database passwords, and credentials for each environment: # Development
DATABASE_URL = postgres://dev:devpass@localhost:5432/dev
# Staging
STAGING_DATABASE_URL = postgres://staging:[email protected] :5432/staging
# Production
DATABASE_URL = postgres://prod:[email protected] :5432/prod
Test features in development, then staging, then production: # flags/new-feature.yaml
environments :
development : true # Enable immediately
staging : true # Enable after dev testing
production : false # Enable after staging validation
Complete Example
Here’s a complete example showing environment overrides across multiple systems:
# project.yaml
environments :
- name : "development"
url : "http://localhost:8080"
infrastructure :
type : "local"
- name : "staging"
url : "https://staging-api.myapp.com"
infrastructure :
type : "vps"
host : "staging.acme-corp.com"
- name : "production"
url : "https://api.myapp.com"
infrastructure :
type : "vps"
host : "prod-01.acme-corp.com"
cloud_adapters :
- provider : "aws"
region : "eu-west-1"
credentials : "aws-production"
services : [ s3 , ses ]
# database/database.yaml
connections :
- id : "primary"
adapter : "postgres"
url : ${DATABASE_URL}
environment_overrides :
development :
primary :
adapter : "sqlite"
path : "./data/dev.db"
staging :
primary :
url : ${STAGING_DATABASE_URL}
# storage/storage.yaml
adapter : "s3"
config :
bucket : ${S3_BUCKET}
environment_overrides :
development :
adapter : "local"
config :
path : "./data/storage"
Next Steps
Database Schema Configure database connections and migrations
Storage Set up file storage buckets
Functions Deploy serverless functions
Deployments Configure deployment pipelines