Organizing Bundles
Best practices for structuring bundles, naming conventions, and scaling your environment variable management.
Overview
As your project grows, managing dozens or hundreds of environment variables across multiple environments becomes challenging. This guide provides proven strategies for organizing bundles effectively.
What you'll learn:
- Bundle naming conventions
- Environment-based organization strategies
- Tagging and categorization
- Security best practices
- Performance optimization
Time to complete: ~15 minutes reading
Bundle Naming Conventions
Hierarchical Naming (Recommended)
Use forward slashes to create hierarchical namespaces:
environment/service
Examples:
production/api
production/web
production/worker
staging/api
staging/web
development/api
development/web
Benefits:
- Easy to filter and search
- Clear visual hierarchy
- Scalable as projects grow
- Consistent with file path conventions
Flat Naming (Simple Projects)
For smaller projects, flat names work well:
prod-api
staging-api
dev-api
test-api
When to use:
- Single service or monolithic app
- 5 or fewer environments
- Small team (1-5 people)
Avoid These Anti-Patterns
❌ Too generic:
env1, env2, secrets, config
Problem: Hard to remember which is which.
❌ Inconsistent formats:
prod-api, staging/web, dev_worker, test-CLI
Problem: Confusing to search and filter.
❌ Too verbose:
production-environment-backend-api-service-v2
Problem: Hard to type, clutters UI.
Organization Strategies
Strategy 1: Environment-First
Structure: environment/service
production/
- production/api
- production/web
- production/worker
- production/db-migrate
staging/
- staging/api
- staging/web
development/
- development/api
- development/web
Best for:
- Teams that deploy environments together
- When environment parity is critical
- Promoting bundles from dev → staging → prod
Tags to add:
production/api: "production,api,backend"
production/web: "production,frontend,react"
staging/api: "staging,api,backend"
Strategy 2: Service-First
Structure: service/environment
api/
- api/production
- api/staging
- api/development
web/
- web/production
- web/staging
- web/development
worker/
- worker/production
- worker/staging
Best for:
- Microservices architectures
- Service-specific teams
- When services are deployed independently
Tags to add:
api/production: "api,production,node"
api/staging: "api,staging,node"
web/production: "web,production,react"
Strategy 3: Feature-Based
Structure: feature/environment
auth/
- auth/production
- auth/staging
payments/
- payments/production
- payments/staging
notifications/
- notifications/production
Best for:
- Feature-flag driven development
- Multi-tenant applications
- Large teams with feature ownership
Strategy 4: Hybrid Approach
Structure: environment/team/service
production/
- production/platform/api
- production/platform/web
- production/growth/analytics
- production/growth/experiments
staging/
- staging/platform/api
- staging/platform/web
Best for:
- Large organizations (50+ people)
- Multiple teams with separate ownership
- Complex microservices ecosystems
Tagging Strategy
Purpose of Tags
Tags enable:
- Fast filtering (
?tag=production) - Cross-cutting searches (e.g., all databases across environments)
- Team-based access control (future: M9+)
Recommended Tag Categories
1. Environment tags:
production, staging, development, test, local
2. Technology tags:
postgres, redis, stripe, aws, sendgrid, react, node
3. Service type tags:
api, frontend, worker, database, cache, queue
4. Team/ownership tags:
platform-team, growth-team, security-team
5. Sensitivity tags:
pci-compliant, gdpr, hipaa, public-safe
Tagging Examples
Production API bundle:
Name: production/api
Tags: production,api,backend,node,postgres,platform-team
Staging web bundle:
Name: staging/web
Tags: staging,frontend,react,vercel,growth-team
Development database bundle:
Name: development/db
Tags: development,database,postgres,local
Tag Conventions
✅ Do:
- Use lowercase
- Use hyphens for multi-word tags (
platform-team) - Keep tags short and descriptive
- Use consistent terminology
❌ Don't:
- Use spaces (
platform team→ useplatform-team) - Create too many tags (5-8 per bundle is plenty)
- Use redundant tags (don't tag
production/apiwithproductionANDprod)
Security Best Practices
Principle of Least Privilege
Bundle scope:
✅ Good: production/web (only web app secrets)
❌ Bad: production/all (entire production environment)
Why: Smaller bundles reduce blast radius if a bundle is compromised.
Secret Marking
Always mark these as secrets:
- API keys (Stripe, SendGrid, etc.)
- Database passwords
- Private keys and certificates
- OAuth client secrets
- Signing keys (JWT, cookies)
Can be non-secret:
- Public URLs
- Port numbers
- Feature flags (non-sensitive)
- Public API base URLs
Example:
// Bundle: production/api
{
"DATABASE_URL": "postgres://...", // Secret ✓
"STRIPE_SECRET_KEY": "sk_live_...", // Secret ✓
"API_PORT": "8080", // Non-secret ✓
"PUBLIC_URL": "https://api.example.com" // Non-secret ✓
}
Environment Separation
Never mix environments in one bundle:
❌ Bad: "all-environments" bundle
DATABASE_URL_PROD=postgres://prod/db
DATABASE_URL_STAGING=postgres://staging/db
DATABASE_URL_DEV=postgres://dev/db
✅ Good: Separate bundles
production/db:
DATABASE_URL=postgres://prod/db
staging/db:
DATABASE_URL=postgres://staging/db
Why: Prevents accidental use of production credentials in staging/dev.
Sensitive Data Patterns
Use environment-specific values:
# Production
STRIPE_KEY=sk_live_abc123...
DATABASE_URL=postgres://prod.rds.aws.com/db
# Staging
STRIPE_KEY=sk_test_xyz789...
DATABASE_URL=postgres://staging.local/db
Audit checklist:
- No production credentials in staging bundles
- All passwords/keys marked as secrets
- No hardcoded IPs or internal hostnames in prod
- No test data in production bundles
Performance Optimization
Bundle Size Guidelines
Optimal bundle sizes:
- Small: 1-10 variables (single service, single purpose)
- Medium: 10-50 variables (full service environment)
- Large: 50-100 variables (monolithic app)
- Too large: 100+ variables (consider splitting)
Why split large bundles:
- Faster search/filter in UI
- Easier to audit changes
- Reduced risk of conflicts
- Better access control (future)
Splitting Large Bundles
Before (monolithic bundle):
production:
- 150 variables (API, web, worker, database, cache, etc.)
After (split by service):
production/api: 30 variables
production/web: 25 variables
production/worker: 20 variables
production/database: 15 variables
production/cache: 10 variables
Result: Each bundle is smaller, faster to load, easier to manage.
Search Performance
Bundle count impact:
- 1-20 bundles: Instant search (< 10ms)
- 20-50 bundles: Fast search (< 50ms)
- 50-100 bundles: Acceptable search (< 200ms)
- 100+ bundles: Consider archiving old bundles
Optimization tips:
- Use tags for filtering (faster than full-text search)
- Archive unused bundles (delete or export to backup)
- Use hierarchical naming for predictable patterns
Lifecycle Management
Development Workflow
Typical lifecycle:
- Create development bundle with test values
- Promote to staging (update values for staging environment)
- Promote to production (update values for production environment)
- Rotate secrets periodically (update values in place)
- Archive when no longer needed
Example workflow:
# 1. Create dev bundle
Create bundle: development/new-feature
Add variables: API_KEY=test123, DB_URL=localhost:5432
# 2. Promote to staging
Duplicate bundle → staging/new-feature
Edit variables: API_KEY=staging456, DB_URL=staging-db:5432
# 3. Promote to production
Duplicate bundle → production/new-feature
Edit variables: API_KEY=live789, DB_URL=prod-db.aws:5432
# 4. Rotate secrets (e.g., every 90 days)
Edit production/new-feature → API_KEY=new-live-key-abc
# 5. Archive (feature removed)
Delete all three bundles (or export first as backup)
Bundle Duplication
When to duplicate:
- Promoting environments (dev → staging → prod)
- Creating environment-specific copies
- Backing up before major changes
Steps:
- Export bundle as JSON
- Create new bundle with environment-specific name
- Import JSON into new bundle
- Update values for new environment
Example:
# Export staging bundle
curl "http://localhost:8888/api/v1/bundles/STAGING_ID/export?format=json" \
-b cookies.txt \
-o staging-backup.json
# Create production bundle
PROD_ID=$(curl -X POST http://localhost:8888/api/v1/bundles \
-H "Content-Type: application/json" \
-b cookies.txt \
-d '{"name": "production/api"}' | jq -r '.id')
# Import (values will need updating for prod)
curl -X POST http://localhost:8888/api/v1/bundles/$PROD_ID/import \
-H "Content-Type: application/json" \
-b cookies.txt \
-d "@staging-backup.json"
Archiving Old Bundles
When to archive:
- Feature removed from production
- Service deprecated
- Environment decommissioned
Archive process:
# 1. Export for backup
curl "http://localhost:8888/api/v1/bundles/OLD_ID/export?format=json&reveal=1" \
-b cookies.txt \
-o archive/old-feature-$(date +%Y%m%d).json
# 2. Store securely (encrypted backup storage)
gpg --encrypt --recipient team@example.com archive/old-feature-*.json
# 3. Delete from EnvCat
curl -X DELETE http://localhost:8888/api/v1/bundles/OLD_ID \
-b cookies.txt
Team Collaboration
Naming Consistency
Team agreement on naming convention:
# Team Convention (document in README)
Bundle naming: `environment/service`
Environments: production, staging, development, test
Services: api, web, worker, db-migrate
Tags: environment,service-type,tech-stack,team
Examples:
- production/api (tags: production,api,node,platform-team)
- staging/web (tags: staging,web,react,growth-team)
Change Communication
Best practices:
- Announce bundle creation in team chat
- Document major changes (e.g., rotating all API keys)
- Use descriptive bundle names (no abbreviations)
Example Slack message:
🔑 New bundle: production/payments-service
Added Stripe live keys for payment processing.
CLI approval: constants request --bundle production/payments-service
Access Patterns (Future: M9+)
Current state (M1-M3): No access control (single user)
Future (M9+): Role-based access control
production/*:
read: [all-engineers]
write: [platform-leads, devops]
staging/*:
read: [all-engineers]
write: [all-engineers]
development/*:
read: [all-engineers]
write: [all-engineers]
Prepare for RBAC:
- Use consistent naming now (easier to apply policies later)
- Tag bundles by team ownership
- Document who "owns" each bundle
Migration Strategies
From .env Files
Scenario: Migrating from per-repo .env files to centralized bundles.
Strategy:
- Inventory: List all
.envfiles across repos - Map: Decide bundle structure (environment/service)
- Import: Bulk import each
.envfile - Validate: Verify all variables imported correctly
- Update CI/CD: Switch to CLI approval flow
- Deprecate: Remove
.envfiles from repos (add to.gitignore)
Example:
# Inventory
api/.env → production/api
web/.env → production/web
worker/.env → production/worker
# Import
for service in api web worker; do
BUNDLE_ID=$(create_bundle "production/$service")
import_env "production/$service" ".env"
done
# Update CI/CD (GitHub Actions example)
# Before:
# env:
# DATABASE_URL: ${{ secrets.DATABASE_URL }}
# After:
# - run: eval "$(constants request --bundle production/api)"
From Other Secret Managers
Scenario: Migrating from AWS Secrets Manager, 1Password, Vault, etc.
Strategy (manual for M1-M3, automated in M8+):
- Export secrets from source (use their CLI/API)
- Transform to EnvCat format (JSON or .env)
- Import to bundles
- Parallel run (both systems active during migration)
- Switch over once validated
- Archive old secrets (keep for rollback)
Example (AWS Secrets Manager → EnvCat):
# 1. Export from AWS
aws secretsmanager get-secret-value \
--secret-id production/api \
--query SecretString \
--output text > aws-secrets.json
# 2. Transform to EnvCat format
jq '{format: "json", data: .}' aws-secrets.json > import.json
# 3. Import
curl -X POST http://localhost:8888/api/v1/bundles/NEW_BUNDLE_ID/import \
-H "Content-Type: application/json" \
-b cookies.txt \
-d @import.json
Troubleshooting
Problem: Too Many Bundles
Symptom: 100+ bundles, hard to find the right one.
Solutions:
- Archive unused bundles (export → delete)
- Consolidate similar bundles (e.g., merge
dev/api-v1anddev/api-v2) - Use hierarchical naming (easier to filter:
production/*) - Leverage tags (filter by
?tag=production)
Problem: Bundle Naming Conflicts
Symptom: Team members creating bundles with similar names.
Solutions:
- Document naming convention (in repo README)
- Use namespaces (e.g.,
team-name/service) - Team review (announce new bundles in Slack)
Problem: Secret Sprawl
Symptom: Same secret duplicated across many bundles.
Solutions:
- Consolidate secrets (one bundle per environment/service)
- Use bundle references (future: M8+, bundle-to-bundle references)
- Document shared secrets (e.g.,
AWS_REGIONused in all bundles)
Example:
Before (duplicated):
production/api: AWS_REGION=us-east-1
production/web: AWS_REGION=us-east-1
production/worker: AWS_REGION=us-east-1
After (consolidated):
production/shared: AWS_REGION=us-east-1
production/api: (reference production/shared)
Note: Bundle references not yet implemented (future: M8+).
Advanced Patterns
Multi-Tenant Bundles
Scenario: SaaS app with customer-specific configs.
Structure:
tenants/customer-a/production
tenants/customer-a/staging
tenants/customer-b/production
tenants/customer-b/staging
Tags:
tenant-customer-a, tenant-customer-b, production, staging
CLI approval:
constants request --bundle tenants/customer-a/production
Feature Flag Bundles
Scenario: Feature-specific configs (beta features, experiments).
Structure:
features/new-checkout/production
features/new-checkout/staging
features/ab-test-homepage/production
Benefits:
- Easy to enable/disable (delete bundle when feature removed)
- Clear ownership (feature team)
- Isolated secrets (no impact on main app)
Ephemeral Environment Bundles
Scenario: Preview deployments, PR environments.
Structure:
preview/pr-123
preview/pr-456
ephemeral/branch-new-feature
Lifecycle:
- Create bundle when PR opened
- CLI approval during preview deployment
- Delete bundle when PR merged/closed
Automation (GitHub Actions example):
# Create bundle for PR preview
- name: Create preview bundle
run: |
BUNDLE_ID=$(curl -X POST http://localhost:8888/api/v1/bundles \
-H "Content-Type: application/json" \
-d "{\"name\": \"preview/pr-${{ github.event.pull_request.number }}\"}" \
| jq -r '.id')
# Import base staging config
curl -X POST http://localhost:8888/api/v1/bundles/$BUNDLE_ID/import \
-d @staging-base.json
Checklist: Bundle Health Audit
Perform quarterly audits:
- Naming consistency: All bundles follow team convention
- Tag accuracy: Tags reflect current state (environment, tech, team)
- Secret marking: All credentials marked as secrets
- Environment separation: No mixed prod/staging bundles
- Duplicate cleanup: No redundant bundles
- Archive old: Unused bundles exported and deleted
- Documentation: Bundle purpose documented (description field)
- Access patterns: Ownership clear (via tags or external docs)
Next Steps
Now that you understand bundle organization:
- Managing Variables - Add, edit, delete variables
- CLI Approval Flow - Request variables from terminal (coming soon)
- Authentication - User and session management
Additional Resources
- API Reference: Bundles - Programmatic management
- Architecture: Design Principles - Why we built it this way
- Security Model - Threat model and guarantees
Feedback
Have bundle organization patterns that work well for your team? Share with us or open an issue.