Chapter 5 of 15
PatchMon Environment Variables Reference
Applies to: PatchMon 2.0+ (Go server)
This document is the authoritative reference for all environment variables supported by the PatchMon server. Configure these in your .env file, which Docker reads and passes into the server container via env_file:.
Variables are loaded from .env in the working directory by default. To use a different file, set the ENV_FILE variable to the path you want the server to read at startup.
How values are resolved
PatchMon resolves configuration in this order, highest to lowest priority:
- Environment variable (set in
.envor in your container/Pod spec) - Database value (set via the Settings UI; only for values marked Editable in UI below)
- Built-in default
This means: if a variable is set in .env, editing it in Settings → Environment has no effect until you remove the env value. The UI flags overridden values with a yellow "env" badge so you can tell at a glance why your change is being ignored. See Settings in the web UI for more on runtime tuning.
Table of Contents
- Required Variables
- Server Configuration
- Database Connection Pool
- Authentication and Sessions
- Redis Configuration
- Rate Limiting
- Password Policy
- Logging and Profiling
- OIDC / SSO
- Compliance / SSG
- RDP / Remote Access
- Body Limits
- Timezone
- Encryption Keys
- Agent Binary Overrides
- Telemetry
- File Loading
1. Required Variables
The server will refuse to start if either of these is missing or empty.
| Variable | Default | Required | Description |
|---|---|---|---|
DATABASE_URL |
(none) | Yes | PostgreSQL connection string. |
JWT_SECRET |
(none) | Yes | Secret key used to sign JWT tokens. Must be a strong, randomly generated value. |
Examples:
DATABASE_URL="postgresql://patchmon_user:strongpassword@localhost:5432/patchmon_db"
JWT_SECRET="$(openssl rand -hex 64)"
Keep
JWT_SECRETstable across restarts. Changing it invalidates all active sessions and forces every user to log in again.
2. Server Configuration
General HTTP server and network settings.
| Variable | Default | Required | Description |
|---|---|---|---|
PORT |
3000 |
No | TCP port the server listens on. In the Docker Compose stack the published port mapping follows this value, so you only need to update CORS_ORIGIN to match. |
APP_ENV |
production |
No | Runtime environment. Accepted values: production, development. NODE_ENV is also read as a backward-compatibility alias; APP_ENV takes precedence when both are set. |
CORS_ORIGIN |
http://localhost:3000 |
No | Allowed CORS origin(s). Must match the exact URL you use to access PatchMon in your browser (protocol, hostname, and port; no path, no trailing slash). To allow multiple origins, separate them with a comma and no spaces (e.g. https://patchmon.example.com,https://patchmon.internal.lan). |
ENABLE_HSTS |
false |
No | When true, the server adds an HTTP Strict Transport Security header to responses. Enable this only when PatchMon is served over HTTPS. |
TRUST_PROXY |
true |
No | When true, the server trusts X-Forwarded-For / X-Forwarded-Proto and related headers from a reverse proxy (Traefik, Caddy, nginx, NPM, etc.). Required for accurate client IP detection, correct rate limiting, and OIDC's HTTPS check when TLS is terminated at the proxy. Default is true because the officially supported deployment is Docker behind a reverse proxy; set to false explicitly only if PatchMon is exposed directly to the internet without a proxy. |
TRUSTED_PROXY_RANGES |
(empty) | No | Comma-separated CIDRs or bare IPs of the reverse proxies in front of PatchMon, for example 10.0.0.0/8,172.16.0.0/12. Used together with TRUST_PROXY to work out the real client IP from X-Forwarded-For, which drives rate limiting, login lockout, and audit logging. Leave it empty when there is a single reverse proxy, which is the usual setup: PatchMon then uses the address your proxy appended to the header, which a client cannot forge. Set it only when proxies are chained (for example Cloudflare in front of Nginx Proxy Manager), listing the intermediate hops so the original client IP is resolved rather than your CDN's egress address. Configured via environment only, and shown read-only in the settings UI, because widening it would allow clients to spoof their own IP. |
Production example:
PORT=3000
APP_ENV=production
CORS_ORIGIN=https://patchmon.example.com
ENABLE_HSTS=true
TRUST_PROXY=true
Set
CORS_ORIGINto the full URL your users type in their browser. A mismatch here is the most common cause of CORS errors after a fresh deployment. If PatchMon is accessed from multiple URLs (e.g. an external domain and an internal LAN address), list them comma-separated with no spaces:CORS_ORIGIN=https://patchmon.example.com,https://patchmon.internal.lan.
3. Database Connection Pool
These variables control how the server manages its PostgreSQL connection pool. The defaults work well for most deployments; adjust them if you are running a large number of monitored hosts or see connection timeout errors.
All timeout values are in seconds unless otherwise noted.
| Variable | Default | Required | Description |
|---|---|---|---|
PM_DB_CONN_MAX_ATTEMPTS |
30 |
No | How many times the server will retry connecting to the database on startup before giving up. Useful in containerised environments where the database may not be ready immediately. |
PM_DB_CONN_WAIT_INTERVAL |
2 |
No | Seconds to wait between each connection retry attempt on startup. |
DB_CONNECTION_LIMIT |
30 |
No | Maximum number of concurrent database connections in the pool. |
DB_POOL_TIMEOUT |
20 |
No | Seconds to wait for an available connection from the pool before returning a timeout error. |
DB_CONNECT_TIMEOUT |
10 |
No | Seconds to wait when establishing a new individual connection to PostgreSQL. |
DB_IDLE_TIMEOUT |
300 |
No | Seconds an idle connection is kept open before being closed and removed from the pool. |
DB_MAX_LIFETIME |
1800 |
No | Maximum lifetime in seconds of any connection in the pool, regardless of activity. Connections are recycled after this time to prevent stale connections. |
DB_TRANSACTION_MAX_WAIT |
10000 |
No | Milliseconds to wait for a transaction to acquire a database lock before giving up. |
DB_TRANSACTION_TIMEOUT |
30000 |
No | Milliseconds allowed for a standard database transaction to complete. |
DB_TRANSACTION_LONG_TIMEOUT |
60000 |
No | Milliseconds allowed for long-running operations (for example, bulk package imports or compliance scans). Increase this if those operations are timing out. |
Sizing guidance:
| Deployment size | DB_CONNECTION_LIMIT |
|---|---|
| Small (1–10 hosts) | 15 |
| Medium (10–50 hosts) | 30 (default) |
| Large (50+ hosts) | 50 or higher |
If you see connection pool exhausted errors in the server logs, increase DB_CONNECTION_LIMIT in increments of 10 and monitor until the errors stop.
4. Authentication and Sessions
Settings for JWT tokens, browser sessions, account lockout, two-factor authentication, and user roles.
JWT and Tokens
| Variable | Default | Required | Description |
|---|---|---|---|
JWT_SECRET |
(none) | Yes | See Required Variables. |
JWT_EXPIRES_IN |
1h |
No | How long an access token is valid. Accepts duration strings: 30m, 1h, 2h, 1d. The web interface renews the token automatically in the background, so a short value is not visible to signed-in users; it only controls how quickly a stolen token becomes useless. |
AUTH_BROWSER_SESSION_COOKIES |
false |
No | When set to true, the token and refresh_token cookies are issued without a Max-Age attribute, making them session cookies that are cleared when the browser is closed rather than persisting across browser restarts. |
Account Lockout
Lockout is applied per user account after repeated failed login attempts.
| Variable | Default | Required | Description |
|---|---|---|---|
MAX_LOGIN_ATTEMPTS |
5 |
No | Number of consecutive failed login attempts before the account is temporarily locked. |
LOCKOUT_DURATION_MINUTES |
15 |
No | How long (in minutes) an account stays locked after exceeding MAX_LOGIN_ATTEMPTS. |
Session Inactivity
| Variable | Default | Required | Description |
|---|---|---|---|
SESSION_INACTIVITY_TIMEOUT_MINUTES |
30 |
No | Minutes without user activity before a session is invalidated and the browser returns to the login screen. Set to 0 to disable the inactivity timeout entirely. |
"Activity" means someone actually using the interface: clicking, typing, scrolling, moving the pointer, or switching back to the tab. Pages that refresh data on a timer do not count, so a browser left open on an unattended machine still times out.
The timer is enforced by the server and evaluated when the next request arrives, so a session that has been idle past the limit ends at the next click or background refresh rather than at the exact second the limit passes. Signing out, or an administrator revoking the session, ends it immediately either way.
This setting is independent of JWT_EXPIRES_IN. Access tokens are renewed in the background for as
long as the session stays active, so a value larger than JWT_EXPIRES_IN works as expected.
Two-Factor Authentication (TFA)
These settings apply only to users who have TFA enabled on their accounts.
| Variable | Default | Required | Description |
|---|---|---|---|
MAX_TFA_ATTEMPTS |
5 |
No | Number of consecutive failed TFA code entries before the account is temporarily locked. |
TFA_LOCKOUT_DURATION_MINUTES |
30 |
No | How long (in minutes) a TFA lockout lasts. |
TFA_REMEMBER_ME_EXPIRES_IN |
30d |
No | How long a "remember this device" TFA exemption is valid. Accepts duration strings such as 7d, 30d, 90d. |
TFA_MAX_REMEMBER_SESSIONS |
5 |
No | Maximum number of remembered devices per user. When the limit is reached, the oldest remembered session is removed. |
User Defaults
| Variable | Default | Required | Description |
|---|---|---|---|
DEFAULT_USER_ROLE |
user |
No | Role assigned to newly created users. Accepted values: user, admin, readonly. This does not affect existing users. |
5. Redis Configuration
Redis is used for background job queues (asynq), bootstrap tokens, and TFA lockout state. A running Redis instance is required.
| Variable | Default | Required | Description |
|---|---|---|---|
REDIS_HOST |
localhost |
No | Hostname or IP address of the Redis server. |
REDIS_PORT |
6379 |
No | Port the Redis server listens on. |
REDIS_PASSWORD |
(none) | No | Redis authentication password. Strongly recommended in any non-local deployment. |
REDIS_USER |
(none) | No | Redis username for ACL-based authentication (Redis 6.0+). Leave empty to use password-only authentication. |
REDIS_DB |
0 |
No | Redis logical database number (0–15). Change this if you share a Redis instance with other applications. |
REDIS_TLS |
false |
No | When true, the server connects to Redis over TLS. |
REDIS_TLS_VERIFY |
true |
No | When set to false, the server skips Redis TLS certificate verification. Only use this in testing against self-signed certificates. |
REDIS_TLS_CA |
(none) | No | Path to a custom CA certificate file for verifying the Redis TLS connection. Only used when REDIS_TLS=true. |
REDIS_CONNECT_TIMEOUT_MS |
60000 |
No | Milliseconds to wait when establishing a new connection to Redis before timing out. |
REDIS_COMMAND_TIMEOUT_MS |
60000 |
No | Milliseconds to wait for a Redis command to complete before timing out. |
Required Redis permissions when using ACLs:
If you set REDIS_USER and restrict that user with an ACL command allowlist, the user must be able to run server-side Lua scripts. Rate limiting evaluates a small script so that a counter and its expiry are set together, which stops a dropped expiry stranding a client on HTTP 429 indefinitely.
Grant at least:
ACL SETUSER patchmon on >yourpassword ~* +@read +@write +@keyspace +eval +evalsha +script
Without +eval and +evalsha, rate limiting fails. Sign-in and password endpoints deliberately fail closed when the rate limiter is unavailable, so the visible symptom is 503 Service temporarily unavailable on login rather than a rate limiting warning. If you see that after tightening an ACL, check these permissions first.
Redis users with no ACL restrictions, and deployments using REDIS_PASSWORD alone, need no change.
Generating a secure Redis password:
openssl rand -hex 32
6. Rate Limiting
Rate limits protect the API from abuse. Limits are applied per IP address, split across three endpoint categories. All window values are in milliseconds.
| Variable | Default | Required | Description |
|---|---|---|---|
RATE_LIMIT_WINDOW_MS |
900000 |
No | Time window for the general API rate limit (default: 15 minutes). |
RATE_LIMIT_MAX |
5000 |
No | Maximum requests per window for general API endpoints (dashboards, hosts, packages, settings). |
AUTH_RATE_LIMIT_WINDOW_MS |
600000 |
No | Time window for authentication endpoints (login, token refresh) (default: 10 minutes). |
AUTH_RATE_LIMIT_MAX |
500 |
No | Maximum requests per window for authentication endpoints. |
AGENT_RATE_LIMIT_WINDOW_MS |
60000 |
No | Time window for agent check-in and reporting endpoints (default: 1 minute). |
AGENT_RATE_LIMIT_MAX |
1000 |
No | Maximum requests per window for agent endpoints. Increase this if you have a large number of agents checking in frequently. |
PASSWORD_RATE_LIMIT_WINDOW_MS |
900000 |
No | Time window for password change and reset operations (default: 15 minutes). |
PASSWORD_RATE_LIMIT_MAX |
5 |
No | Maximum password change attempts per window. Kept deliberately low to limit brute-force attacks on password reset flows. |
Quick reference: window conversions
| Milliseconds | Human-readable |
|---|---|
60000 |
1 minute |
600000 |
10 minutes |
900000 |
15 minutes |
7. Password Policy
Rules applied when a user sets or changes a local account password. These do not apply to OIDC users, who authenticate against their identity provider.
| Variable | Default | Required | Description |
|---|---|---|---|
PASSWORD_MIN_LENGTH |
8 |
No | Minimum number of characters required in a password. |
PASSWORD_REQUIRE_UPPERCASE |
true |
No | Require at least one uppercase letter. Set to false to disable. |
PASSWORD_REQUIRE_LOWERCASE |
true |
No | Require at least one lowercase letter. Set to false to disable. |
PASSWORD_REQUIRE_NUMBER |
true |
No | Require at least one numeric digit. Set to false to disable. |
PASSWORD_REQUIRE_SPECIAL |
true |
No | Require at least one special character (e.g. !, @, #). Set to false to disable. |
All four complexity options default to
true. To disable a rule you must explicitly set it tofalse: omitting the variable leaves the rule enabled.
8. Logging and Profiling
| Variable | Default | Required | Description |
|---|---|---|---|
ENABLE_LOGGING |
true |
No | Structured application logging to stdout. Setting this to false silences the server completely. Not reduced logging: none at all, which leaves every instruction in this guide that asks you to check the server logs with nothing to show. Changed in 2.0.3; it previously defaulted to false. An explicit false, in either .env or Settings > Environment, is still honoured. |
LOG_LEVEL |
info |
No | Minimum log level to output. Accepted values: debug, info, warn, error. Must be one of these exact strings. The server will fail to start if an invalid value is provided. |
ENABLE_PPROF |
false |
No | When true, serves Go pprof profiling endpoints on a separate loopback-only listener (see PPROF_PORT). For diagnostics only. Do not enable in production unless actively investigating a performance issue. |
PPROF_PORT |
6060 |
No | Port for the profiling listener when ENABLE_PPROF=true. Binds to 127.0.0.1 only, so nothing needs opening in a firewall or reverse proxy. |
MEMSTATS_INTERVAL_SEC |
60 |
No | How often (in seconds) the server logs Go runtime memory statistics when profiling is active. Only relevant when ENABLE_PPROF=true. |
Log level guide:
| Level | When to use |
|---|---|
debug |
Active troubleshooting: very verbose, includes internal operations |
info |
Normal production operation |
warn |
Quieter production operation; only non-critical issues and errors |
error |
Minimal output; critical errors only |
9. OIDC / SSO
OpenID Connect configuration for Single Sign-On. When OIDC_ENABLED=true, the four marked variables below become required and the server will refuse to start without them.
Core Settings
| Variable | Default | Required | Description |
|---|---|---|---|
OIDC_ENABLED |
false |
No | Set to true to activate OIDC authentication. |
OIDC_ISSUER_URL |
(none) | If OIDC enabled | The issuer URL of your identity provider (e.g. https://auth.example.com). The server fetches the OIDC discovery document from this URL. |
OIDC_CLIENT_ID |
(none) | If OIDC enabled | The client ID registered in your identity provider. |
OIDC_CLIENT_SECRET |
(none) | If OIDC enabled | The client secret from your identity provider. |
OIDC_REDIRECT_URI |
(none) | If OIDC enabled | The callback URL registered in your identity provider. Must be: https://your-patchmon-url/api/v1/auth/oidc/callback |
OIDC_SCOPES |
openid email profile groups |
No | Space-separated list of OAuth scopes to request. The groups scope is required for group-to-role mapping to work. |
OIDC_ENFORCE_HTTPS |
true |
No | When true (the default), the server rejects OIDC configurations using a non-HTTPS issuer URL. Set to false only in a local development environment with a non-TLS identity provider. |
User Provisioning
| Variable | Default | Required | Description |
|---|---|---|---|
OIDC_AUTO_CREATE_USERS |
false |
No | When true, a PatchMon account is automatically created the first time an OIDC user logs in. When false, an administrator must create the account first. |
OIDC_DEFAULT_ROLE |
user |
No | Role assigned to automatically created OIDC users when no group mapping matches. Accepted values: superadmin, admin, host_manager, user, readonly. |
OIDC_DISABLE_LOCAL_AUTH |
false |
No | When true, local username/password authentication is disabled. Only OIDC login is accepted. Useful when enforcing SSO organisation-wide. |
Login Page
| Variable | Default | Required | Description |
|---|---|---|---|
OIDC_BUTTON_TEXT |
Login with SSO |
No | Text displayed on the SSO login button on the PatchMon login page. |
OIDC_POST_LOGOUT_URI |
Derived from FRONTEND_URL, then CORS_ORIGIN |
No | URL the user is redirected to after logging out of the identity provider. Defaults to the login page of your PatchMon instance. |
Session
| Variable | Default | Required | Description |
|---|---|---|---|
OIDC_SESSION_TTL |
600 |
No | Lifetime in seconds of the temporary OIDC session state stored during the OAuth flow. Increase this only if users on very slow networks experience session-expired errors mid-login. |
Group-to-Role Mapping
Map groups from your identity provider directly to PatchMon roles. Set OIDC_SYNC_ROLES=true to keep role assignments in sync with group membership on every login.
| Variable | Default | Required | Description |
|---|---|---|---|
OIDC_SYNC_ROLES |
false |
No | When true, the user's PatchMon role is updated on every login to match their current IdP group membership. When false, roles are managed locally in PatchMon and OIDC login does not change them. |
OIDC_ADMIN_GROUP |
(none) | No | Name of the IdP group whose members are granted the admin role. |
OIDC_SUPERADMIN_GROUP |
(none) | No | Name of the IdP group whose members are granted the superadmin role. |
OIDC_HOST_MANAGER_GROUP |
(none) | No | Name of the IdP group whose members are granted the host_manager role. |
OIDC_READONLY_GROUP |
(none) | No | Name of the IdP group whose members are granted the readonly role. |
OIDC_USER_GROUP |
(none) | No | Name of the IdP group whose members are granted the standard user role. |
Example: Authentik
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://authentik.example.com/application/o/patchmon/
OIDC_CLIENT_ID=patchmon
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=https://patchmon.example.com/api/v1/auth/oidc/callback
OIDC_SCOPES=openid email profile groups
OIDC_AUTO_CREATE_USERS=true
OIDC_DEFAULT_ROLE=user
OIDC_BUTTON_TEXT=Login with Authentik
OIDC_SYNC_ROLES=true
OIDC_ADMIN_GROUP=PatchMon Admins
OIDC_USER_GROUP=PatchMon Users
Example: Keycloak
OIDC_ENABLED=true
OIDC_ISSUER_URL=https://keycloak.example.com/realms/your-realm
OIDC_CLIENT_ID=patchmon
OIDC_CLIENT_SECRET=your-client-secret
OIDC_REDIRECT_URI=https://patchmon.example.com/api/v1/auth/oidc/callback
OIDC_SCOPES=openid email profile groups
OIDC_AUTO_CREATE_USERS=true
OIDC_DEFAULT_ROLE=user
OIDC_BUTTON_TEXT=Login with Keycloak
10. Compliance / SSG
Settings for SCAP Security Guide content used by the compliance scanning feature.
| Variable | Default | Required | Description |
|---|---|---|---|
SSG_CONTENT_DIR |
./ssg-content |
No | Path to the directory holding SCAP Security Guide datastream files (ssg-*-ds.xml), which agents download from. The official image already contains this content at /app/ssg-content and sets this variable for you, so leave it alone unless you are deliberately supplying your own content. Do not mount an empty volume at this path. Doing so hides the bundled content and leaves every host in your fleet unable to update its compliance content. |
11. RDP / Remote Access
Configuration for the Guacamole daemon (guacd) that powers in-browser RDP sessions.
| Variable | Default | Required | Description |
|---|---|---|---|
GUACD_PATH |
(none) | No | Absolute path to the guacd binary. When empty, the server locates guacd using the system PATH. Set this if guacd is installed in a non-standard location. |
GUACD_ADDRESS |
127.0.0.1:4822 |
No | Host and port the server uses to connect to the running guacd process. Change this if guacd is running on a different host or non-default port. |
Patching
| Variable | Default | Required | Description |
|---|---|---|---|
PATCH_RUN_STALL_TIMEOUT_MIN |
30 |
No | Minutes a patch run can stay in running state before the periodic cleanup (every 10 minutes) marks it as timed_out. Minimum 5; values below 5 are clamped at startup with a warning. Also editable via Settings → Environment in the web UI; the env var still wins if set. Changes made in the UI take effect on the next cleanup sweep without a restart. |
Reporting
| Variable | Default | Required | Description |
|---|---|---|---|
AGENT_REPORTS_RETENTION_DAYS |
30 |
No | Days to retain Agent Activity rows (every ping, full report, partial report, Docker upload, and compliance scan submission writes one row). The daily cleanup sweep at 02:00 deletes anything older. Range 7..365; values outside the range are clamped at startup with a warning. Also editable via Settings → Environment in the web UI; the env var still wins if set. Changes made in the UI take effect on the next cleanup sweep without a restart. |
12. Body Limits
Maximum sizes for request bodies accepted by the API. Increase these only if you encounter HTTP 413 errors caused by legitimate large payloads.
Accepted suffixes: b, kb, mb, gb. Examples: 10mb, 512kb.
| Variable | Default | Required | Description |
|---|---|---|---|
JSON_BODY_LIMIT |
5mb |
No | Maximum size of JSON request bodies for standard API endpoints (user management, settings, host actions, etc.). |
AGENT_UPDATE_BODY_LIMIT |
5mb |
No | Maximum size of request bodies on agent check-in and package reporting endpoints. Increase this if agents managing a very large number of packages hit the limit. |
13. Timezone
| Variable | Default | Required | Description |
|---|---|---|---|
TZ |
UTC |
No | IANA timezone name used for timestamps in server logs and scheduled operations. If TZ is not set, the server also checks TIMEZONE before falling back to UTC. All timestamps stored in the database remain in UTC regardless of this setting. |
Common values:
TZ=UTC # Recommended for servers
TZ=Europe/London
TZ=Europe/Paris
TZ=America/New_York
TZ=America/Chicago
TZ=America/Los_Angeles
TZ=Asia/Tokyo
14. Encryption Keys
PatchMon encrypts sensitive values at rest: AI provider credentials, bootstrap enrolment tokens, OIDC client secrets, notification destination secrets. The encryption key is resolved from the first non-empty value in this list:
AI_ENCRYPTION_KEYSESSION_SECRET- Derived from
DATABASE_URL(fallback; not recommended for production)
If you rotate this value, every encrypted secret in the database becomes unreadable. Set it once at install time and treat it with the same care as JWT_SECRET.
| Variable | Default | Required | Description |
|---|---|---|---|
AI_ENCRYPTION_KEY |
(none) | Recommended | 32+ byte random secret used to encrypt AI provider keys, bootstrap tokens, OIDC client secrets, and notification destination credentials. Configure via .env only (not editable from the Settings UI). |
SESSION_SECRET |
(none) | No | Fallback encryption key used if AI_ENCRYPTION_KEY is not set. Exists for backward compatibility with early 1.x installs; prefer AI_ENCRYPTION_KEY for new deployments. |
Generating a secure value:
openssl rand -hex 32
If neither
AI_ENCRYPTION_KEYnorSESSION_SECRETis set, the server derives an encryption key fromDATABASE_URL. This works but means your encryption key is only as strong as your database connection string. Any change to the DB host, port, or password rotates the encryption key and invalidates every encrypted value. Always setAI_ENCRYPTION_KEYexplicitly in production.
15. Agent Binary Overrides
Advanced deployments that want to replace the bundled agent binaries (for example, to ship a custom build or host binaries on a different volume) can override the directory the install script serves from. Normally you should leave these unset. The server embeds all supported agent binaries in the image at build time.
| Variable | Default | Required | Description |
|---|---|---|---|
AGENT_BINARIES_DIR |
(none) | No | Absolute path to a directory containing replacement agent binaries (e.g. patchmon-agent-linux-amd64). Takes precedence over AGENTS_DIR when both are set. |
AGENTS_DIR |
(none) | No | Alternative name for AGENT_BINARIES_DIR. Kept for compatibility with installs that set this variable before 2.0. |
When both are empty, the server serves binaries from the static/ path embedded in its binary.
16. Telemetry
PatchMon can send anonymous usage heartbeats (version, host count, rough OS distribution) to the upstream metrics endpoint once a day. This is fully opt-in. See Metrics and telemetry for what is sent and the Settings → Metrics page to toggle it.
| Variable | Default | Required | Description |
|---|---|---|---|
METRICS_API_URL |
(none) | No | Override the upstream metrics endpoint. Leave empty to send to the default PatchMon telemetry service. Set to an internal URL to collect telemetry privately. |
17. File Loading
| Variable | Default | Required | Description |
|---|---|---|---|
ENV_FILE |
.env |
No | Path to the .env file the server reads at startup. If the file does not exist at the given path, startup continues silently and only actual process environment variables are used. |
FRONTEND_URL |
(none) | No | Optional alias used by OIDC when computing OIDC_POST_LOGOUT_URI. If set and OIDC_POST_LOGOUT_URI is not set, the post-logout redirect defaults to <FRONTEND_URL>/login. Otherwise the server falls back to <CORS_ORIGIN>/login. Most deployments can ignore this; set CORS_ORIGIN correctly and it's not needed. |
Complete Minimal Configuration
The smallest valid .env for a production deployment:
# Required
DATABASE_URL="postgresql://patchmon_user:strongpassword@database:5432/patchmon_db"
JWT_SECRET="paste-output-of-openssl-rand-hex-64-here"
# Encryption (strongly recommended in production)
AI_ENCRYPTION_KEY="paste-output-of-openssl-rand-hex-32-here"
# Server
PORT=3000
APP_ENV=production
CORS_ORIGIN=https://patchmon.example.com
ENABLE_HSTS=true
TRUST_PROXY=true
# Redis
REDIS_HOST=redis
REDIS_PORT=6379
REDIS_PASSWORD="paste-output-of-openssl-rand-hex-32-here"
# Logging
ENABLE_LOGGING=true
LOG_LEVEL=info
# Timezone
TZ=UTC
Everything else defaults to a sensible production value and does not need to be set unless you want to change the behaviour described in this document.
The
setup-env.shscript shipped with the Docker compose generates a valid.envwith all three secrets (JWT_SECRET,REDIS_PASSWORD, andPOSTGRES_PASSWORD) pre-populated. See Installing PatchMon Server on Docker. For day-to-day changes to rate limits, logging, password policy, timezone, and similar runtime-tunable values, prefer the Settings UI. See Settings in the web UI.
Troubleshooting
Server fails to start with "DATABASE_URL is required" or "JWT_SECRET is required"
These two variables have no default. Verify they are present in your .env file and that the file is being loaded (check the ENV_FILE variable if you use a custom path).
CORS errors in the browser
CORS_ORIGIN must exactly match the URL in your browser's address bar, including the protocol (http vs https) and port. A common mistake is setting it to https://patchmon.example.com while accessing the site on http://. If you access PatchMon from multiple URLs, list all of them comma-separated with no spaces (e.g. CORS_ORIGIN=https://patchmon.example.com,https://patchmon.internal.lan).
Rate limit errors (HTTP 429)
Increase the relevant *_RATE_LIMIT_MAX value for the endpoint category hitting the limit. For large agent fleets, AGENT_RATE_LIMIT_MAX is the most common one to raise.
Database connection pool exhausted
Increase DB_CONNECTION_LIMIT. Check your PostgreSQL max_connections setting to ensure the total across all PatchMon instances does not exceed it.
OIDC login fails immediately after enabling
Verify that OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, and OIDC_REDIRECT_URI are all set. The server validates these on startup and will not start if any are missing when OIDC_ENABLED=true. Also confirm that OIDC_REDIRECT_URI is registered as an allowed callback URL in your identity provider.
Sessions lost after server restart
Verify JWT_SECRET has not changed. Rotating this value invalidates all existing tokens.
PatchMon 2.0+ (Go server)