PatchMon Environment Variables Reference
This document provides a comprehensive reference for all environment variables available in PatchMon. These variables can be configured in your backend/.env file (bare metal installations) or in yourthe .env file alongside docker-compose.yml file (Docker deployments)deployments using env_file).
Table of Contents
- Database Configuration
- Database Connection Pool
- Database Transaction Timeouts
- Authentication & Security
- Password Policy
- Account Lockout
- Session Management
- Two-Factor Authentication (TFA)
- OIDC / SSO
- Server & Network Configuration
- Rate Limiting
- Redis Configuration
- Logging
- Timezone Configuration
Two-FactorBodyAuthenticationSize(TFA)Limits- Encryption
- User Management
- Frontend Configuration
Database Configuration
PostgreSQL database connection settings.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
DATABASE_URL |
PostgreSQL connection string | - | postgresql://user:pass@localhost:5432/patchmon_db |
|
PM_DB_CONN_MAX_ATTEMPTS |
Maximum database connection attempts during startup | 30 |
No | 30 |
PM_DB_CONN_WAIT_INTERVAL |
Wait interval between connection attempts (seconds) | 2 |
No | 2 |
Usage Notes
- The
DATABASE_URLmust be a valid PostgreSQL connection string - Connection retry logic helps handle database startup delays in containerized environments
- Format:
postgresql://[user]:[password]@[host]:[port]/[database] - In Docker deployments,
DATABASE_URLis constructed automatically in the compose file - you do not set it in.env
Database Connection Pool (Prisma)
Connection pooling configuration for optimal database performance and resource management.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
DB_CONNECTION_LIMIT |
Maximum number of database connections per instance | 30 |
No | 30 |
DB_POOL_TIMEOUT |
Seconds to wait for an available connection before timeout | 20 |
No | 20 |
DB_CONNECT_TIMEOUT |
Seconds to wait for initial database connection | 10 |
No | 10 |
DB_IDLE_TIMEOUT |
Seconds before closing idle connections | 300 |
No | 300 |
DB_MAX_LIFETIME |
Maximum lifetime of a connection in seconds | 1800 |
No | 1800 |
Sizing Guidelines
Small Deployment (1-10 hosts):
DB_CONNECTION_LIMIT=15
DB_POOL_TIMEOUT=20
Medium Deployment (10-50 hosts):
DB_CONNECTION_LIMIT=30 # Default
DB_POOL_TIMEOUT=20
Large Deployment (50+ hosts):
DB_CONNECTION_LIMIT=50
DB_POOL_TIMEOUT=30
Connection Pool Calculation
Use this formula to estimate your needs:
DB_CONNECTION_LIMIT = (expected_hosts * 2) + (concurrent_users * 2) + 5
Example: 20 hosts + 3 concurrent users:
(20 * 2) + (3 * 2) + 5 = 51 connections
Important Notes
- Each backend instance maintains its own connection pool
- Running multiple backend instances requires considering total connections to PostgreSQL
- PostgreSQL default
max_connectionsis 100 (ensure your pool size doesn't exceed this) - Connections are reused efficiently - you don't need one connection per host
- Increase pool size if experiencing timeout errors during high load
Detecting Connection Pool Issues
When connection pool limits are hit, you'll see clear error messages in your backend console:
Typical Pool Timeout Error:
Host creation error: Error: Timed out fetching a new connection from the connection pool.
⚠️ DATABASE CONNECTION POOL EXHAUSTED!
⚠️ Current limit: DB_CONNECTION_LIMIT=30
⚠️ Pool timeout: DB_POOL_TIMEOUT=20s
⚠️
Suggestion: Increase DB_CONNECTION_LIMIT in your .env file
Frontend Error:
POST /api/v1/hosts/create 500 (Internal Server Error)
Response: { "error": "Failed to create host" }
If you see these errors frequently, increase DB_CONNECTION_LIMIT by 10-20 and monitor your system.
Monitoring Connection Pool Usage
You can monitor your PostgreSQL connections to determine optimal pool size:
Check Current Connections:
# Connect to PostgreSQL
psql -U patchmon_user -d patchmon_db
# Run this query
SELECT count(*) as current_connections,
(SELECT setting::int FROM pg_settings WHERE name='max_connections') as max_connections
FROM pg_stat_activity
WHERE datname = 'patchmon_db';
View Connection Details:
SELECT
usename,
application_name,
client_addr,
state,
backend_start,
state_change
FROM pg_stat_activity
WHERE datname = 'patchmon_db'
ORDER BY backend_start DESC;
Recommended Actions:
- If
current_connectionsfrequently approachesDB_CONNECTION_LIMIT, increase the pool size - Monitor during peak usage (when multiple users are active, agents checking in)
- Leave 20-30% headroom for burst traffic
Database Transaction Timeouts
Control how long database transactions can run before being terminated.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
DB_TRANSACTION_MAX_WAIT |
Maximum time (ms) to wait for a transaction to start | 10000 |
No | 10000 |
DB_TRANSACTION_TIMEOUT |
Maximum time (ms) for a standard transaction to complete | 30000 |
No | 30000 |
DB_TRANSACTION_LONG_TIMEOUT |
Maximum time (ms) for long-running transactions (e.g. bulk operations) | 60000 |
No | 60000 |
Usage Notes
- These prevent runaway queries from holding database locks indefinitely
- Increase
DB_TRANSACTION_LONG_TIMEOUTif bulk import or migration operations are timing out - All values are in milliseconds
Authentication & Security
JWT token configuration and security settings.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
JWT_SECRET |
Secret key for signing JWT tokens | - | your-secure-random-secret-key |
|
JWT_EXPIRES_IN |
Access token expiration time | 1h |
No | 1h, 30m, 2h |
JWT_REFRESH_EXPIRES_IN |
Refresh token expiration time | 7d |
No | 7d, 3d, 14d |
Generating Secure Secrets
Linux/macOS:
# Linux/macOS
openssl rand -hex 64
Node.js:
require('crypto').randomBytes(64).toString('hex')
Time Format
Supports the following formats:
s: secondsm: minutesh: hoursd: days
Examples: 30s, 15m, 2h, 7d
Recommended Settings
Development:
JWT_EXPIRES_IN=1h
JWT_REFRESH_EXPIRES_IN=7d
Production:
JWT_EXPIRES_IN=30m
JWT_REFRESH_EXPIRES_IN=3d
High Security:
JWT_EXPIRES_IN=15m
JWT_REFRESH_EXPIRES_IN=1d
Password Policy
Enforce password complexity requirements for local user accounts.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
PASSWORD_MIN_LENGTH |
Minimum password length | 8 |
No | 8, 12, 16 |
PASSWORD_REQUIRE_UPPERCASE |
Require at least one uppercase letter | true |
No | true, false |
PASSWORD_REQUIRE_LOWERCASE |
Require at least one lowercase letter | true |
No | true, false |
PASSWORD_REQUIRE_NUMBER |
Require at least one number | true |
No | true, false |
PASSWORD_REQUIRE_SPECIAL |
Require at least one special character | true |
No | true, false |
PASSWORD_RATE_LIMIT_WINDOW_MS |
Rate limit window for password changes (ms) | 900000 |
No | 900000 (15 min) |
PASSWORD_RATE_LIMIT_MAX |
Maximum password change attempts per window | 5 |
No | 5 |
Recommended Settings
Standard (default):
PASSWORD_MIN_LENGTH=8
PASSWORD_REQUIRE_UPPERCASE=true
PASSWORD_REQUIRE_LOWERCASE=true
PASSWORD_REQUIRE_NUMBER=true
PASSWORD_REQUIRE_SPECIAL=true
High Security:
PASSWORD_MIN_LENGTH=12
PASSWORD_REQUIRE_UPPERCASE=true
PASSWORD_REQUIRE_LOWERCASE=true
PASSWORD_REQUIRE_NUMBER=true
PASSWORD_REQUIRE_SPECIAL=true
PASSWORD_RATE_LIMIT_MAX=3
Usage Notes
- These rules apply to local accounts only - OIDC users authenticate against their identity provider
- Password changes and new account creation both enforce these rules
PASSWORD_RATE_LIMIT_*prevents brute-force password change attempts
Account Lockout
Protect against brute-force login attacks by temporarily locking accounts after repeated failures.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
MAX_LOGIN_ATTEMPTS |
Failed login attempts before account lockout | 5 |
No | 5, 3, 10 |
LOCKOUT_DURATION_MINUTES |
Minutes the account stays locked after exceeding attempts | 15 |
No | 15, 30, 60 |
Usage Notes
- Lockout is per-account, not per-IP
- The failed attempts counter has a 15-minute rolling window - if no further failed attempts occur within that window, the counter resets on its own
- A successful login clears the failed attempts counter (before lockout is triggered)
- Once locked out, the account stays locked for the full
LOCKOUT_DURATION_MINUTES- there is no way to bypass this except waiting - Setting
MAX_LOGIN_ATTEMPTStoo low may lock out legitimate users who mistype passwords
Recommended Settings
Standard:
MAX_LOGIN_ATTEMPTS=5
LOCKOUT_DURATION_MINUTES=15
High Security:
MAX_LOGIN_ATTEMPTS=3
LOCKOUT_DURATION_MINUTES=30
Session Management
Control user session behavior and security.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
SESSION_INACTIVITY_TIMEOUT_MINUTES |
Minutes of inactivity before automatic logout | 30 |
No | 30 |
Usage Notes
- Sessions are tracked in the database with activity timestamps
- Each authenticated request updates the session activity
- Expired sessions are automatically invalidated
- Users must log in again after timeout period
- Lower values provide better security but may impact user experience
Recommended Settings
- High Security Environment:
15minutes - Standard Security:
30minutes (default) - User-Friendly:
60minutes
Two-Factor Authentication (TFA)
Settings for two-factor authentication when users have it enabled.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
MAX_TFA_ATTEMPTS |
Failed TFA code attempts before lockout | 5 |
No | 5, 3 |
TFA_LOCKOUT_DURATION_MINUTES |
Minutes locked out after exceeding TFA attempts | 30 |
No | 30, 60 |
TFA_REMEMBER_ME_EXPIRES_IN |
"Remember this device" token expiration | 30d |
No | 30d, 7d, 90d |
TFA_MAX_REMEMBER_SESSIONS |
Maximum remembered devices per user | 5 |
No | 5 |
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD |
Failed attempts before flagging suspicious activity | 3 |
No | 3 |
Usage Notes
- These variables only apply when users have TFA enabled on their account
- "Remember this device" allows users to skip TFA on trusted devices
MAX_TFA_ATTEMPTSandTFA_LOCKOUT_DURATION_MINUTESprevent brute-force attacks on TOTP codes- Suspicious activity detection can trigger additional security measures
- Remembered sessions can be revoked by users or admins
Recommended Settings
Standard:
MAX_TFA_ATTEMPTS=5
TFA_LOCKOUT_DURATION_MINUTES=30
TFA_REMEMBER_ME_EXPIRES_IN=30d
TFA_MAX_REMEMBER_SESSIONS=5
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3
High Security:
MAX_TFA_ATTEMPTS=3
TFA_LOCKOUT_DURATION_MINUTES=60
TFA_REMEMBER_ME_EXPIRES_IN=7d
TFA_MAX_REMEMBER_SESSIONS=3
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=2
OIDC / SSO
OpenID Connect configuration for Single Sign-On. Set OIDC_ENABLED=true and fill in your identity provider details to enable SSO.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
OIDC_ENABLED |
Enable OIDC authentication | false |
No | true, false |
OIDC_ISSUER_URL |
Identity provider issuer URL | - | If OIDC enabled | https://auth.example.com |
OIDC_CLIENT_ID |
OAuth client ID | - | If OIDC enabled | patchmon |
OIDC_CLIENT_SECRET |
OAuth client secret | - | If OIDC enabled | your-client-secret |
OIDC_REDIRECT_URI |
Callback URL after authentication | - | If OIDC enabled | https://patchmon.example.com/api/v1/auth/oidc/callback |
OIDC_SCOPES |
OAuth scopes to request | openid email profile groups |
No | openid email profile groups |
OIDC_AUTO_CREATE_USERS |
Automatically create PatchMon accounts for new OIDC users | true |
No | true, false |
OIDC_DEFAULT_ROLE |
Default role for auto-created OIDC users | user |
No | user, admin, viewer |
OIDC_DISABLE_LOCAL_AUTH |
Disable local username/password login when OIDC is enabled | false |
No | true, false |
OIDC_BUTTON_TEXT |
Login button text shown on the login page | Login with SSO |
No | Login with SSO, Sign in with Authentik |
Group-to-Role Mapping
Map OIDC groups from your identity provider to PatchMon roles. This keeps role assignments in sync with your IdP.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
OIDC_ADMIN_GROUP |
OIDC group name that maps to admin role | - | No | PatchMon Admins |
OIDC_USER_GROUP |
OIDC group name that maps to user role | - | No | PatchMon Users |
OIDC_SYNC_ROLES |
Sync roles from OIDC groups on each login | true |
No | true, false |
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-here
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_ADMIN_GROUP=PatchMon Admins
OIDC_USER_GROUP=PatchMon Users
OIDC_SYNC_ROLES=true
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-here
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
Usage Notes
OIDC_REDIRECT_URImust be registered as an allowed redirect URI in your identity provider- When
OIDC_DISABLE_LOCAL_AUTH=true, users can only log in via OIDC - useful for enforcing SSO across the organisation - When
OIDC_SYNC_ROLES=true, the user's role is updated on every login based on their OIDC group membership - If a user is in both
OIDC_ADMIN_GROUPandOIDC_USER_GROUP, the admin role takes precedence - The
groupsscope must be supported by your identity provider and included inOIDC_SCOPESfor group mapping to work
Server & Network Configuration
Server protocol, host, and CORS settings.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
PORT |
Backend API server port | 3001 |
No | 3001 |
NODE_ENV |
Node.js environment mode | production |
No | production, development |
SERVER_PROTOCOL |
http |
No | http, https |
|
SERVER_HOST |
localhost |
No | patchmon.example.com |
|
SERVER_PORT |
3000 |
No | 3000, 443 |
|
CORS_ORIGIN |
Allowed CORS origin URL | http://localhost:3000 |
No | https://patchmon.example.com |
CORS_ORIGINS |
Multiple allowed CORS origins (comma-separated) | - | No | https://a.example.com,https://b.example.com |
ENABLE_HSTS |
Enable HTTP Strict Transport Security | true |
No | true, false |
TRUST_PROXY |
Trust proxy headers (when behind reverse proxy) | true |
No | true, false |
Usage Notes
SERVER_PROTOCOL,SERVER_HOST, andSERVER_PORTare used to generate agent installation scriptsCORS_ORIGINmust match the URL you use to accessthePatchMonfrontendin your browserCORS_ORIGINS(plural, comma-separated) overridesCORS_ORIGINwhen set - only needed if PatchMon is accessed from multiple domains- Set
TRUST_PROXYtotruewhen behind nginx, Apache, or other reverse proxies ENABLE_HSTSshould betruein production with HTTPS
Example Configurations
Local Development:
SERVER_PROTOCOL=http
SERVER_HOST=localhost
SERVER_PORT=3000
CORS_ORIGIN=http://localhost:3000
ENABLE_HSTS=false
TRUST_PROXY=false
Production with HTTPS:
SERVER_PROTOCOL=https
SERVER_HOST=patchmon.example.com
SERVER_PORT=443
CORS_ORIGIN=https://patchmon.example.com
ENABLE_HSTS=true
TRUST_PROXY=true
ProductionMultiple with Custom Port:Domains:
SERVER_PROTOCOL=https
SERVER_HOST=patchmon.example.com
SERVER_PORT=8443443
CORS_ORIGIN=CORS_ORIGINS=https://patchmon.example.com:8443com,https://patchmon-alt.example.com
ENABLE_HSTS=true
TRUST_PROXY=true
Rate Limiting
Protect your API from abuse with configurable rate limits.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
RATE_LIMIT_WINDOW_MS |
General rate limit window (milliseconds) | 900000 |
No | 900000 (15 min) |
RATE_LIMIT_MAX |
Maximum requests per window (general) | 5000 |
No | 5000 |
AUTH_RATE_LIMIT_WINDOW_MS |
Authentication endpoints rate limit window (ms) | 600000 |
No | 600000 (10 min) |
AUTH_RATE_LIMIT_MAX |
Maximum auth requests per window | 500 |
No | 500 |
AGENT_RATE_LIMIT_WINDOW_MS |
Agent API rate limit window (ms) | 60000 |
No | 60000 (1 min) |
AGENT_RATE_LIMIT_MAX |
Maximum agent requests per window | 1000 |
No | 1000 |
Understanding Rate Limits
Rate limits are applied per IP address and endpoint category:
- General API: Dashboard, hosts, packages, user management
- Authentication: Login, logout, token refresh
- Agent API: Agent check-ins, updates, package reports
Calculating Windows
The window is a sliding time frame. Examples:
900000ms = 15 minutes600000ms = 10 minutes60000ms = 1 minute
Recommended Settings
Default (Balanced):
RATE_LIMIT_WINDOW_MS=900000 # 15 minutes
RATE_LIMIT_MAX=5000 # ~5.5 requests/second
AUTH_RATE_LIMIT_WINDOW_MS=600000 # 10 minutes
AUTH_RATE_LIMIT_MAX=500 # ~0.8 requests/second
AGENT_RATE_LIMIT_WINDOW_MS=60000 # 1 minute
AGENT_RATE_LIMIT_MAX=1000 # ~16 requests/second
Strict (High Security):
RATE_LIMIT_WINDOW_MS=900000 # 15 minutes
RATE_LIMIT_MAX=2000 # ~2.2 requests/second
AUTH_RATE_LIMIT_WINDOW_MS=600000 # 10 minutes
AUTH_RATE_LIMIT_MAX=100 # ~0.16 requests/second
AGENT_RATE_LIMIT_WINDOW_MS=60000 # 1 minute
AGENT_RATE_LIMIT_MAX=500 # ~8 requests/second
Relaxed (Development/Testing):
RATE_LIMIT_WINDOW_MS=900000 # 15 minutes
RATE_LIMIT_MAX=10000 # ~11 requests/second
AUTH_RATE_LIMIT_WINDOW_MS=600000 # 10 minutes
AUTH_RATE_LIMIT_MAX=1000 # ~1.6 requests/second
AGENT_RATE_LIMIT_WINDOW_MS=60000 # 1 minute
AGENT_RATE_LIMIT_MAX=2000 # ~33 requests/second
Redis Configuration
Redis is used for BullMQ job queues and caching.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
REDIS_HOST |
Redis server hostname | localhost |
No | localhost, redis, 10.0.0.5 |
REDIS_PORT |
Redis server port | 6379 |
No | 6379 |
REDIS_USER |
Redis username (Redis 6+) | - | No | default |
REDIS_PASSWORD |
Redis authentication password | - | your-redis-password |
|
REDIS_DB |
Redis database number | 0 |
No | 0, 1, 2 |
Usage Notes
- Redis authentication is highly recommended for security
- Redis 6.0+ supports ACL with usernames; earlier versions use password-only auth
- If no password is set, Redis will be accessible without authentication (not recommended)
- Database number allows multiple applications to use the same Redis instance
Docker Deployment
In Docker, set REDIS_PASSWORD in your .env file. The compose file automatically passes it to both the Redis passwordcontainer must(via beits setstartup incommand) twoand places:
Redisbackend servicecommand:(viaenv_file).command:Bare
redis-serverMetal--requirepass your-redis-password-here- Deployment
BackendTheenvironment:setup script configures Redis ACL with a dedicated user and password per instance. The credentials are written tobackend/.envautomatically.REDIS_PASSWORD: your-redis-password-here
Generating Secure Passwords
openssl rand -hex 32
Logging
Control application logging behavior and verbosity.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
LOG_LEVEL |
Logging level | info |
No | debug, info, warn, error |
ENABLE_LOGGING |
Enable/disable application logging | true |
No | true, false |
PM_LOG_TO_CONSOLE |
Output logs to the console | false |
No | true, false |
PM_LOG_REQUESTS_IN_DEV |
Log HTTP requests in development mode | false |
No | true, false |
PRISMA_LOG_QUERIES |
Log all Prisma database queries | false |
No | true, false |
Log Levels
Ordered from most to least verbose:
debug: All logs including database queries, internal operationsinfo: General information, startup messages, normal operationswarn: Warning messages, deprecated features, non-critical issueserror: Error messages only, critical issues
Recommended Settings
Development:
LOG_LEVEL=debug
ENABLE_LOGGING=true
PM_LOG_TO_CONSOLE=true
PM_LOG_REQUESTS_IN_DEV=true
PRISMA_LOG_QUERIES=true
Production:
LOG_LEVEL=info
ENABLE_LOGGING=true
PM_LOG_TO_CONSOLE=false
PRISMA_LOG_QUERIES=false
Production (Quiet):
LOG_LEVEL=warn
ENABLE_LOGGING=true
PRISMA_LOG_QUERIES=false
Timezone Configuration
Control timezone handling for timestamps and logs across the application.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
TZ |
Timezone for timestamps and logs | UTC |
No | UTC, America/New_York, Europe/London |
| | | |
Usage Notes
- The
TZenvironment variable controls timezone handling across all components:- Backend (Node.js): Timestamps in API responses, database records, logs
- Agent (Go): Agent logs, integration data timestamps
Platform APIs (Python): Deployment timestamps, metrics timestamps
- If
neitherTZnorisTIMEZONEisnot set, the application defaults to UTC - Database timestamps are always stored in UTC for consistency
- Display timestamps can be converted to the configured timezone
using utility functions This ensures consistent timezone handling and resolves discrepancies between server local time and UTC
Common Timezone Values
# UTC (Recommendedrecommended for servers):
TZ=UTC
# UK
TZ=Europe/London
# US
Timezones:
TZ=America/New_York # Eastern Time
TZ=America/Chicago # Central
Time
TZ=America/Denver # Mountain Time
TZ=America/Los_Angeles # Pacific
Time
European Timezones:
TZ=Europe/London # GMT/BSTEurope
TZ=Europe/Paris # CET/CEST
TZ=Europe/Berlin
# CET/CESTAsia
Asian Timezones:
TZ=Asia/Tokyo # JST
TZ=Asia/Shanghai
# CST
TZ=Asia/Dubai # GST
Recommended
Settings
Body Size Limits
ProductionControl (UTCthe -maximum Recommended):size of request bodies accepted by the API.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
|
Maximum JSON request body size | 5mb |
No | 5mb, 10mb, 1mb |
AGENT_UPDATE_BODY_LIMIT |
Maximum body size for agent update payloads | 2mb |
No | 2mb, 5mb |
Usage Notes
EnsuresJSON_BODY_LIMITconsistencyappliesacrosstodistributedallsystemsstandard API endpoints (dashboard actions, user management, etc.)AvoidsAGENT_UPDATE_BODY_LIMITdaylightappliessavingspecificallytimetoissuesagent check-in and package report payloadsStandardIncreasepracticetheseforifserveragentsapplicationsare managing a very large number of packages and the payload exceeds the limit
Developmentthese (Localas Timezone):
TZ=America/New_Yorkas Easierpractical toreadlimitlogsmemory usage andtimestamps during developmentMatches developer's local timezone
Troubleshooting
Timestamps showing incorrect time:
VerifyTZis set correctly in your.envfileRestartreduce theapplicationimpactafterofchangingoversizedTZCheck that the timezone string is valid (use IANA timezone database names)
Agent logs showing different timezone than server:
EnsureTZis set in the agent's environmentFor systemd services, setTZin the service file or system environmentAgent and server should use the same timezone for consistency
Database timestamps vs displayed timestamps:
Database always stores UTC timestampsFrontend/API can convert to configured timezone for displayThis is expected behavior for data consistencyrequests
Two-Factor Authentication (TFA)Encryption
OptionalControls two-factorencryption authenticationof settingssensitive data stored in the database (whene.g. TFAAI isprovider enabled)API keys, bootstrap tokens).
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
|
- |
No | |
|
|
AI_ENCRYPTION_KEY is not set |
- |
No | Output of | openssl
| | |
How It Works
The backend uses this priority chain to determine the encryption key:
AI_ENCRYPTION_KEY- used directly if set (64 hex chars = 32 bytes, or any string which gets SHA-256 hashed)SESSION_SECRET- ifAI_ENCRYPTION_KEYis not set, SHA-256 hashed to derive the keyDATABASE_URL- if neither above is set, derives a key from the database URL (logs a security warning)- Ephemeral - last resort, generates a random key (data encrypted with this key will be unreadable after a restart)
What Gets Encrypted
- AI API keys - API keys for AI providers (e.g. OpenAI) are AES-256-GCM encrypted before being stored in the database
- Bootstrap tokens - Agent auto-enrollment API keys are encrypted before temporary storage in Redis
Usage Notes
TheseForvariablesmostonlydeployments,applyyouwhendousersnothaveneedTFAtoenabledset either variable - the key is derived fromDATABASE_URLwhich is stable"RememberSetthisAI_ENCRYPTION_KEYdevice"ifallowsyouusersneedtoencryptionskipstabilityTFAacrossondatabasetrustedURLdeviceschanges or multi-replica deploymentsSuspiciousTheseactivitydodetectionnothelpsaffectpreventuserbrutepasswordforcestorageattacks(passwords Rememberedaresessionsbcryptcanhashed,benotrevoked by users or adminsencrypted)
Time Format
Same as JWT expiration: s (seconds), m (minutes), h (hours), d (days)
Recommended Settings
Standard:
TFA_REMEMBER_ME_EXPIRES_IN=30d
TFA_MAX_REMEMBER_SESSIONS=5
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3
High Security:
TFA_REMEMBER_ME_EXPIRES_IN=7d
TFA_MAX_REMEMBER_SESSIONS=3
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=2
User Management
Default settings for new users.
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
DEFAULT_USER_ROLE |
Default role assigned to new users | user |
No | user, admin, viewer |
Available Roles
admin: Full system access, can manage users and settingsuser: Standard access, can manage hosts and packagesviewer: Read-only access, cannot make changes
Usage Notes
- Only applies to newly created users
- Existing users are not affected by changes to this variable
- First user created through setup is always an admin
- Can be changed per-user through the user management interface
Frontend Configuration
Frontend-specific environment variables (used during build and runtime).
| Variable | Description | Default | Required | Example |
|---|---|---|---|---|
VITE_API_URL |
Backend API base URL | /api/v1 |
No | http://localhost:3001/api/v1 |
VITE_APP_NAME |
Application name displayed in UI | PatchMon |
No | PatchMon |
VITE_APP_VERSION |
Application version displayed in UI | (from package.json) | No | 1. |
BACKEND_HOST |
Backend hostname (Docker only) | backend |
No | backend, localhost |
BACKEND_PORT |
Backend port (Docker only) | 3001 |
No | 3001 |
VITE_ENABLE_LOGGING |
Enable frontend debug logging | false |
No | true, false |
Usage Notes
- Frontend variables are prefixed with
VITE_for the Vite build system TheseVITE_*variables are embeddedduringat build time(except-fortheyDockercannot be changed at runtimevariables)VITE_API_URLcan be relative (/api/v1) or absoluteDocker-specific variables (BACKEND_HOST,andBACKEND_PORT)are used by the Docker frontendservercontainer's Nginx proxy config
Complete Example Configuration
Bare Metal (Production)
# Database Configuration
DATABASE_URL="postgresql://patchmon_user:secure_db_password@localhost:5432/patchmon_db"
PM_DB_CONN_MAX_ATTEMPTS=30
PM_DB_CONN_WAIT_INTERVAL=2
# Database Connection Pool
DB_CONNECTION_LIMIT=30
DB_POOL_TIMEOUT=20
DB_CONNECT_TIMEOUT=10
DB_IDLE_TIMEOUT=300
DB_MAX_LIFETIME=1800
# JWT
Configuration
JWT_SECRET="generated-secure-secret-from-openssl"
JWT_EXPIRES_IN=30m
JWT_REFRESH_EXPIRES_IN=3d
# Server Configuration
PORT=3001
NODE_ENV=production
SERVER_PROTOCOL=https
SERVER_HOST=patchmon.example.com
SERVER_PORT=443
CORS_ORIGIN=https://patchmon.example.com
ENABLE_HSTS=true
TRUST_PROXY=true
# Session
Configuration
SESSION_INACTIVITY_TIMEOUT_MINUTES=30
# User Configuration
DEFAULT_USER_ROLE=user
# Rate Limiting
RATE_LIMIT_WINDOW_MS=900000
RATE_LIMIT_MAX=5000
AUTH_RATE_LIMIT_WINDOW_MS=600000
AUTH_RATE_LIMIT_MAX=500
AGENT_RATE_LIMIT_WINDOW_MS=60000
AGENT_RATE_LIMIT_MAX=1000
# Redis
Configuration
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_PASSWORD=secure_redis_password
REDIS_DB=0
# Logging
LOG_LEVEL=info
ENABLE_LOGGING=true
# Timezone
Configuration
TZ=UTC
# TFA Configuration (Optional)
TFA_REMEMBER_ME_EXPIRES_IN=30d
TFA_MAX_REMEMBER_SESSIONS=5
TFA_SUSPICIOUS_ACTIVITY_THRESHOLD=3
Docker Compose (Production)
SeeFor Docker deployments, see docker/env.example for a complete docker-compose.ymlDockertemplate. deploymentCopy exampleit withto .env, fill in the required values, and run docker compose up -d. The compose file reads all variables.variables via env_file: .env.
Troubleshooting
Common Issues
"timeout of 10000ms exceeded" when adding hosts:
- Increase
DB_CONNECTION_LIMIT(try 30 or higher) - Increase
DB_POOL_TIMEOUT(try 20 or 30) - Check backend logs for "
⚠️DATABASE CONNECTION POOLEXHAUSTED!"EXHAUSTED" messages Monitor PostgreSQL connection count:SELECT count(*) FROM pg_stat_activity WHERE datname = 'patchmon_db';
Database connection failures on startup:
- Increase
PM_DB_CONN_MAX_ATTEMPTS - Increase
PM_DB_CONN_WAIT_INTERVAL - Verify
DATABASE_URLis correct
"Invalid or expired session" errors:
- Check
JWT_SECREThasn't changed between restarts - Verify
SESSION_INACTIVITY_TIMEOUT_MINUTESisn't too low - Ensure
JWT_EXPIRES_INis reasonable
Rate limit errors:errors (429 Too Many Requests):
- Increase
RATE_LIMIT_MAXvalues - Increase window duration (
*_WINDOW_MSvariables)
CORS errors:
- Verify
CORS_ORIGINmatches your frontend URL exactly (protocol + domain + port) IncludeForprotocol,multipledomain,domains,anduseportCORS_ORIGINS(plural, comma-separated)
OIDC login fails:
- Verify
OIDC_REDIRECT_URIis registered in your identity provider - Check
OIDC_ISSUER_URLis reachable from the PatchMon server - Ensure
OIDC_CLIENT_SECRETmatches the value in your IdP
Encrypted data unreadable after restart:
- Set
AI_ENCRYPTION_KEYto a stable value so the key persists across restarts - Re-enter AI provider API keys if
non-standard)the encryption key has changed
Security Best Practices
-
Always set strong
passwords:secrets:- Use
openssl rand -hex 64forJWT secretsJWT_SECRET - Use
openssl rand -hex 32fordatabase/database and Redis passwords
- Use
-
Enable HTTPS in production:
- Set
SERVER_PROTOCOL=https - Enable
ENABLE_HSTS=true - Use proper SSL certificates
- Set
-
Configure appropriate rate limits:
- Adjust based on expected traffic
Monitor for rate limit violations- Lower limits for public-facing deployments
-
Use session timeouts:
- Don't set
SESSION_INACTIVITY_TIMEOUT_MINUTEStoo high - Balance security with user experience
- Don't set
-
Secure Redis:
- Always set
REDIS_PASSWORD - Use Redis ACLs in Redis 6+ for additional security
- Don't expose Redis port publicly
- Always set
-
ConnectionEnablepoolaccountsizing:lockout:MonitorKeepdatabaseMAX_LOGIN_ATTEMPTSconnectionsandunderLOCKOUT_DURATION_MINUTESloadat defaults or stricter
AdjustEnforce password policy:
- Keep all
DB_CONNECTION_LIMITPASSWORD_REQUIRE_*basedoptionson actual usageenabled EnsureConsidertotalincreasingconnectionsPASSWORD_MIN_LENGTHdon'ttoexceed PostgreSQL limits12+
- Keep all
Version Information
Document Version:1.0- Last Updated:
OctoberFebruary20252026 - Applicable to PatchMon: v1.
3.1+4.0+
For updates and additional documentation, see: https://github.com/PatchMon/PatchMon