Skip to main content

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

PostgreSQL database connection settings.

Variable Description Default Required Example
DATABASE_URL PostgreSQL connection string - Yes 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_URL must 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_URL is 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_connections is 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';
SELECT 
    usename,
    application_name,
    client_addr,
    state,
    backend_start,
    state_change
FROM pg_stat_activity 
WHERE datname = 'patchmon_db'
ORDER BY backend_start DESC;
  • If current_connections frequently approaches DB_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.

VariableDescriptionDefaultRequiredExample
DB_TRANSACTION_MAX_WAITMaximum time (ms) to wait for a transaction to start10000No10000
DB_TRANSACTION_TIMEOUTMaximum time (ms) for a standard transaction to complete30000No30000
DB_TRANSACTION_LONG_TIMEOUTMaximum time (ms) for long-running transactions (e.g. bulk operations)60000No60000

Usage Notes

  • These prevent runaway queries from holding database locks indefinitely
  • Increase DB_TRANSACTION_LONG_TIMEOUT if 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 - Yes 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: seconds
  • m: minutes
  • h: hours
  • d: days

Examples: 30s, 15m, 2h, 7d

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.

VariableDescriptionDefaultRequiredExample
PASSWORD_MIN_LENGTHMinimum password length8No8, 12, 16
PASSWORD_REQUIRE_UPPERCASERequire at least one uppercase lettertrueNotrue, false
PASSWORD_REQUIRE_LOWERCASERequire at least one lowercase lettertrueNotrue, false
PASSWORD_REQUIRE_NUMBERRequire at least one numbertrueNotrue, false
PASSWORD_REQUIRE_SPECIALRequire at least one special charactertrueNotrue, false
PASSWORD_RATE_LIMIT_WINDOW_MSRate limit window for password changes (ms)900000No900000 (15 min)
PASSWORD_RATE_LIMIT_MAXMaximum password change attempts per window5No5

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.

VariableDescriptionDefaultRequiredExample
MAX_LOGIN_ATTEMPTSFailed login attempts before account lockout5No5, 3, 10
LOCKOUT_DURATION_MINUTESMinutes the account stays locked after exceeding attempts15No15, 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_ATTEMPTS too low may lock out legitimate users who mistype passwords

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
  • High Security Environment: 15 minutes
  • Standard Security: 30 minutes (default)
  • User-Friendly: 60 minutes

Two-Factor Authentication (TFA)

Settings for two-factor authentication when users have it enabled.

VariableDescriptionDefaultRequiredExample
MAX_TFA_ATTEMPTSFailed TFA code attempts before lockout5No5, 3
TFA_LOCKOUT_DURATION_MINUTESMinutes locked out after exceeding TFA attempts30No30, 60
TFA_REMEMBER_ME_EXPIRES_IN"Remember this device" token expiration30dNo30d, 7d, 90d
TFA_MAX_REMEMBER_SESSIONSMaximum remembered devices per user5No5
TFA_SUSPICIOUS_ACTIVITY_THRESHOLDFailed attempts before flagging suspicious activity3No3

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_ATTEMPTS and TFA_LOCKOUT_DURATION_MINUTES prevent brute-force attacks on TOTP codes
  • Suspicious activity detection can trigger additional security measures
  • Remembered sessions can be revoked by users or admins

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.

VariableDescriptionDefaultRequiredExample
OIDC_ENABLEDEnable OIDC authenticationfalseNotrue, false
OIDC_ISSUER_URLIdentity provider issuer URL-If OIDC enabledhttps://auth.example.com
OIDC_CLIENT_IDOAuth client ID-If OIDC enabledpatchmon
OIDC_CLIENT_SECRETOAuth client secret-If OIDC enabledyour-client-secret
OIDC_REDIRECT_URICallback URL after authentication-If OIDC enabledhttps://patchmon.example.com/api/v1/auth/oidc/callback
OIDC_SCOPESOAuth scopes to requestopenid email profile groupsNoopenid email profile groups
OIDC_AUTO_CREATE_USERSAutomatically create PatchMon accounts for new OIDC userstrueNotrue, false
OIDC_DEFAULT_ROLEDefault role for auto-created OIDC usersuserNouser, admin, viewer
OIDC_DISABLE_LOCAL_AUTHDisable local username/password login when OIDC is enabledfalseNotrue, false
OIDC_BUTTON_TEXTLogin button text shown on the login pageLogin with SSONoLogin 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.

