Configuration Options
Configure Windshift with command-line flags or environment variables. Docker deployments usually use environment variables. The standalone binary can use either style. Do not set both for one setting because current releases do not use one precedence rule for every setting.
Production checklist
For a self-hosted production deployment, set these first:
| Setting | Why it matters |
|---|---|
SSO_SECRET |
Required. Signs session cookies and SSO state. Generate once and keep it stable across restarts. |
BASE_URL |
Required for correct email links, SSO redirects, WebAuthn origins, calendar feeds, and runner callbacks. |
ALLOWED_HOSTS |
Optional browser-origin allowlist. It is derived from BASE_URL for a single-domain deployment; set it explicitly only when you need additional origins. |
USE_PROXY=true |
Required when TLS terminates at a reverse proxy. Only enable when Windshift is not directly internet-reachable. |
SESSION_IP_BINDING |
Controls how Windshift handles a session that changes client IP address. Start with the default log mode. |
ATTACHMENT_PATH |
Required if users upload attachments and you want them persisted outside the database/container filesystem. |
POSTGRES_CONNECTION_STRING |
Recommended for multi-user production. SQLite is fine for small or single-node installs. |
Generate a secret with:
openssl rand -hex 32Keep the value. Changing SSO_SECRET invalidates existing sessions and can break encrypted SSO/provider credentials.
Configuration precedence
Current releases resolve flags and environment variables for each setting. Avoid setting both for one setting.
- Environment variable wins:
PORT,DB_PATH,POSTGRES_CONNECTION_STRING,ATTACHMENT_PATH,LOG_LEVEL,LOG_FORMAT,SSH_PORT,SSH_HOST,MAX_READ_CONNS,MAX_WRITE_CONNS,MAX_USER_CONCURRENCY,POSTGRES_REPLICA_COUNT,POSTGRES_CONNECTION_HEADROOM,DB_REQUEST_TIMEOUT,ALLOW_LOCAL_CONNECTIONS, andWINDSHIFT_MEMORY_LIMIT_MB. - CLI flag wins:
ALLOWED_HOSTS,BASE_URL,WINDSHIFT_CONTEXT_PATH,LLM_PROVIDERS_FILE, andAI_PROMPTS_DIR. - Boolean settings combine: An enabled flag or a
trueenvironment variable enables the setting. A flag cannot disable a setting that its environment variable enables. This includesUSE_PROXY,DISABLE_PLUGINS,SSH_ENABLED, andCODING_AGENT_ENABLED.
Built-in defaults apply when neither source provides a value. Until releases standardize this behavior, use one configuration style for each setting.
HTTP, proxy, and public URL
| Flag | Env var | Default | Description |
|---|---|---|---|
--port, -p |
PORT |
8080 |
HTTP server port. |
--base-url |
BASE_URL |
- | Public URL users access Windshift from, for example https://windshift.example.com. |
--context-path |
WINDSHIFT_CONTEXT_PATH |
- | Optional subpath such as /windshift when served below a domain root. |
--allowed-hosts |
ALLOWED_HOSTS |
derived from BASE_URL |
Comma-separated browser origins for CORS, CSRF, WebAuthn, and SSO redirect validation. This is not a Host-header request filter. |
--allowed-port |
- | - | Extra port used for CORS and WebAuthn origin validation. |
--use-proxy |
USE_PROXY |
false |
Trust X-Forwarded-Proto and X-Forwarded-For from trusted proxies. |
--allow-insecure-http |
ALLOW_INSECURE_HTTP |
false |
Allow browser access via plain http on non-localhost origins. Trusted LANs and testing only. |
--additional-proxies |
ADDITIONAL_PROXIES |
- | Comma-separated trusted proxy IPs in addition to private network ranges. |
| - | FORM_EMBED_ORIGINS |
- | Comma-separated origins permitted to embed public forms. |
--no-csrf |
- | false |
Disable CSRF protection. Development only. |
When USE_PROXY=true, Windshift trusts forwarded headers. Make sure only your reverse proxy can reach the Windshift port. Do not expose the backend port directly to the internet.
ALLOWED_HOSTS configures browser-security middleware. It does not reject requests based on their Host header. For a normal single-domain deployment, leave it unset. Windshift then derives the origin from BASE_URL. An explicit value overrides that derivation.
Plain HTTP is localhost-only by default. A
BASE_URLsuch ashttp://myhost.internal:8080fails at startup withFailed to create CORS middleware ... insecure origin patterns. Credentialed CORS refuses insecure origins other than localhost. Serve HTTPS through a reverse proxy withUSE_PROXY=true, or directly with--tls-certand--tls-key. On a trusted LAN, you can setALLOW_INSECURE_HTTP=true. See Docker for details.
For subpath deployments, include the path in both BASE_URL and WINDSHIFT_CONTEXT_PATH:
BASE_URL=https://example.com/windshift
WINDSHIFT_CONTEXT_PATH=/windshiftSecrets and authentication
| Flag | Env var | Default | Description |
|---|---|---|---|
| - | SSO_SECRET |
required | Preferred session and SSO signing/encryption secret. |
| - | SESSION_SECRET |
- | Backward-compatible fallback when SSO_SECRET is unset. |
--enable-fallback |
ENABLE_ADMIN_FALLBACK |
false |
Enable password-based admin fallback for restrictive SSO setups. |
| - | RECOVER_USER |
- | Recovery helper for emergency user access flows. |
| - | SESSION_VALIDATION_CACHE_TTL |
5s |
How long session-validation results are cached. Accepts a Go duration such as 5s. |
| - | SESSION_IP_BINDING |
log |
Handle a client-IP change on an existing session. Accepted values are log, strict, and off. |
SSO_SECRET is required at startup. SESSION_SECRET is accepted only as a fallback for older deployments.
WebAuthn
| Flag | Env var | Default | Description |
|---|---|---|---|
| - | WEBAUTHN_RP_ID |
BASE_URL host |
WebAuthn relying-party ID. Use a hostname, or a full HTTP or HTTPS URL from which Windshift extracts the hostname. |
| - | WEBAUTHN_RP_NAME |
Windshift |
Display name shown by authenticators. |
Set BASE_URL and ALLOWED_HOSTS correctly before enabling passkeys/WebAuthn.
The RP ID resolves in this order: WEBAUTHN_RP_ID, then the hostname from BASE_URL, then the process host name. In a container the process host name is the container ID, which no browser will match, so set WEBAUTHN_RP_ID explicitly in containerized deployments. See WebAuthn relying-party ID in containers.
localhost is a valid RP ID. The development exception also permits WebAuthn over HTTP when the host is localhost. A single-label hostname such as windshift is not a valid RP ID for passkeys. Use a dotted hostname or localhost.
Windshift 0.8.5 and later accept values such as https://windshift.example.com:8443 for WEBAUTHN_RP_ID. Windshift stores only windshift.example.com as the protocol RP ID.
Database
| Flag | Env var | Default | Description |
|---|---|---|---|
--db |
DB_PATH |
windshift.db |
SQLite database file path. |
--postgres-connection-string, --pg-conn |
POSTGRES_CONNECTION_STRING |
- | PostgreSQL connection string. When set, PostgreSQL is used instead of SQLite. |
| - | DB_TYPE |
- | Set to postgres to build a connection string from POSTGRES_* variables. |
| - | POSTGRES_SSLMODE |
disable |
TLS mode for a connection built from the split POSTGRES_* variables: disable, allow, prefer, require, verify-ca, or verify-full. Use require or stricter for remote or managed PostgreSQL. |
| - | POSTGRES_HOST |
postgres |
Host used when DB_TYPE=postgres and no connection string is supplied. |
| - | POSTGRES_PORT |
5432 |
PostgreSQL port for generated connection strings. |
| - | POSTGRES_USER |
windshift |
PostgreSQL user for generated connection strings. |
| - | POSTGRES_PASSWORD |
- | PostgreSQL password for generated connection strings. |
| - | POSTGRES_DB |
windshift |
PostgreSQL database for generated connection strings. |
--max-read-conns |
MAX_READ_CONNS |
30 |
Read connection pool size. On PostgreSQL this also sizes the pool (max open = this value, max idle = half). Keep it under the server's max_connections. |
--max-write-conns |
MAX_WRITE_CONNS |
1 |
SQLite write connection pool size. |
--postgres-replica-count |
POSTGRES_REPLICA_COUNT |
1 |
Number of Windshift replicas sharing PostgreSQL, used to validate the aggregate connection budget. |
--postgres-connection-headroom |
POSTGRES_CONNECTION_HEADROOM |
10 |
PostgreSQL connections reserved for migrations, administration, and other clients when validating the connection budget. |
--db-request-timeout |
DB_REQUEST_TIMEOUT |
12s |
Maximum database-work duration for normal HTTP requests. Accepts a Go duration such as 12s or 1m. |
--max-user-concurrency |
MAX_USER_CONCURRENCY |
16 |
Maximum simultaneous in-flight /api requests per authenticated user. 0 disables the cap. |
Use POSTGRES_CONNECTION_STRING when possible. Use POSTGRES_* variables when Docker Compose or a secret manager makes separate values easier.
Process memory and cache budget
| Flag | Env var | Default | Description |
|---|---|---|---|
--memory-limit-mb |
WINDSHIFT_MEMORY_LIMIT_MB |
2048 MiB |
Total Windshift process-memory budget. Values below 512 MiB fail at startup. |
The environment variable wins when both forms are set. The value uses MiB.
Windshift derives two soft budgets from the process budget:
- The Go runtime receives a soft heap target equal to 80% of the process budget.
- BigCache receives 25% of the process budget, capped at 512 MiB.
The cache budget is shared by item, permission, notification, activity, and authentication caches. Cache eviction affects performance, not correctness. Windshift reloads an evicted value from persistent storage.
The Go target is not a hard RSS limit. Database allocations, goroutine stacks, memory-mapped files, and temporary request data can raise process RSS above it. Set the container memory limit to at least the Windshift process budget. Do not set a lower container limit and rely on garbage collection.
Administrators can inspect the resolved budget, cache capacity, entries, hits, misses, and evictions under Admin → Diagnostics → Cache memory.
Files and attachments
| Flag | Env var | Default | Description |
|---|---|---|---|
--attachment-path |
ATTACHMENT_PATH |
- | Directory for uploaded attachments. |
In Docker, mount a persistent volume at /data and use /data/attachments.
TLS
| Flag | Env var | Default | Description |
|---|---|---|---|
--tls-cert |
- | - | TLS certificate path when Windshift terminates TLS directly. |
--tls-key |
- | - | TLS private key path when Windshift terminates TLS directly. |
| - | TLS_SKIP_VERIFY |
false |
Disable certificate-chain and hostname verification for outbound TLS connections. Use only with trusted self-signed destinations. |
Most deployments should terminate TLS at a reverse proxy and run Windshift over an internal network.
Keep TLS_SKIP_VERIFY=false in production. If you set it to true, Windshift accepts unverified certificates for outbound HTTPS, SMTP, IMAP, and LDAP connections. This setting affects the whole process, not one integration.
SSH TUI and MCP
| Flag | Env var | Default | Description |
|---|---|---|---|
--ssh |
SSH_ENABLED |
false |
Enable the SSH TUI server. |
--ssh-port |
SSH_PORT |
23234 |
SSH server port. |
--ssh-host |
SSH_HOST |
localhost |
SSH bind address. |
--ssh-key |
- | .ssh/windshift_host_key |
SSH host key path. |
--mcp |
MCP_ENABLED |
false |
Enable the MCP server at /mcp. |
Logging and rate limits
| Flag | Env var | Default | Description |
|---|---|---|---|
--log-level |
LOG_LEVEL |
info |
debug, info, warn, or error. |
--log-format |
LOG_FORMAT |
text |
text, json, or logfmt. |
--disable-ip-rate-limit |
DISABLE_IP_RATE_LIMIT |
false |
Disable IP-based rate limiting. Use only behind trusted controls. |
For container production logs, use:
LOG_FORMAT=json
LOG_LEVEL=infoPlugins
| Flag | Env var | Default | Description |
|---|---|---|---|
--disable-plugins |
DISABLE_PLUGINS |
false |
Disable the plugin system. |
| - | PLUGIN_DIR |
- | Primary plugin directory. |
| - | PLUGIN_DIRS |
- | Extra plugin directories, comma-separated. |
Private network egress
| Flag | Env var | Default | Description |
|---|---|---|---|
--allow-local-connections |
ALLOW_LOCAL_CONNECTIONS |
true |
Allow server-side HTTP clients to reach local, loopback, and private-network addresses. |
By default, Windshift allows server-side outbound HTTP to local, loopback, and private-network destinations. This affects SCM integrations, Jira import, LLM providers, webhooks, OIDC, and SMTP. Public endpoints work without configuration.
To restore blocking for private-address destinations, set the flag or environment variable to false:
./windshift --allow-local-connections=falseALLOW_LOCAL_CONNECTIONS=falseKeep the default when Windshift must reach an internal endpoint. Examples include self-hosted Gitea or GitHub Enterprise, Jira Data Center, a private identity provider, a local LLM gateway, and an internal SMTP server:
ALLOW_LOCAL_CONNECTIONS=trueThis global switch is not a per-endpoint allowlist. It permits egress to the entire private network. If you set it to false, integrations that use local or private endpoints cannot connect. Use network policy to keep sensitive endpoints, such as cloud metadata services and admin panels, out of reach of the Windshift host.
See Production-ready self-hosting for deployment profiles and a production checklist.
Earlier releases used the per-endpoint allowlists
OIDC_ALLOWED_PRIVATE_CIDRSandLLM_ALLOWED_PRIVATE_CIDRS. Windshift ignores these removed variables. UseALLOW_LOCAL_CONNECTIONSinstead.
AI and LLM providers
AI features are configured in the Windshift admin UI after startup. Operators can customize the provider catalog and network policy here.
| Flag | Env var | Default | Description |
|---|---|---|---|
--llm-providers |
LLM_PROVIDERS_FILE |
- | Path to a custom LLM providers JSON file. Replaces the built-in provider catalog. |
--ai-prompts-dir |
AI_PROMPTS_DIR |
/data/prompts in Docker |
Directory containing custom AI prompt override files. |
| - | LLM_ENDPOINT |
- | Legacy/fallback OpenAI-compatible inference endpoint. Prefer in-app AI connections for normal use. |
Windshift's built-in provider catalog includes Anthropic, OpenAI, Google Gemini, Z.AI, OpenRouter, and Local / Custom. OpenRouter includes a seed model list so admins can select a model immediately. The Refresh button can still fetch the live catalog.
Local and internal LLM endpoints
By default, Windshift allows LLM calls to local and private endpoints. Public provider endpoints need no extra configuration. If you set ALLOW_LOCAL_CONNECTIONS=false, local or private models, such as Ollama, LM Studio, or an internal gateway, cannot receive inference requests or model-list refreshes.
ALLOW_LOCAL_CONNECTIONS=falseSee Private network egress for what this switch covers.
Then create a Local / Custom AI connection in the admin UI with a base URL such as:
http://host.docker.internal:11434/v1or:
http://172.17.0.1:11434/v1Only allow the exact host or subnet you need. Do not allow broad ranges such as 10.0.0.0/8 unless your network policy already prevents access to sensitive services.
Custom provider catalog
To add a provider, change default models, or route a provider through a proxy, copy the built-in catalog. Then point Windshift to your copy:
internal/llm/llm_providers.json
Minimal OpenAI-compatible provider:
{
"providers": [
{
"type": "local",
"name": "Local / Custom",
"api_format": "openai",
"base_url": "http://127.0.0.1:11434/v1",
"models_endpoint": "/v1/models",
"models_auth_scheme": "bearer",
"models_response_format": "openai",
"models": [
{ "id": "llama3.1:8b", "name": "Llama 3.1 8B", "max_tokens": 4096 }
]
}
]
}Supported api_format values are:
| Value | Use for |
|---|---|
openai |
OpenAI-compatible chat completions APIs. |
anthropic |
Anthropic Messages API. |
Provider model-refresh settings:
| Field | Description |
|---|---|
models_endpoint |
Endpoint to fetch a model list, joined with the provider base URL. |
models_base_url |
Optional separate base URL for the model-list endpoint. |
models_auth_scheme |
bearer, anthropic, or google. |
models_response_format |
openai, anthropic, or google. |
chat_path |
Optional chat path override for OpenAI-compatible providers, for example /chat/completions. |
AI prompt overrides
Every AI feature uses an embedded system prompt. To override one prompt, place a <name>.txt file in AI_PROMPTS_DIR. Missing files keep the built-in default.
| Filename | Feature |
|---|---|
plan_my_day.txt |
Plan My Day |
catch_me_up.txt |
Catch Me Up |
find_similar.txt |
Find Similar |
decompose.txt |
Decompose |
release_notes.txt |
Release Notes |
dependency_analysis.txt |
Dependency Analysis |
ai_chat.txt |
AI Chat |
daily_briefing.txt |
Daily Briefing |
summarize_test_plan.txt |
Summarize Test Plan |
coding_agent_initial.txt |
Coding agent initial run prompt |
Default prompts are here:
Important:
ai_chat.txtcontains four runtime placeholders. In order, they are%s(today's date),%s(current user name),%d(user ID), and%d(assignee ID for "my items" lookups). Preserve all four in this order when you override the prompt.
Coding agent runner
See Coding Agent Runner for the full deployment guide.
The coding-agent system is opt-in. Enable it with --enable-coding-agent or CODING_AGENT_ENABLED=true. The Windshift server dispatches runs to windshift-runner hosts. It does not run agent containers. The server reads only these two variables:
| Flag / Env var | Default | Description |
|---|---|---|
--enable-coding-agent / CODING_AGENT_ENABLED |
false |
Enable the coding-agent system (orchestration only). |
CODING_AGENT_WS_API_URL |
BASE_URL + /api |
API URL agent containers use to reach Windshift. Override when BASE_URL is not reachable from containers. Must end in /api. |
Configure the agent image, Docker binary, worktree and cache location, concurrency, and container resource limits on the runner host. Use WSRUNNER_* variables, not Windshift server variables. See Coding Agent Runner for the runner Compose file and the full WSRUNNER_* configuration table.
Notifications, Jira, and sidecars
| Env var | Default | Description |
|---|---|---|
NOTIFICATION_FLUSH_INTERVAL |
built-in | Notification write-batcher flush interval, Go duration such as 5s. |
NOTIFICATION_BATCH_SIZE |
built-in | Notification write-batcher batch size. |
NOTIFICATION_SYNC_INTERVAL |
built-in | Notification synchronization interval. |
WINDSHIFT_NOTIFICATION_BATCH_INTERVAL |
built-in | Email notification batch scheduler cadence. |
JIRA_CAPTURE_PAYLOADS |
- | Directory for Jira import request/response payload debugging. |
LOGBOOK_ENDPOINT |
- | URL of a Logbook sidecar service, if used. |
Examples
Minimal SQLite
SSO_SECRET=$(openssl rand -hex 32) \
BASE_URL=http://localhost:8080 \
./windshift --db /data/windshift.dbProduction behind a reverse proxy
./windshift \
--postgres-connection-string "postgres://windshift:secret@db:5432/windshift?sslmode=require" \
--use-proxy \
--allowed-hosts windshift.example.com \
--base-url https://windshift.example.com \
--attachment-path /data/attachments \
--log-level info \
--log-format jsonLocal LLM on the host
ALLOW_LOCAL_CONNECTIONS=true \
./windshift --base-url http://localhost:8080Then add a Local / Custom AI connection in the UI with http://localhost:11434/v1.