Skip to content
Configuration

Configuration

Team Server reads config/production.yaml. Container deployments mount it at /app/config/production.yaml.

Environment Variable Templates

Use Tera templates to keep credentials outside the YAML file:

database:
  uri: "<%= get_env(name='DATABASE_URL') %>"
server:
  host: "<%= get_env(name='SERVER_URL', default='http://localhost:5150') %>"

An unset variable without a default stops startup. The older {{ get_env(...) }} syntax still renders but logs a deprecation warning and can be corrupted by YAML formatters. Replace {{ and }} with <%= and %>.

Standard Sections

Logger

logger:
  enable: true
  pretty_backtrace: false
  level: info
  format: compact
SettingValues
enableEnable or disable application logging
pretty_backtraceEnable formatted Rust backtraces
leveltrace, debug, info, warn, or error
formatcompact, pretty, or json
override_filterOptional 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'; 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
SettingPurpose
binding, portListener address and port
hostPublic URL used by the application
corsBrowser origins, methods, and exposed headers
limit_payloadMaximum request body size
secure_headersCSP, HSTS, MIME, and framing headers
staticTeam Server frontend files
timeout_requestRequest timeout in milliseconds

secure_headers.preset does not enable the middleware by itself. Set enable: true.

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: false

Use a URI in this form:

postgresql://username:password@hostname:5432/database

auto_migrate: true applies database migrations during startup. Keep both dangerously_* settings false outside disposable development databases. Size the connection pool for the database limit and the number of Team Server replicas.

Workers

workers:
  mode: BackgroundAsync
ModeBehavior
BackgroundAsyncProcess asynchronous work in the background; recommended
BackgroundQueueProcess queued work in the background
ForegroundBlockingProcess work synchronously

Notifications and 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:
    calculate_statistics:
      run: "calculate_statistics"
      schedule: "0 0 1 * * * *"
    update_status:
      run: "update_status"
      schedule: "0 */5 * * * * *"
    discover_github_pipelines:
      run: "discover_github_pipelines"
      schedule: "0 2 * * * * *"
    discover_github_builds:
      run: "discover_github_builds"
      schedule: "0 3,18,33,48 * * * * *"

calculate_statistics and update_status are required and must remain enabled. Add both discovery jobs for each configured CI/CD provider:

ProviderPipeline jobBuild job
Bamboodiscover_bamboo_pipelinesdiscover_bamboo_builds
GitHubdiscover_github_pipelinesdiscover_github_builds
GitLabdiscover_gitlab_pipelinesdiscover_gitlab_builds
Jenkinsdiscover_jenkins_pipelinesdiscover_jenkins_builds
TeamCitydiscover_teamcity_pipelinesdiscover_teamcity_builds

Discovery jobs return without work when their provider has no configured integration, so they are safe to leave enabled.

Application Settings

Outbound HTTP Policy

Outbound integration requests deny private, loopback, link-local, and other reserved addresses by default and trust the public web PKI. Allow only the internal networks used by self-hosted CI/CD systems, 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"
SettingPurpose
allowed_networksCIDRs or individual addresses exempt from the reserved-address block
ca_certificatesPEM bundles trusted in addition to public roots

Every other reserved range remains blocked. A malformed network or unusable CA bundle stops startup. Mount CA files into the container and use paths relative to its /app working directory.

JWT

Team Server signs 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: 604800

Each 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 48

Changing 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 and notifications. base_url must be the public URL used by browsers and Sensors. It also builds Slack OAuth callbacks and links inside alerts.

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://....

If an ingress, reverse proxy, or load balancer terminates TLS, omit settings.tls but keep the public SERVER_URL on HTTPS. Configure the terminating layer to set secure session-cookie attributes.

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-me

This provider supports one administrative account and is intended only for local development and testing. Do not use it in production.

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
SettingRequirement
client_idOIDC client identifier
client_secretOIDC client secret
issuer_urlProvider issuer URL
redirect_urlExact callback registered with the provider
scopesInclude openid, email, and profile
domainsOne or more email domains allowed to sign in

The callback path always includes /api:

https://team-server.example.com/api/authentication/{provider}/callback

Supported provider keys and typical issuer URLs are:

Provider keyIssuer URL
googlehttps://accounts.google.com
microsofthttps://login.microsoftonline.com/{tenant-id}/v2.0
cyberarkYour CyberArk Identity tenant issuer
oracleYour OCI IAM identity-domain issuer

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 discovery jobs and identity providers your deployment uses.

logger:
  enable: true
  pretty_backtrace: false
  level: info
  format: compact

scheduler:
  output: stdout
  jobs:
    calculate_statistics:
      run: "calculate_statistics"
      schedule: "0 0 1 * * * *"
    update_status:
      run: "update_status"
      schedule: "0 */5 * * * * *"

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'; 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.com

Required Environment Variables

VariablePurpose
DATABASE_URLPostgreSQL connection URI
ENDURA_LICENSE_KEYTeam Server license
JWT_SENSOR_SECRETSensor-token signing secret
JWT_USER_SECRETUser-token signing secret
SERVER_URLPublic Team Server URL
TENANT_NAMEOrganization name

Add the client ID and client secret variables referenced by each configured OIDC provider.