Configuration Guide
June 25, 2026 · View on GitHub
Custos server can be configured using a YAML configuration file instead of environment variables. This guide explains how to set up and use the configuration system.
Quick Start
- Place your configuration file at
config/custos.yaml - Start the server:
./custos
The server will automatically load the configuration file. If the config file is not found at the resolved path, the server exits with an error — make sure config/custos.yaml exists (or override CONFIG_PATH).
Configuration File Location
By default, the server looks for the configuration file at config/custos.yaml. You can customize this location using the CONFIG_PATH environment variable:
CONFIG_PATH=/etc/custos/config.yaml ./custos
Configuration Structure
Core Configuration
The core section contains essential server settings:
core:
database:
url: "admin:admin@tcp(localhost:3306)/custos?parseTime=true&charset=utf8mb4"
api:
port: 8080
log_level: "info"
- database.url: MariaDB / MySQL DSN (the server uses the
go-sql-driver/mysqldriver). Required. - api.port: HTTP API port (default: 8080)
- log_level: Logging level (info, debug, warn, error)
Auth Configuration
The verifier and caller resolver read this block. The loader refuses to boot when either OIDC field is empty; the cache TTL is capped at 60s.
core:
auth:
oidc:
issuer: "${OIDC_ISSUER_URL}"
audience: "${OIDC_AUDIENCE}"
cache_ttl: "30s"
| Key | Default | Notes |
|---|---|---|
core.auth.oidc.issuer | (required) | OIDC issuer URL. The verifier discovers JWKS from <issuer>/.well-known/openid-configuration. Empty → boot fails with core.auth.oidc.issuer is required. |
core.auth.oidc.audience | (required) | Expected aud claim. Usually the client_id registered with the IdP. Empty → boot fails with core.auth.oidc.audience is required. |
core.auth.cache_ttl | 30s | TTL of the in-process caller + privilege cache, keyed by OIDC sub. Any time.Duration string (s, m). Zero or negative → 30s. Values above 60s are capped at 60s at boot and a WARN is logged. |
Public routes (those that bypass JWT verification) are declared in code via router.Public(...). There is no YAML allowlist.
Connector Configuration
The connectors section defines which connectors are enabled and their settings.
SLURM Association Mapper
connectors:
slurm-mapper:
type: "slurm-association-mapper"
enabled: true
slurm_api:
url: "https://slurm-api.example.com"
version: "0.0.38"
username: "slurm_admin"
token: "${SLURM_TOKEN}"
SLURM Usage Monitor
slurm-usage-monitor:
type: "slurm-usage-monitor"
enabled: true
slurm_api:
url: "https://slurm-api.example.com"
version: "0.0.38"
username: "slurm_admin"
token: "${SLURM_TOKEN}"
cluster_id: "slurm-cluster"
COmanage Identity Provisioner
comanage-provisioner:
type: "comanage-identity-provisioner"
enabled: true
registry:
url: "https://comanage.example.org"
co_id: 1
api_user: "comanage_api_user"
api_key: "${COMANAGE_API_KEY}"
unix_cluster:
id: 10
person_id_type: "eppn"
provisioning:
custos_cluster_id: "cluster-001"
default_shell: "/bin/bash"
homedir_prefix: "/home/"
http_timeout: "30s"
AMIE Processor
amie-processor:
type: "amie-processor"
enabled: true
credentials:
base_url: "https://amie.xsede.org"
site_code: "XSEDE"
api_key: "${AMIE_API_KEY}"
cluster:
id: "cluster-001"
polling:
poll_interval: "30s"
worker_interval: "5s"
poller_enabled: true
timeouts:
connect_timeout: "5s"
read_timeout: "20s"
Environment Variable Substitution
The configuration parser supports environment variable substitution using the ${VAR_NAME} syntax. This allows you to:
- Keep sensitive values (API keys, passwords) out of version control
- Use different values for different deployment environments
- Reference environment variables directly in the config file
Example
core:
database:
url: "${DATABASE_URL}"
In your shell:
export DATABASE_URL="admin:admin@tcp(localhost:3306)/custos?parseTime=true&charset=utf8mb4"
./custos
If an environment variable referenced in the config file is not set, it will remain unexpanded:
token: "${MISSING_TOKEN}" # Will not be replaced if MISSING_TOKEN is not set
Environment Overrides
A small set of process-level knobs aren't in the YAML — they're read directly from the environment at boot.
| Variable | Default | Purpose |
|---|---|---|
CONFIG_PATH | config/custos.yaml | Override the config file location. |
DB_MAX_OPEN_CONNS | 25 | Maximum open database connections. |
DB_MAX_IDLE_CONNS | 5 | Maximum idle database connections. |
Enabling/Disabling Connectors
The configuration supports any number of connectors with any names. Each connector is identified by its type field which determines which loader is used:
connectors:
connector-name: # Can be any identifier
type: "slurm-association-mapper" # Determines which loader to use
enabled: true
# Connector-specific configuration
To disable a connector, set enabled: false:
connectors:
slurm-mapper:
type: "slurm-association-mapper"
enabled: false
# Rest of configuration is still required but will be ignored
When a connector is disabled:
- It will not be loaded on server startup
- An info log message will indicate it's disabled
- The server will continue to function normally
Supported Connector Types
slurm-association-mapper- SLURM Association Mapperslurm-usage-monitor- SLURM Usage Monitorcomanage-identity-provisioner- COmanage Identity Provisioneramie-processor- AMIE Processor
New connector types can be added by:
- Implementing the connector package
- Adding the loader function to
internal/connectors/loader.go - Mapping the type to the loader in
connectorLoadersmap
Configuration Validation
The configuration is validated during parsing. Common issues:
- Missing required fields: If
core.database.urlis missing, the server will fail to start - Invalid YAML syntax: Check your YAML indentation and structure
- Undefined environment variables: Variables referenced with
${VAR}will be left as-is if not set
Logging
Configuration loading is logged at the INFO level:
{
"time": "2026-06-12T10:30:45Z",
"level": "INFO",
"msg": "loaded config",
"path": "config/custos.yaml"
}
Connector loading logs indicate which connectors are enabled or disabled:
{
"time": "2026-06-12T10:30:46Z",
"level": "INFO",
"msg": "loading SLURM Association Mapper connector"
}
Adding New Connectors
To add a new connector type:
- Implement the connector package with a
LoadConnectorfunction - Update
internal/connectors/loader.goto import the new connector - Add the type-to-loader mapping in the
connectorLoadersmap:
connectorLoaders := map[string]func(context.Context, *sqlx.DB, *events.Bus, *service.Service, *sync.WaitGroup, *http.ServeMux, *config.ConnectorConfig) error{
"my-new-connector": mynewconnector.LoadConnector,
// ... existing connectors
}
- Add the connector to your config file with the corresponding type
No code changes are required elsewhere — the configuration system is fully extensible.
Example Complete Configuration
See config/custos.yaml for a complete example configuration with all connectors.