Skip to main content

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

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+)

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 → use platform-team)
  • Create too many tags (5-8 per bundle is plenty)
  • Use redundant tags (don't tag production/api with production AND prod)

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:

  1. Create development bundle with test values
  2. Promote to staging (update values for staging environment)
  3. Promote to production (update values for production environment)
  4. Rotate secrets periodically (update values in place)
  5. 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:

  1. Export bundle as JSON
  2. Create new bundle with environment-specific name
  3. Import JSON into new bundle
  4. 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:

  1. Inventory: List all .env files across repos
  2. Map: Decide bundle structure (environment/service)
  3. Import: Bulk import each .env file
  4. Validate: Verify all variables imported correctly
  5. Update CI/CD: Switch to CLI approval flow
  6. Deprecate: Remove .env files 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+):

  1. Export secrets from source (use their CLI/API)
  2. Transform to EnvCat format (JSON or .env)
  3. Import to bundles
  4. Parallel run (both systems active during migration)
  5. Switch over once validated
  6. 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-v1 and dev/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_REGION used 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:

  1. Create bundle when PR opened
  2. CLI approval during preview deployment
  3. 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

Feedback

Have bundle organization patterns that work well for your team? Share with us or open an issue.