VariableDescriptionDefaultRequiredExample
OIDC_ADMIN_GROUPOIDC group name that maps to admin role-NoPatchMon Admins
OIDC_USER_GROUPOIDC group name that maps to user role-NoPatchMon Users
OIDC_SYNC_ROLESSync roles from OIDC groups on each logintrueNotrue, 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_URI must 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_GROUP and OIDC_USER_GROUP, the admin role takes precedence
  • The groups scope must be supported by your identity provider and included in OIDC_SCOPES for 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 Frontend serverServer protocol http No http, https
SERVER_HOST Frontend serverServer hostname/domain localhost No patchmon.example.com
SERVER_PORT Frontend serverServer port 3000 No 3000, 443
CORS_ORIGIN Allowed CORS origin URL http://localhost:3000 No https://patchmon.example.com
CORS_ORIGINSMultiple allowed CORS origins (comma-separated)-Nohttps://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, 1, loopback

Usage Notes

  • SERVER_PROTOCOL, SERVER_HOST, and SERVER_PORT are used to generate agent installation scripts
  • CORS_ORIGIN must match the URL you use to access thePatchMon frontendin your browser
  • CORS_ORIGINS (plural, comma-separated) overrides CORS_ORIGIN when set - only needed if PatchMon is accessed from multiple domains
  • Set TRUST_PROXY to true when behind nginx, Apache, or other reverse proxies
  • ENABLE_HSTS should be true in 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:

  • 900000 ms = 15 minutes
  • 600000 ms = 10 minutes
  • 60000 ms = 1 minute

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 - ⚠️ Recommended 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:

the
  1. Redisbackend service command:(via env_file).

    command:

    Bare redis-serverMetal --requirepass your-redis-password-here

  2. Deployment

    BackendThe environment:setup script configures Redis ACL with a dedicated user and password per instance. The credentials are written to backend/.env automatically.

    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 trueNotrue, false
PM_LOG_TO_CONSOLEOutput logs to the consolefalseNotrue, false
PM_LOG_REQUESTS_IN_DEVLog HTTP requests in development modefalse No true, false
PRISMA_LOG_QUERIES Log all Prisma database queries false No true, false

Log Levels

Ordered from most to least verbose:

  1. debug: All logs including database queries, internal operations
  2. info: General information, startup messages, normal operations
  3. warn: Warning messages, deprecated features, non-critical issues
  4. error: Error messages only, critical issues

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, Asia/Tokyo
TIMEZONEAlternative timezone variable (same as TZ)UTCNoUTC, America/New_York, Europe/London

Usage Notes

  • The TZ environment 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 neither TZ noris TIMEZONE isnot 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

Body Size Limits

ProductionControl (UTCthe -maximum Recommended):size of request bodies accepted by the API.


























VariableDescriptionDefaultRequiredExample
TZ=UTCJSON_BODY_LIMIT Maximum JSON request body size5mbNo5mb, 10mb, 1mb
AGENT_UPDATE_BODY_LIMITMaximum body size for agent update payloads2mbNo2mb, 5mb

