Key points
- Bootstrap environment and deployment secrets are not the long-term source of truth — GitOps is. They exist so the platform can start and reach the point where GitOps loads.
JWT_SIGNING_KEYandSERVICE_JWT_SIGNING_KEYmust be different secrets so user tokens cannot impersonate services.- Compose fails fast on a missing required value rather than starting in a broken state.
- Production should keep
DISPATCHER_TLS_MODEatmtlsortls, neverdisabled. - Rotating
SERVICE_JWT_SIGNING_KEYandDISPATCHER_TLS_SECRETtogether is what prevents retired runners from rejoining a replacement control plane.
Field reference
DATABASE_URLapi, aaastringRequiredNone
PostgreSQL connection string for runtime state, config sync state, auth records, run records, logs, and evidence.
DATABASE_URL=postgres://nopsai:***@db:5432/nopsai?sslmode=disable- Compose fails fast with
set DATABASE_URLwhen it is missing.
Holds every durable product record. Treat as a top-tier secret.
docker-compose.yaml
NOPSAI_MASTER_KEYapistringRequiredNone
Root application encryption material for the credential registry and encrypted envelopes.
NOPSAI_MASTER_KEY=<32+ byte random value>Losing this key makes every stored credential unrecoverable; rotating it requires re-encrypting the registry.
config/config.go
JWT_SIGNING_KEYapistringRequiredNone
Signs user, personal-access, service-account, browser, CLI, automation, and hosted MCP tokens.
JWT_SIGNING_KEY=<high-entropy value>Must be a different secret from SERVICE_JWT_SIGNING_KEY so user tokens cannot impersonate services.
config/config.go
SERVICE_JWT_SIGNING_KEYapi, dispatcher, git-bot, runners, agentsstringRequiredNone
Signs internal REST and dispatcher service tokens between platform components.
SERVICE_JWT_SIGNING_KEY=<high-entropy value>Shared by every component. Rotating it, together with DISPATCHER_TLS_SECRET, is what stops retired runners from rejoining.
config/config.go
NOPSAI_BOOTSTRAP_ADMIN_EMAILapistringRequired[email protected] in Compose
Email address of the first local bootstrap administrator.
[email protected]docker-compose.yaml
NOPSAI_BOOTSTRAP_ADMIN_PASSWORDapistringRequiredNone
Initial password used to create or rotate the first local bootstrap administrator.
NOPSAI_BOOTSTRAP_ADMIN_PASSWORD=<generated value>- Generated Docker Compose installs reject the built-in development
adminpassword.
Rotates on first login by default. See NOPSAI_BOOTSTRAP_ADMIN_MUST_CHANGE_PASSWORD.
docker-compose.yaml
NOPSAI_BOOTSTRAP_ADMIN_PASSWORD_FILEapipathOptionalNone
File path form of the bootstrap password, for secret managers that mount files.
NOPSAI_BOOTSTRAP_ADMIN_PASSWORD_FILE=/run/secrets/bootstrap-admin-passwordconfig/config.go
NOPSAI_BOOTSTRAP_ADMIN_MUST_CHANGE_PASSWORDapibooleanOptionaltrue
Forces the bootstrap administrator to rotate the password at first login.
NOPSAI_BOOTSTRAP_ADMIN_MUST_CHANGE_PASSWORD=trueTurning this off leaves a deployment-provisioned password in place indefinitely.
docker-compose.yaml
NOPSAI_BOOTSTRAP_ADMIN_ALLOW_DEFAULT_PASSWORDapibooleanOptionalfalse
Allows the built-in development password. Intended for throwaway local environments only.
NOPSAI_BOOTSTRAP_ADMIN_ALLOW_DEFAULT_PASSWORD=falseMust stay false in any environment reachable by more than one person.
docker-compose.yaml
NOPSAI_API_URLdispatcher, git-bot, runnersurlRequiredhttp://nopsai:8080 in Compose
Private API callback URL used by components that report back to the control plane.
NOPSAI_API_URL=http://nopsai:8080docker-compose.yaml
AAA_API_URLapiurlRequiredhttp://aaa:8082 in Compose
Private URL of the AAA authorization service.
AAA_API_URL=http://aaa:8082docker-compose.yaml
GIT_BOT_API_URLapiurlRequiredNone
Private git-bot URL used for repository fetch/write and GitHub check-run updates.
GIT_BOT_API_URL=http://git-bot:8083config/config.go
DISPATCHER_GRPC_ADDRESSapi, runners, agentshost:portRequireddispatcher:9090 in Helm, dispatcher:9091 in Compose
Dispatcher gRPC endpoint. Cross-namespace Kubernetes runners normally need the fully qualified dispatcher.<platform-namespace>.svc.cluster.local:9090.
DISPATCHER_GRPC_ADDRESS=dispatcher.nopsai.svc.cluster.local:9090- The dispatcher container itself listens on :9090 unless DISPATCHER_LISTEN_ADDRESS overrides it.
- Helm reads this from
topology.dispatcherGRPCAddress.
deploy/helm/nopsai/values.yaml
NOPSAI_LISTEN_ADDRESSapihost:portOptional:8080
Bind address for the API HTTP server.
NOPSAI_LISTEN_ADDRESS=:8080config/config.go
AAA_LISTEN_ADDRESSaaahost:portOptional:8082
Bind address for the AAA service.
AAA_LISTEN_ADDRESS=:8082config/config.go
DISPATCHER_LISTEN_ADDRESSdispatcherhost:portOptional:9090
Bind address for the dispatcher gRPC server.
DISPATCHER_LISTEN_ADDRESS=:9090services/dispatcher/internal/app/app.go
GIT_BOT_LISTEN_ADDRESSgit-bothost:portOptional:8083
Bind address for the git-bot service.
GIT_BOT_LISTEN_ADDRESS=:8083config/config.go
NOPSAI_PUBLIC_URLapiurlOptionalNone
Externally reachable base URL, used in notification links and OIDC redirect construction.
NOPSAI_PUBLIC_URL=https://nopsai.example.comconfig/config.go
NOPSAI_PLATFORM_IDapi, runnersstringOptionalNone
Ownership boundary that keeps bundled runners and generated runner installs attributed to the same platform.
NOPSAI_PLATFORM_ID=prod-euconfig/config.go
DISPATCHER_TLS_MODEdispatcher, runners, agentsstringRequiredNone
Transport mode for dispatcher gRPC.
DISPATCHER_TLS_MODE=mtlsmtls, tls, disabled
Production must not use disabled. Startup gates check this when production gates are required.
config/config.go
DISPATCHER_TLS_SECRETdispatcher, runners, agentsstringConditionalNone
Shared high-entropy TLS or mTLS bootstrap secret. Required unless the mode is disabled.
DISPATCHER_TLS_SECRET=<high-entropy value>Rotate together with SERVICE_JWT_SIGNING_KEY before exposing a fresh dispatcher, otherwise old runner definitions can authenticate again.
config/config.go
DISPATCHER_TLS_SERVER_NAMErunners, agentsstringOptionalHost from DISPATCHER_GRPC_ADDRESS
Server name presented during TLS verification when it differs from the dial address.
DISPATCHER_TLS_SERVER_NAME=dispatcher.nopsai.svcconfig/config.go
DISPATCHER_ROUTINGdispatchermap<string,string[]>OptionalNone
Configured route map from scope to runner IDs. GitOps owns this; the live view is derived from registered runners.
DISPATCHER_ROUTING=platform/production=runner-a,runner-bconfig/config.go
RUNTIMErunnerstringOptionaldocker
Execution runtime the runner provides. Hybrid deployments use a comma-separated list.
RUNTIME=docker,kubernetesdocker, kubernetes
config/config.go
RUNNER_IDrunnerstringRequiredGenerated with a unique suffix
Dispatcher identity for this runner. Generated installs add a unique suffix to the friendly name.
RUNNER_ID=eu-build-01-7f2a- Deliberate runner removal adds this ID to
ejected_runner_idsand blocks reconnection.
config/config.go
RUNNER_NAMErunnerstringOptionalNone
Friendly name preserved by generated installs and shown in the dispatcher fleet view.
RUNNER_NAME=eu-build-01RUNNER_SCOPESrunnerstringOptionalAll scopes
Scopes this runner accepts work for. An explicit empty value means all scopes.
RUNNER_SCOPES=platform/production,platform/stagingconfig/config.go
RUNNER_CAPACITYrunnerintegerOptionalRunner default
Number of concurrent runs this runner advertises to the dispatcher.
RUNNER_CAPACITY=4config/config.go
DOCKER_NETWORK_NAMEdocker runnerstringOptionalDocker bridge
Network joined by step containers. Blank, bridge, or default keeps normal egress; none disables step networking; any other value joins that network.
DOCKER_NETWORK_NAME=nonebridge, default, none, <custom network name>
Use none when step workloads must run without egress, or a dedicated network for controlled egress.
config/config.go
RUNTIME_POOLSkubernetes runnermapOptionalNone
Named Kubernetes runtime pools selectable through pipeline or step runtime_pool.
RUNTIME_POOLS=gpu:node-role=gpuconfig/config.go
KUBERNETES_NAMESPACEkubernetes runnerstringOptionalRunner namespace
Namespace where the runner creates agent pods, step pods, and workspace PVCs.
KUBERNETES_NAMESPACE=nopsai-runnersconfig/config.go
KUBERNETES_SERVICE_ACCOUNTkubernetes runnerstringOptionalChart-managed runner service account
RBAC-bearing service account used by the runner Deployment and agent pods.
KUBERNETES_SERVICE_ACCOUNT=nopsai-runnerdeploy/helm/nopsai
KUBERNETES_WORKLOAD_SERVICE_ACCOUNTkubernetes runnerstringOptionalChart-managed no-RBAC service account
Separate service account for step pods, deliberately without cluster permissions.
KUBERNETES_WORKLOAD_SERVICE_ACCOUNT=nopsai-workloadKeeping step pods on a no-RBAC account is what stops workload code from calling the Kubernetes API.
deploy/helm/nopsai
KUBERNETES_IMAGE_PULL_SECRETSkubernetes runnerstring[]OptionalNone
Infrastructure-owned pull secrets attached to runner and step pods for private registries.
KUBERNETES_IMAGE_PULL_SECRETS=regcredconfig/config.go
KUBERNETES_STORAGE_CLASSkubernetes runnerstringOptionalCluster default
Storage class used for the agent-owned workspace PVC.
KUBERNETES_STORAGE_CLASS=fast-ssdKUBERNETES_DEFAULT_WORKSPACE_SIZEkubernetes runnerstringOptionalChart default
Requested size of the run workspace PVC.
KUBERNETES_DEFAULT_WORKSPACE_SIZE=20GiKUBERNETES_AFFINITY_ENABLEDkubernetes runnerbooleanOptionalChart default
Whether step pods in a run are scheduled onto the same node by default.
KUBERNETES_AFFINITY_ENABLED=truepipeline affinity_enabled
KUBERNETES_CLEANUP_FINISHED_PODSkubernetes runnerbooleanOptionalChart default
Whether finished step pods are deleted after their status is reported.
KUBERNETES_CLEANUP_FINISHED_PODS=trueNOPSAI_REGISTRY_DOCKER_CONFIG_B64docker runnerstringOptionalNone
Base64 Docker config delivered at runner bootstrap so Docker runners can build per-image RegistryAuth locally.
NOPSAI_REGISTRY_DOCKER_CONFIG_B64=<base64 docker config json>- The legacy
/v1/internal/registry-auth/dockerbroker route has been removed.
Keeps registry credentials on the runner instead of brokering them per image pull.
doc/runner-registry-auth.md
SYSTEM_LOGS_PROVIDERapistringOptionalNone
Provider used to read live platform logs. Hybrid deployments use a comma-separated list.
SYSTEM_LOGS_PROVIDER=docker,kubernetesdocker, kubernetes
config/config.go
SYSTEM_LOGS_DOCKER_HOSTapiurlConditionaltcp://docker-socket-proxy:2375 in Compose
Restricted Docker endpoint used for System Logs. Required for the docker provider.
SYSTEM_LOGS_DOCKER_HOST=tcp://docker-socket-proxy:2375Point this at the restricted socket proxy, never the raw Docker socket. The proxy allow-lists the reads System Logs needs.
docker-compose.yaml
SYSTEM_LOGS_KUBERNETES_NAMESPACEapistringOptionalPlatform namespace
Namespace searched for platform pods when the kubernetes provider is used.
SYSTEM_LOGS_KUBERNETES_NAMESPACE=nopsaiSYSTEM_LOGS_KUBERNETES_LABEL_SELECTORapistringOptionalNone
Label selector limiting which pods appear as System Logs sources.
SYSTEM_LOGS_KUBERNETES_LABEL_SELECTOR=app.kubernetes.io/part-of=nopsaiFINAL_OUTPUT_PDF_RENDERER_URLapiurlConditionalhttp://gotenberg:3000 in Compose
Gotenberg endpoint used to render PDF final outputs. Required for type: pdf.
FINAL_OUTPUT_PDF_RENDERER_URL=http://gotenberg:3000docker-compose.yaml
FINAL_OUTPUT_PDF_TIMEOUT_SECONDSapiintegerOptional45
Timeout applied to a single PDF render request.
FINAL_OUTPUT_PDF_TIMEOUT_SECONDS=45config.yml
RUNTIME_OUTPUT_MAX_BYTESapi, agentintegerOptional65536
Maximum size of a single runtime output value written under /nopsai/outputs.
RUNTIME_OUTPUT_MAX_BYTES=65536config.yml
DEFAULT_PIPELINE_TIMEOUTapidurationOptionalNone
Fallback whole-run timeout applied when a pipeline does not declare timeout.
DEFAULT_PIPELINE_TIMEOUT=2hconfig/config.go
DATA_BACKUP_DIRapipathOptional/data/backups
Directory where product backups are written as gzip-compressed JSON Lines files.
DATA_BACKUP_DIR=/data/backups- The default Compose topology does not mount this path as durable storage.
config/config.go
METRICS_REQUIRE_AUTHapibooleanOptionalfalse
Whether GET /metrics requires a bearer token. Public by default for scraper compatibility.
METRICS_REQUIRE_AUTH=trueEnable when the metrics endpoint is reachable outside the cluster network.
config/config.go
NOPSAI_REQUIRE_PRODUCTION_GATESapibooleanOptionalfalse
Enforces the production startup gates, refusing to start on an unsafe configuration.
NOPSAI_REQUIRE_PRODUCTION_GATES=trueTurn this on in production so weak transport and default credentials fail startup instead of running.
pkg/startupgates/startupgates.go
AUTO_REMOVAL_AGENT_CONTAINERdocker runnerbooleanOptionaltrue
Whether the per-run agent container is removed after the run completes.
AUTO_REMOVAL_AGENT_CONTAINER=falseconfig/config.go
LOG_LEVELall servicesstringOptionalinfo
Minimum severity written to service logs.
LOG_LEVEL=debugdebug, info, warn, error
config/config.go
LOG_FORMATall servicesstringOptionaljson
Service log encoding.
LOG_FORMAT=consoleconfig/config.go
NOPSAI_ENVIRONMENTall servicesstringOptionallocal
Environment label attached to logs, metrics, and audit metadata.
NOPSAI_ENVIRONMENT=productionconfig/config.go
AUTH_PROVIDER_LOCAL_ENABLEDapibooleanOptionaltrue
Whether local username and password login is offered.
AUTH_PROVIDER_LOCAL_ENABLED=false- Can only be disabled while external authentication is enabled with at least one enabled provider.
- The protected break-glass administrator remains available regardless.
config/config.go
JWT_EXPIRY_MINUTESapiintegerOptionalPlatform default
Lifetime of an issued access token.
JWT_EXPIRY_MINUTES=60config/config.go
REFRESH_TOKEN_TTL_MINUTESapiintegerOptionalPlatform default
Lifetime of a refresh token.
REFRESH_TOKEN_TTL_MINUTES=10080config/config.go
IDLE_TIMEOUT_MINUTESapiintegerOptionalPlatform default
Idle period after which a browser session is ended.
IDLE_TIMEOUT_MINUTES=30config/config.go
RATE_LIMIT_LOGIN_PER_MINUTEapiintegerOptionalPlatform default
Login attempts accepted per minute before requests are throttled.
RATE_LIMIT_LOGIN_PER_MINUTE=10Together with the lockout settings, this is the brute-force control for local login.
config/config.go
LOGIN_LOCKOUT_THRESHOLDapiintegerOptionalPlatform default
Failed attempts within the lockout window before an account is locked.
LOGIN_LOCKOUT_THRESHOLD=5config/config.go
LOGIN_LOCKOUT_WINDOW_MINUTESapiintegerOptionalPlatform default
Window over which failed login attempts are counted.
LOGIN_LOCKOUT_WINDOW_MINUTES=15config/config.go
JWT_ISSUERapistringOptionalPlatform default
Issuer claim written into user and API tokens.
JWT_ISSUER=https://nopsai.example.comconfig/config.go
JWT_AUDIENCEapistringOptionalPlatform default
Audience claim written into user and API tokens.
JWT_AUDIENCE=nopsai-apiconfig/config.go
JWT_RSA_KEY_PATHapipathOptionalNone
RSA private key path for asymmetric token signing instead of the shared HMAC key.
JWT_RSA_KEY_PATH=/run/secrets/jwt-rsa.pemconfig/config.go
SERVICE_JWT_ISSUERall servicesstringOptionalPlatform default
Issuer claim for internal service tokens.
SERVICE_JWT_ISSUER=nopsai-internalconfig/config.go
SERVICE_JWT_AUDIENCEall servicesstringOptionalPlatform default
Audience claim for internal service tokens.
SERVICE_JWT_AUDIENCE=nopsai-servicesconfig/config.go
GITHUB_APP_IDapi, git-botstringOptionalNone
GitHub App ID. The long-term source of truth is setting/git-apps/github.yaml.
GITHUB_APP_ID=1234567config/config.go
GITHUB_INSTALLATION_IDapi, git-botstringOptionalNone
Single-installation bootstrap value. Multi-installation setups use the GitOps file instead.
GITHUB_INSTALLATION_ID=87654321config/config.go
GITHUB_PRIVATE_KEY_CREDENTIAL_REFapi, git-botstringOptionalNone
Credential registry reference holding the GitHub App private key.
GITHUB_PRIVATE_KEY_CREDENTIAL_REF=git-apps/github-private-keyPreferred over the legacy inline key and key-path variables.
config/config.go
GITHUB_WEBHOOK_CREDENTIAL_REFapi, git-botstringOptionalNone
Credential registry reference holding the GitHub webhook signing secret.
GITHUB_WEBHOOK_CREDENTIAL_REF=git-apps/github-webhook-secretconfig/config.go
GITHUB_PRIVATE_KEYapi, git-botstringOptionalNone
Legacy inline private key.
GITHUB_PRIVATE_KEY=<legacy inline PEM private key>Superseded by GITHUB_PRIVATE_KEY_CREDENTIAL_REF
config/config.go
GITHUB_PRIVATE_KEY_PATHapi, git-botpathOptionalNone
Legacy private key file path.
GITHUB_PRIVATE_KEY_PATH=/run/secrets/github-app.pemSuperseded by GITHUB_PRIVATE_KEY_CREDENTIAL_REF
config/config.go
GITHUB_WEBHOOK_SECRETapi, git-botstringOptionalNone
Legacy inline webhook signing secret.
GITHUB_WEBHOOK_SECRET=<shared secret>Superseded by GITHUB_WEBHOOK_CREDENTIAL_REF
config/config.go
Examples
apiVersion: v1
kind: Secret
metadata:
name: nopsai-bootstrap
stringData:
database-url: postgres://nopsai:***@nopsai-postgresql:5432/nopsai
master-key: <32+ byte random value>
jwt-signing-key: <high-entropy value>
service-jwt-signing-key: <different high-entropy value>
aaa-shared-internal-token: <high-entropy value>
bootstrap-admin-password: <generated value>
dispatcher-tls-secret: <high-entropy value>How it works
The table below covers every documented variable. Filter by scope to see what a single component actually reads, or use the environment variable index for the same rows grouped for scanning.
Keep bootstrap-only values such as DATABASE_URL, NOPSAI_MASTER_KEY, JWT_SIGNING_KEY, SERVICE_JWT_SIGNING_KEY, AAA_SHARED_INTERNAL_TOKEN, and the bootstrap admin password in deployment secrets or the service environment — not in a configuration repository.
Most product settings move to GitOps once the platform is running. Files under setting/system/ become authoritative for runner topology, authentication, mail, data management, models, MCP, and Agent roles.
Implementation evidence
config/config.goConfig struct with yaml and env tags for every setting.
config.ymlWhich values stay bootstrap-only and which move to GitOps.

