Configuration
Team Server reads config/production.yaml. Container deployments mount it at /app/config/production.yaml.
Environment Variable Templates
Use Tera environment templates for credentials:
database:
uri: "<%= get_env(name='DATABASE_URL') %>"
server:
host: "<%= get_env(name='SERVER_URL') %>"Missing variables without defaults stop startup. Replace deprecated {{ get_env(...) }} delimiters with <%= and %>; old syntax warns and can be damaged by YAML formatters.
Standard Sections
Logger
logger:
enable: true
pretty_backtrace: false
level: info
format: compact| Setting | Values |
|---|---|
enable | Enable or disable application logging |
pretty_backtrace | Enable formatted Rust backtraces |
level | trace, debug, info, warn, or error |
format | compact, pretty, or json |
override_filter | Optional filter that includes third-party library logs |
Server
server:
binding: 0.0.0.0
port: 5150
host: "<%= get_env(name='SERVER_URL') %>"
middlewares:
compression:
enable: true
cors:
enable: true
allow_origins:
- "<%= get_env(name='SERVER_URL') %>"
allow_headers: ["*"]
allow_methods: ["*"]
expose_headers: ["x-total-count"]
fallback:
enable: false
limit_payload:
body_limit: 5mb
logger:
enable: true
secure_headers:
enable: true
preset: github
overrides:
"Content-Security-Policy": "default-src 'self'; script-src 'self' 'sha256-HvYZHxmCfa13ffpFFfnqlSwYhOKQhnA3t3xMKSF0QsE='; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"
static:
enable: true
must_exist: false
folder:
uri: "/"
path: "public"
fallback: "public/index.html"
timeout_request:
enable: true
timeout: 30000| Setting | Purpose |
|---|---|
binding, port | Listener address and port |
host | Public URL used by the application |
cors | Browser origins, methods, and exposed headers |
limit_payload | Maximum request body size |
secure_headers | CSP, HSTS, MIME, and framing headers |
static | Team Server frontend files |
timeout_request | Request timeout in milliseconds |
Set secure_headers.enable: true; a preset alone is insufficient. Use the deployed release’s CSP script hash or the frontend may fail to load.
Login, logout, and browser-session changes require an Origin matching settings.tenant.base_url, regardless of CORS. See origin rules.
Database
Team Server requires PostgreSQL in production:
database:
uri: "<%= get_env(name='DATABASE_URL') %>"
enable_logging: false
connect_timeout: 1500
idle_timeout: 500
min_connections: 1
max_connections: 1
auto_migrate: true
dangerously_truncate: false
dangerously_recreate: falseUse a URI in this form:
postgresql://username:password@hostname:5432/databaseauto_migrate: true runs startup migrations. Keep dangerously_* false outside disposable databases. Size the pool for database limits and workload. Helm uses one instance because workers/scheduler lack leader election.
Workers
workers:
mode: BackgroundAsync| Mode | Behavior |
|---|---|
BackgroundAsync | Process asynchronous work in the background; recommended |
BackgroundQueue | Process queued work in the background |
ForegroundBlocking | Process work synchronously |
Email, Slack, and Microsoft Teams notifications, plus other background work, require a functioning worker mode.
Scheduler
Schedules use seven fields: second minute hour day month day-of-week year.
scheduler:
output: stdout
jobs:
statistics_calculate:
run: "statistics_calculate"
schedule: "0 0 1 * * * *"
status_reconcile:
run: "status_reconcile"
schedule: "0 */5 * * * * *"
github_pipelines:
run: "github_sync mode:pipelines trigger:scheduled"
schedule: "0 2 * * * * *"
github_builds:
run: "github_sync mode:builds trigger:scheduled"
schedule: "0 3,18,33,48 * * * * *"
log_prune:
run: "log_prune confirm:true"
schedule: "0 30 2 * * * *"statistics_calculate and status_reconcile are required and must remain enabled. Keep log_prune confirm:true enabled to enforce the retention periods configured in Administration > Log Settings. Add both synchronization jobs for each configured CI/CD provider:
| Provider | Pipeline job | Build job |
|---|---|---|
| Bamboo | bamboo_sync mode:pipelines trigger:scheduled | bamboo_sync mode:builds trigger:scheduled |
| GitHub | github_sync mode:pipelines trigger:scheduled | github_sync mode:builds trigger:scheduled |
| GitLab | gitlab_sync mode:pipelines trigger:scheduled | gitlab_sync mode:builds trigger:scheduled |
| Jenkins | jenkins_sync mode:pipelines trigger:scheduled | jenkins_sync mode:builds trigger:scheduled |
| Koji | koji_sync mode:pipelines trigger:scheduled | koji_sync mode:builds trigger:scheduled |
| TeamCity | teamcity_sync mode:pipelines trigger:scheduled | teamcity_sync mode:builds trigger:scheduled |
Without id, sync jobs process every provider integration; with none configured, they do no work.
Administrative Settings
Manage retention, Days Until Stale, and Build Discovery Hours in Administration, not this YAML. Retention pruning covers application logs, submitted Sensor logs, and terminal discovery history; it does not delete policies, builds, violations, or host-local logs. See recency calculations.
Application Settings
Outbound HTTP Policy
Outbound OIDC and integration requests deny private, loopback, link-local, and other reserved addresses by default and trust the public web PKI. This policy also applies to SMTP relay resolution. Allow only the internal networks used by private identity providers, self-hosted CI/CD systems, or SMTP relays, and add internal certificate authorities when required:
settings:
http:
allowed_networks:
- "10.20.0.0/16"
- "192.168.50.14"
ca_certificates:
- "config/corporate-ca.pem"| Setting | Purpose |
|---|---|
allowed_networks | CIDRs or individual addresses exempt from the reserved-address block |
ca_certificates | PEM bundles trusted in addition to public roots |
Other reserved ranges stay blocked. The policy includes OIDC discovery, keys, and token exchange. Invalid networks/CA bundles stop startup. Mount CAs and use container paths relative to /app.
Credential Encryption
Every Team Server process, including local tasks, needs ENDURA_CREDENTIAL_ENCRYPTION_KEY to encrypt stored integration credentials. Generate exactly 32 bytes in standard base64:
openssl rand -base64 32Store it outside production.yaml. Missing/invalid keys or unreadable stored credentials prevent startup.
Existing databases with plaintext credentials cannot be upgraded yet
Earlier plaintext integration credentials cannot be converted: startup fails even with a valid key. Keep the earlier release until conversion is supported. New databases and databases without plaintext credentials can start.
For rotation, put earlier keys in ENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS, comma-separated, same format, without spaces, empty entries, or duplicates. Startup validates every credential and atomically re-encrypts old-key values. Remove previous keys only after successful credential validation.
JWT
Team Server signs Runtime Sensor and user tokens with separate secrets:
settings:
jwt:
sensor:
secret: "<%= get_env(name='JWT_SENSOR_SECRET') %>"
expiration: 31557600
user:
secret: "<%= get_env(name='JWT_USER_SECRET') %>"
expiration: 604800Each value must be base64 that decodes to at least 32 bytes, and the values must differ. Team Server refuses to start otherwise. Generate them separately:
openssl rand -base64 48
openssl rand -base64 48Changing a secret invalidates tokens signed with its previous value.
Tenant
settings:
tenant:
name: "<%= get_env(name='TENANT_NAME') %>"
base_url: "<%= get_env(name='SERVER_URL') %>"name appears in the UI, email tests, and alerts. Public base_url drives Slack callbacks, Teams messaging endpoints, alert links, browser Origin checks, and cookie security. Teams requires publicly reachable HTTPS with a trusted certificate.
TLS
To terminate TLS in Team Server:
settings:
tls:
certificate: "/run/secrets/server_cert"
private_key: "/run/secrets/server_key"Both files must be PEM encoded and readable by the Team Server process. Set SERVER_URL to https://....
For ingress/proxy TLS, omit settings.tls and keep public SERVER_URL on HTTPS. HTTPS tenant URLs add Secure cookies; HttpOnly and SameSite=Lax always apply. No cookie rewriting is needed.
Authentication
Configure at least one OIDC provider for production. Users authenticate first and receive the Viewer role; permissions are then managed through Access Control.
Built-in Username and Password
Set both variables to enable the built-in account:
ENDURA_AUTH_USERNAME=developer
ENDURA_AUTH_PASSWORD=replace-meUse this single login only for development/testing. First login creates an unscoped Viewer with email <username>@localhost; promote it using the deployment guide’s first-Administrator procedure.
OpenID Connect
All providers share this structure:
settings:
oidc:
google:
client_id: "<%= get_env(name='GOOGLE_OIDC_CLIENT_ID') %>"
client_secret: "<%= get_env(name='GOOGLE_OIDC_CLIENT_SECRET') %>"
issuer_url: "https://accounts.google.com"
redirect_url: "<%= get_env(name='SERVER_URL') %>/api/authentication/google/callback"
scopes: [openid, email, profile]
domains:
- example.com| Setting | Requirement |
|---|---|
client_id | OIDC client identifier |
client_secret | OIDC client secret |
issuer_url | Provider issuer URL |
redirect_url | Exact callback registered with the provider |
scopes | Include openid, email, and profile |
domains | One or more email domains allowed to sign in |
Providers must support authorization code response/grant types, RS256 ID tokens, client_secret_basic, and S256 PKCE (fresh per login). Include openid, email, and profile for identity claims; omitted openid is added automatically.
Metadata comes from {issuer_url}/.well-known/openid-configuration; its issuer must match exactly. Issuer and discovered authorization/token/key endpoints require HTTPS; discovery/key/token requests cannot redirect. Configure outbound policy for private providers/CAs.
The callback path always includes /api:
https://team-server.example.com/api/authentication/{provider}/callbackSupported provider keys and typical issuer URLs are:
| Provider key | Issuer URL |
|---|---|
google | https://accounts.google.com |
microsoft | https://login.microsoftonline.com/{tenant-id}/v2.0 |
cyberark | Your CyberArk Identity tenant issuer |
oracle | Your OCI IAM identity-domain issuer |
For Oracle, the discovery document’s issuer must equal the configured identity-domain URL. If the domain uses Oracle’s default https://identity.oraclecloud.com/ issuer, configure an identity-domain custom issuer with the same identity-domain prefix before using that URL as issuer_url.
Register the provider-specific callback, store the client secret outside the YAML file, and list every email domain that should be allowed to authenticate.
Production Example
This example contains the core configuration and Google OIDC. Add the synchronization jobs and identity providers your deployment uses.
logger:
enable: true
pretty_backtrace: false
level: info
format: compact
scheduler:
output: stdout
jobs:
statistics_calculate:
run: "statistics_calculate"
schedule: "0 0 1 * * * *"
status_reconcile:
run: "status_reconcile"
schedule: "0 */5 * * * * *"
log_prune:
run: "log_prune confirm:true"
schedule: "0 30 2 * * * *"
server:
binding: 0.0.0.0
port: 5150
host: "<%= get_env(name='SERVER_URL') %>"
middlewares:
compression:
enable: true
cors:
enable: true
allow_origins:
- "<%= get_env(name='SERVER_URL') %>"
allow_headers: ["*"]
allow_methods: ["*"]
expose_headers: ["x-total-count"]
fallback:
enable: false
limit_payload:
body_limit: 5mb
logger:
enable: true
secure_headers:
enable: true
preset: github
overrides:
"Content-Security-Policy": "default-src 'self'; script-src 'self' 'sha256-HvYZHxmCfa13ffpFFfnqlSwYhOKQhnA3t3xMKSF0QsE='; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'none'; form-action 'self'; frame-ancestors 'none'"
static:
enable: true
must_exist: false
folder:
uri: "/"
path: "public"
fallback: "public/index.html"
timeout_request:
enable: true
timeout: 30000
database:
uri: "<%= get_env(name='DATABASE_URL') %>"
enable_logging: false
connect_timeout: 1500
idle_timeout: 500
min_connections: 1
max_connections: 1
auto_migrate: true
dangerously_truncate: false
dangerously_recreate: false
workers:
mode: BackgroundAsync
settings:
jwt:
sensor:
secret: "<%= get_env(name='JWT_SENSOR_SECRET') %>"
expiration: 31557600
user:
secret: "<%= get_env(name='JWT_USER_SECRET') %>"
expiration: 604800
tenant:
name: "<%= get_env(name='TENANT_NAME') %>"
base_url: "<%= get_env(name='SERVER_URL') %>"
oidc:
google:
client_id: "<%= get_env(name='GOOGLE_OIDC_CLIENT_ID') %>"
client_secret: "<%= get_env(name='GOOGLE_OIDC_CLIENT_SECRET') %>"
issuer_url: "https://accounts.google.com"
redirect_url: "<%= get_env(name='SERVER_URL') %>/api/authentication/google/callback"
scopes: [openid, email, profile]
domains:
- example.comRequired Environment Variables
| Variable | Purpose |
|---|---|
DATABASE_URL | PostgreSQL connection URI |
ENDURA_CREDENTIAL_ENCRYPTION_KEY | Current 32-byte credential-encryption key, encoded with standard base64 |
ENDURA_LICENSE_KEY | Team Server license |
JWT_SENSOR_SECRET | Runtime Sensor token-signing secret |
JWT_USER_SECRET | User-token signing secret |
SERVER_URL | Public Team Server URL |
TENANT_NAME | Organization name |
Add the client ID and client secret variables referenced by each configured OIDC provider. ENDURA_CREDENTIAL_ENCRYPTION_PREVIOUS_KEYS is optional and used only while rotating the credential-encryption key.