Usage Notes

  • EnsuresJSON_BODY_LIMIT consistencyapplies acrossto distributedall systemsstandard API endpoints (dashboard actions, user management, etc.)
  • AvoidsAGENT_UPDATE_BODY_LIMIT daylightapplies savingspecifically timeto issuesagent check-in and package report payloads
  • StandardIncrease practicethese forif serveragents applicationsare managing a very large number of packages and the payload exceeds the limit
  • Keep

    Developmentthese (Localas Timezone):

    low
    TZ=America/New_Yorkas 
    • Easierpractical to readlimit logsmemory usage and timestamps during development
    • Matches developer's local timezone

    Troubleshooting

    Timestamps showing incorrect time:

    • Verify TZ is set correctly in your .env file
    • Restartreduce the applicationimpact afterof changingoversized TZ
    • Check that the timezone string is valid (use IANA timezone database names)

    Agent logs showing different timezone than server:

    • Ensure TZ is set in the agent's environment
    • For systemd services, set TZ in the service file or system environment
    • Agent and server should use the same timezone for consistency

    Database timestamps vs displayed timestamps:

    • Database always stores UTC timestamps
    • Frontend/API can convert to configured timezone for display
    • This 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).

    opensslrand-hex
    Variable Description Default Required Example
    TFA_REMEMBER_ME_EXPIRES_INAI_ENCRYPTION_KEY "RememberEncryption thiskey device"for tokensensitive expirationdata at rest (64 hex characters) 30d- No 30d,Output of 7d,openssl 90drand -hex 32
    TFA_MAX_REMEMBER_SESSIONSSESSION_SECRET MaximumFallback rememberedkey devicesused perif userAI_ENCRYPTION_KEY is not set 5- No Output of 5
    TFA_SUSPICIOUS_ACTIVITY_THRESHOLDFailed attempts before flagging suspicious3No332

    How It Works

    The backend uses this priority chain to determine the encryption key:

    1. AI_ENCRYPTION_KEY - used directly if set (64 hex chars = 32 bytes, or any string which gets SHA-256 hashed)
    2. SESSION_SECRET - if AI_ENCRYPTION_KEY is not set, SHA-256 hashed to derive the key
    3. DATABASE_URL - if neither above is set, derives a key from the database URL (logs a security warning)
    4. 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

    • TheseFor variablesmost onlydeployments, applyyou whendo usersnot haveneed TFAto enabledset either variable - the key is derived from DATABASE_URL which is stable
    • "RememberSet thisAI_ENCRYPTION_KEY device"if allowsyou usersneed toencryption skipstability TFAacross ondatabase trustedURL deviceschanges or multi-replica deployments
    • SuspiciousThese activitydo detectionnot helpsaffect preventuser brutepassword forcestorage attacks
    • (passwords
    • Rememberedare sessionsbcrypt canhashed, benot revoked by users or adminsencrypted)

    Time Format

    Same as JWT expiration: s (seconds), m (minutes), h (hours), d (days)

    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 settings
    • user: Standard access, can manage hosts and packages
    • viewer: 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.3.14.0
    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 embedded duringat build time (except- forthey Dockercannot be changed at runtime variables)
    • VITE_API_URL can be relative (/api/v1) or absolute
    • Docker-specific variables (BACKEND_HOST, and BACKEND_PORT) are used by the Docker frontend servercontainer'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/docker-compose.ymlenv.example for a complete Dockertemplate. 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 POOL EXHAUSTED!"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_URL is correct

    "Invalid or expired session" errors:

    • Check JWT_SECRET hasn't changed between restarts
    • Verify SESSION_INACTIVITY_TIMEOUT_MINUTES isn't too low
    • Ensure JWT_EXPIRES_IN is reasonable

    Rate limit errors:errors (429 Too Many Requests):

    • Increase RATE_LIMIT_MAX values
    • Increase window duration (*_WINDOW_MS variables)

    CORS errors:

    • Verify CORS_ORIGIN matches your frontend URL exactly (protocol + domain + port)
    • IncludeFor protocol,multiple domain,domains, anduse portCORS_ORIGINS (plural, comma-separated)

    OIDC login fails:

    • Verify OIDC_REDIRECT_URI is registered in your identity provider
    • Check OIDC_ISSUER_URL is reachable from the PatchMon server
    • Ensure OIDC_CLIENT_SECRET matches the value in your IdP

    Encrypted data unreadable after restart:

    • Set AI_ENCRYPTION_KEY to 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

    1. Always set strong passwords:secrets:

      • Use openssl rand -hex 64 for JWT secretsJWT_SECRET
      • Use openssl rand -hex 32 for database/database and Redis passwords
    2. Enable HTTPS in production:

      • Set SERVER_PROTOCOL=https
      • Enable ENABLE_HSTS=true
      • Use proper SSL certificates
    3. Configure appropriate rate limits:

      • Adjust based on expected traffic
      • Monitor for rate limit violations
      • Lower limits for public-facing deployments
    4. Use session timeouts:

      • Don't set SESSION_INACTIVITY_TIMEOUT_MINUTES too high
      • Balance security with user experience
    5. Secure Redis:

      • Always set REDIS_PASSWORD
      • Use Redis ACLs in Redis 6+ for additional security
      • Don't expose Redis port publicly
    6. ConnectionEnable poolaccount sizing:lockout:

      • MonitorKeep databaseMAX_LOGIN_ATTEMPTS connectionsand underLOCKOUT_DURATION_MINUTES loadat defaults or stricter
    7. Adjust

      Enforce password policy:

      • Keep all DB_CONNECTION_LIMITPASSWORD_REQUIRE_* basedoptions on actual usageenabled
      • EnsureConsider totalincreasing connectionsPASSWORD_MIN_LENGTH don'tto exceed PostgreSQL limits12+

    Version Information

    • Document Version: 1.0
    • Last Updated: OctoberFebruary 20252026
    • Applicable to PatchMon: v1.3.1+4.0+

    For updates and additional documentation, see: https://github.com/PatchMon/PatchMon