Every key in the server's configuration file, its default, and the security checks harnessd runs on it.
harnessd reads one YAML file, by default /etc/harness/harnessd.yaml. Pass another with --config, or set HARNESSD_CONFIG. Relative paths in the file resolve against the file's own folder, and ~ is expanded. The server never rewrites this file: changes made from the app are kept in a private overlay on top of it.
Complete example#
listen: 127.0.0.1:8443
tls:
cert: /etc/harness/tls/fullchain.pem
key: /etc/harness/tls/privkey.pem
tools_listen: 172.17.0.1:7879
data_dir: /var/lib/harness
secrets_file: /etc/harness/secrets.env
session:
idle_minutes: 60
absolute_hours: 12
lockout:
max_failures: 5
lock_minutes: 15
auth:
require_totp_for_admins: true
gateway:
base_url: http://10.8.0.20:4000
enabled_by_default: true
auth_header: bearer
models:
opus: claude-opus-5-5
sonnet: claude-sonnet-5-5
haiku: claude-haiku-5-5
tags: true
limits:
max_concurrent_agents: 6
daily_budget_usd: 200
daily_token_budget: 50000000
sandbox:
network:
default_allow: [registry.npmjs.org, pypi.org, files.pythonhosted.org, github.com, codeload.github.com]
cloudflare:
bridge_url: https://harness-sandbox-bridge.your-account.workers.dev
projects:
- name: shop-storefront
repo: git@github.com:your-org/shop-storefront.git
workflow: WORKFLOW.md
auto_start: true
secrets_file: /etc/harness/projects/shop-storefront.env
- name: shop-api
path: /srv/checkouts/shop-api
auto_start: false
Network#
| Key | Default | Description |
|---|
listen | required | host:port (or [ipv6]:port) for the API. Port defaults to 8443. Must be a specific interface. Use 127.0.0.1:8443 and an SSH tunnel; see Connect the app to a server. |
allow_all_interfaces | false | Set true to allow 0.0.0.0, :: or * in listen. Deliberately awkward. |
trusted_proxies | none | CIDR ranges (or single addresses) of load balancers in front of the server, for example [10.0.0.0/16]. Only a connection from one of them can name the client in X-Forwarded-For, and the client is the rightmost address that isn't a trusted proxy. Without it the header is ignored. See Behind a load balancer. |
tls.cert | | PEM certificate chain. TLS 1.3 only. |
tls.key | | PEM private key. Must be mode 0600. |
insecure_http_behind_proxy | false | Plain HTTP, only when listen is 127.0.0.1, ::1 or localhost, behind a TLS-terminating proxy on the same host. |
tools_listen | 172.17.0.1:7879 on Linux, 127.0.0.1:7879 on macOS | The agents' MCP tools listener; serves only /mcp/<session-token>, without TLS. On Linux use the Docker bridge; on a Mac with Docker Desktop, loopback works (containers reach it as host.docker.internal). Port defaults to 7879. |
Storage#
| Key | Default | Description |
|---|
data_dir | /var/lib/harness | Users, audit log, cloned repos, run history, logs and the secrets overlay. Kept at 0700. |
secrets_file | | KEY=VALUE file of server-wide secrets and variables. Must be 0600. See Secrets. |
Sessions and sign-in#
| Key | Default | Description |
|---|
session.idle_minutes | 60 | A session ends after this long without a request. Must be positive. |
session.absolute_hours | 12 | A session ends this long after sign-in, regardless of activity. Over 24 is warned about. |
lockout.max_failures | 5 | Failed sign-ins per account within the window before it's locked. Per IP address the threshold is 4× this. |
lockout.lock_minutes | 15 | The window and the lock's duration. |
auth.require_totp_for_admins | false | Admins must have an authenticator enrolled to sign in; check fails while any active admin has none. Recommended. |
Model gateway#
Server-wide equivalent of Settings → Model Gateway. The key goes in secrets.env as HARNESS_GATEWAY_API_KEY (or LITELLM_API_KEY).
| Key | Default | Description |
|---|
gateway.base_url | | Gateway URL. |
gateway.enabled_by_default | true when base_url is set | Route every project through it unless it opts out. |
gateway.auth_header | bearer | bearer or x-api-key. |
gateway.models.opus, .sonnet, .haiku, .codex | | Model slots. |
gateway.tags | true | x-litellm-tags spend tags. |
gateway.disable_experimental_betas | false | For proxies that reject Claude Code's beta fields. |
gateway.wire_api | responses | Codex: responses or chat. |
Limits#
| Key | Default | Description |
|---|
limits.max_concurrent_agents | none | Agents at once across all projects. |
limits.daily_budget_usd | none | Dispatch stops for the day once all projects together reach it. |
limits.daily_token_budget | none | The same, in tokens. |
Sandbox#
| Key | Default | Description |
|---|
sandbox.network.default_allow | SI Fabrik's built-in list | Hosts every sandbox may reach. See Network policy. |
sandbox.cloudflare.bridge_url | | Bridge Worker URL for sandbox.provider: cloudflare. Token: HARNESS_CLOUDFLARE_TOKEN in secrets.env. |
Projects#
| Key | Default | Description |
|---|
projects[].name | required | Unique name; how clients see the project. |
projects[].repo | | Git URL, cloned once into data_dir/repos/<name>. |
projects[].path | | An existing checkout, used in place. Set repo or path. |
projects[].workflow | WORKFLOW.md | Path of the workflow file inside the repository. |
projects[].auto_start | false | Start the orchestrator when the server starts. |
projects[].secrets_file | | The project's own KEY=VALUE file (0600), layered over secrets_file. |
See Projects on a server.
Security checks#
harnessd check runs these checks, and harnessd serve runs them again and refuses to start on any error:
| Check | Error when |
|---|
| Interface | listen binds every interface without allow_all_interfaces: true |
| TLS | No tls.cert/tls.key, unless plain HTTP is allowed on loopback with insecure_http_behind_proxy |
| File modes | tls.key, secrets_file or any projects[].secrets_file is readable by group or others, or missing |
| Tools listener | tools_listen binds every interface, or equals listen |
| Trusted proxies | A trusted_proxies range is wider than /8 (IPv4) or /32 (IPv6). An entry that isn't an address or range stops the file from loading. |
| Lifetimes | Session or lockout values aren't positive |
| Admins | There's no active admin account |
| Admin TOTP | require_totp_for_admins is on and an active admin has no authenticator |
Warnings: tools_listen on loopback (Docker sandboxes can't reach it), session.absolute_hours over 24, trusted_proxies set while listen binds every interface, and no projects configured.
See also#