Modular Configuration Architecture
The app-portal uses a modular configuration architecture that splits the traditional monolithic app-config.yaml into focused, manageable configuration modules. This improves maintainability, reduces merge conflicts, and makes configuration more discoverable.
Overview
Instead of a single large configuration file, settings are organized into logical modules within the app-config/ directory. Each module focuses on a specific aspect of the Backstage application.
Configuration Structure
app-portal/
├── app-config.yaml # Legacy config (kept for reference)
├── app-config/ # Modular configuration directory
│ ├── README.md # Configuration documentation
│ ├── auth.yaml # Authentication providers
│ ├── backend.yaml # Backend service settings
│ ├── catalog.yaml # Software catalog configuration
│ ├── ingestor.yaml # Ingestor plugins configuration
│ ├── integrations.yaml # SCM integrations (GitHub/GitLab)
│ ├── kubernetes.yaml # Kubernetes cluster connections
│ ├── scaffolder.yaml # Scaffolder templates settings
│ └── techdocs.yaml # TechDocs configuration
Module Descriptions
auth.yaml - Authentication Configuration
Configures authentication providers and security settings:
auth:
environment: development
providers:
github:
development:
clientId: ${AUTH_GITHUB_CLIENT_ID}
clientSecret: ${AUTH_GITHUB_CLIENT_SECRET}
session:
secret: ${SESSION_SECRET}
Key settings:
- OAuth providers (GitHub, GitLab, Google, etc.)
- Session management
- API key configuration
- Guest access policies
backend.yaml - Backend Service Configuration
Core backend service settings:
backend:
baseUrl: http://localhost:7007
listen:
port: 7007
cors:
origin: http://localhost:3000
methods: [GET, HEAD, PATCH, POST, PUT, DELETE]
credentials: true
database:
client: better-sqlite3
connection: ':memory:'
reading:
allow:
- host: example.com
- host: '*.mozilla.org'
Key settings:
- Server ports and URLs
- CORS configuration
- Database connections
- Content security policies
- Cache settings
catalog.yaml - Software Catalog Configuration
Defines how the catalog discovers and processes entities:
catalog:
rules:
- allow: [Component, System, API, Resource, Location, Template]
locations:
- type: url
target: https://github.com/open-service-portal/app-portal/blob/main/catalog-info.yaml
providers:
github:
providerId:
organization: 'open-service-portal'
catalogPath: '/catalog-info.yaml'
filters:
branch: 'main'
repository: '.*'
schedule:
frequency: { minutes: 30 }
timeout: { minutes: 3 }
Key settings:
- Entity discovery locations
- GitHub/GitLab organization scanning
- Processing rules and filters
- Refresh schedules
- Custom processors
ingestor.yaml - Ingestor Plugins Configuration
Configuration for Kubernetes and Crossplane ingestors:
kubernetesIngestor:
enabled: true
clusters:
- name: local
authProvider: serviceAccount
skipTLSVerify: false
schedule:
frequency: { seconds: 60 }
timeout: { seconds: 30 }
crossplaneIngestor:
enabled: true
defaultOwner: platform-team
defaultSystem: crossplane
xrdFilters:
labelSelector: "openportal.dev/ingest=true"
templateGeneration:
generateApiEntities: true
includeCompositionDetails: false
caching:
ttl: 300
maxSize: 100
Key settings:
- Cluster connections
- Discovery intervals
- XRD filtering rules
- Template generation options
- Caching configuration
integrations.yaml - SCM Integrations
Source control management integrations:
integrations:
github:
- host: github.com
token: ${GITHUB_TOKEN}
gitlab:
- host: gitlab.com
token: ${GITLAB_TOKEN}
apiBaseUrl: https://gitlab.com/api/v4
Key settings:
- GitHub/GitLab credentials
- Enterprise instances
- API endpoints
- Rate limiting
kubernetes.yaml - Kubernetes Configuration
Kubernetes cluster connections:
kubernetes:
serviceLocatorMethod:
type: 'multiTenant'
clusterLocatorMethods:
- type: 'config'
clusters:
- name: local-cluster
url: https://kubernetes.default.svc
authProvider: 'serviceAccount'
skipTLSVerify: false
serviceAccountToken: ${K8S_SA_TOKEN}
Key settings:
- Cluster authentication
- Service discovery
- TLS verification
- Custom resources
scaffolder.yaml - Scaffolder Configuration
Template scaffolding settings:
scaffolder:
defaultAuthor:
name: Scaffolder
email: scaffolder@backstage.io
defaultCommitMessage: "Initial commit"
Key settings:
- Default values
- Git configuration
- Custom actions
- Template locations
techdocs.yaml - TechDocs Configuration
Documentation platform settings:
techdocs:
builder: 'local'
generator:
runIn: 'local'
publisher:
type: 'local'
Key settings:
- Build configuration
- Storage backends
- Publishing targets
- MkDocs settings
Configuration Loading
The modular configuration is loaded by the enhanced start.js script:
// start.js
const configPaths = [
'--config', 'app-config.yaml',
'--config', 'app-config/auth.yaml',
'--config', 'app-config/backend.yaml',
'--config', 'app-config/catalog.yaml',
'--config', 'app-config/ingestor.yaml',
'--config', 'app-config/integrations.yaml',
'--config', 'app-config/kubernetes.yaml',
'--config', 'app-config/scaffolder.yaml',
'--config', 'app-config/techdocs.yaml'
];
// Environment-specific overrides
if (process.env.APP_CONFIG_ENV) {
configPaths.push('--config', `app-config.${process.env.APP_CONFIG_ENV}.yaml`);
}
Benefits
1. Improved Organization
- Logical grouping of related settings
- Easier to find specific configurations
- Clear separation of concerns
2. Better Collaboration
- Reduced merge conflicts
- Teams can own specific config modules
- Parallel development of features
3. Enhanced Maintainability
- Smaller, focused files
- Easier to review changes
- Simpler troubleshooting
4. Flexible Deployment
- Environment-specific overrides
- Feature flags per module
- Gradual rollout capabilities
Migration Guide
From Monolithic to Modular
-
Backup existing configuration:
cp app-config.yaml app-config.yaml.backup -
Create app-config directory:
mkdir -p app-config -
Split configuration into modules:
- Move auth settings to
auth.yaml - Move backend settings to
backend.yaml - Continue for each module
- Move auth settings to
-
Update start script:
- Ensure
start.jsloads all modules - Test configuration loading
- Ensure
-
Verify functionality:
yarn dev
Environment-Specific Configuration
Create environment-specific files that override base settings:
# app-config.production.yaml
backend:
baseUrl: https://backstage.example.com
database:
client: pg
connection:
host: ${POSTGRES_HOST}
port: ${POSTGRES_PORT}
user: ${POSTGRES_USER}
password: ${POSTGRES_PASSWORD}
Best Practices
1. Module Boundaries
- Keep modules focused on a single concern
- Avoid cross-module dependencies
- Use clear, descriptive filenames
2. Secret Management
- Never commit secrets to modules
- Use environment variables
- Consider secret management tools (SOPS, Vault)
3. Documentation
- Document each module's purpose
- Include examples in comments
- Maintain module README files
4. Version Control
- Review module changes carefully
- Use semantic commit messages
- Tag configuration versions
5. Testing
- Test each module independently
- Validate complete configuration
- Use configuration schemas
Troubleshooting
Common Issues
Configuration Not Loading
# Check if all files exist
ls -la app-config/*.yaml
# Verify YAML syntax
yarn backstage-cli config:check
Missing Environment Variables
# List required variables
grep -r '\${' app-config/ | grep -v '#'
# Set missing variables
export GITHUB_TOKEN=your-token
Merge Conflicts
- Resolve conflicts per module
- Test affected functionality
- Validate final configuration
Debugging
Enable configuration debugging:
LOG_LEVEL=debug yarn dev
Check loaded configuration:
yarn backstage-cli config:print
Advanced Topics
Dynamic Configuration
Load configuration from external sources:
// Custom config loader
const loadDynamicConfig = async () => {
const remoteConfig = await fetch('https://config-service/backstage');
return remoteConfig.json();
};
Configuration Validation
Add schema validation:
# config-schema.yaml
auth:
type: object
required: [providers]
properties:
providers:
type: object
Feature Flags
Implement feature toggles:
# features.yaml
features:
crossplaneIngestor: true
customTemplateCards: false
darkMode: true