Configuration
RSSMonster has sensible defaults for a personal installation, but database, crawling, security, AI, and recommendation behavior can be configured with environment variables.
Where Configuration Lives
The configuration file depends on how RSSMonster is run:
- Default Docker Compose: create
.envin the repository root. Compose reads it for variable substitution indocker-compose.yml. - MySQL Docker Compose: use the same root
.envwithdocker-compose.mysql.yml. - Manual server installation: copy
server/.env.exampletoserver/.env. The web, crawl worker, and AI worker load this file. - Client: copy
client/.env.exampletoclient/.env. Variables beginning withVITE_are compiled into the client bundle, so rebuild the client after changing them.
Restart the affected process or recreate its container after changing server settings. For Docker, only variables listed under a service’s environment section are passed into the container. Add crawling options to rssmonster-worker, processing-job options to rssmonster-ai-worker, and shared database or inference options to both services. For example:
services:
rssmonster-worker:
environment:
CRAWL_VERBOSE_LOGGING: ${CRAWL_VERBOSE_LOGGING:-false}
Do not commit .env files. Store secrets as long, random values. On systems with POSIX file permissions, restrict each secret-bearing environment file to its owner after creating it:
chmod 600 .env
# For a manual server installation:
chmod 600 server/.env
The root .env remains on the Docker host: Compose reads it for variable substitution and passes the configured values into the containers. It is excluded from Docker build contexts and is not mounted into the containers, so neither the Dockerfile nor Compose can set its host permissions. On platforms without POSIX permissions, use the platform’s access controls to grant access only to the account that runs RSSMonster.
Docker Configuration
The supplied Compose files accept these root .env values:
| Variable | Default | Description |
|---|---|---|
RSSMONSTER_IMAGE | unset | Complete RSSMonster image reference. When set, it overrides RSSMONSTER_TAG and can include a digest. |
RSSMONSTER_TAG | latest | RSSMonster image tag used when RSSMONSTER_IMAGE is unset. Use latest only for quick starts or deliberate rolling updates. |
RSSMONSTER_BIND_ADDRESS | 127.0.0.1 | Host address on which Docker publishes the application port. |
RSSMONSTER_PORT | 3000 | Host port mapped to container port 3000. |
JWT_SECRET | required | Secret used to sign login tokens. |
FEVER_CREDENTIAL_SECRET | required | Secret used to protect Fever API credentials. Keep it stable after users create credentials. |
TRUST_PROXY | false | Express trusted-proxy setting; see Proxy and network security. |
RSSMONSTER_INTERNAL_HOST_ALLOWLIST | empty | Explicit exceptions for feeds hosted on private networks. |
The MySQL Compose file additionally accepts:
| Variable | Default | Description |
|---|---|---|
DB_DATABASE | rssmonster | MySQL database name. |
DB_USERNAME | rssmonster | MySQL application user. |
DB_PASSWORD | required | Password shared by RSSMonster and the MySQL application user. |
MYSQL_ROOT_PASSWORD | required | MySQL root password used by the database container. |
Generate secrets with openssl rand -hex 32. Use a different value for each secret and password.
Pinning the Docker image
The quick start defaults to latest for convenience. Do not use this moving tag for unattended production deployments: a later docker compose pull can change the running application and apply database migrations without selecting a specific release first.
The image publishing workflow creates a sha- tag for each published source revision. Pin that tag in the root .env:
RSSMONSTER_TAG=sha-abcdef0
For cryptographic pinning, use the published manifest digest as the complete image reference instead:
RSSMONSTER_IMAGE=rssmonster/rssmonster@sha256:replace-with-published-digest
RSSMONSTER_IMAGE takes precedence over RSSMONSTER_TAG and applies to the web, crawl-worker, and AI-worker containers so they always run the same image. Change the pin deliberately when updating, review the release or source changes, back up the database, and then run docker compose pull followed by docker compose up -d with the applicable Compose file.
See Backup and Restore for consistent SQLite and MySQL procedures and guidance on preserving the root .env separately.
Database
SQLite
DB_DIALECT=sqlite
DB_STORAGE=./data/rssmonster.sqlite
DB_STORAGE is resolved relative to the server directory for a manual installation. Use an absolute path in production when that makes persistence clearer. The default Docker deployment sets it to /app/data/rssmonster.sqlite and mounts a persistent volume at /app/data.
RSSMonster enables WAL mode, foreign-key enforcement, and a five-second busy timeout for SQLite. It also forces sequential crawling: parallel feed processing is disabled, concurrent user crawls are limited to one, and the user batch size is limited to one. Settings that request more concurrency are therefore ignored or capped when SQLite is active.
SQLite is intended for lightweight local experimentation, not database-intensive processing such as background AI jobs. Optional-processing concurrency is therefore always limited to one on SQLite, and the default SQLite Compose profile intentionally does not start rssmonster-ai-worker. Use the MySQL Compose profile when background article analysis or semantic labeling is required. This is a deliberate product and deployment boundary, not a missing Compose service.
MySQL
DB_DIALECT=mysql
DB_HOSTNAME=localhost
DB_PORT=3306
DB_DATABASE=rssmonster
DB_USERNAME=rssmonster
DB_PASSWORD=replace-with-a-strong-password
Use MySQL for higher write concurrency, multiple active users, background AI processing, or other demanding workloads. All five connection values are required when DB_DIALECT=mysql; the default port is 3306.
After changing databases or creating a new database, apply the canonical migrations from the server directory with npm run db. Docker images apply pending migrations automatically at startup.
MySQL sort memory
For large article collections with observed sort-memory bottlenecks, consider this MySQL server setting in my.cnf:
[mysqld]
sort_buffer_size = 4M
This is an optional tuning example, not a prerequisite or a supplied Compose setting. Evaluate query behavior and memory use before changing it.
Application and Authentication
| Variable | Default | Description |
|---|---|---|
NODE_ENV | development | Runtime mode: development, production, or test. Use production for deployments. |
PORT | 3000 | HTTP port used by the server. The supplied containers set this to 3000. |
JWT_SECRET | none | Required secret for signing and verifying JWTs. |
JWT_EXPIRES_IN | 86400 | Login-token lifetime in seconds. The example file uses 604800 (seven days). |
ALLOW_REGISTRATION | true | Environment default; an administrator’s saved choice in Settings → Server settings takes precedence. Set to false to disable public account creation. Hides signup and rejects registration API requests with HTTP 403, including when no users exist. Existing accounts, login, and password recovery are unaffected. |
FEVER_CREDENTIAL_SECRET | none | Required secret for keyed Fever credential hashes. Changing it invalidates existing Fever API credentials. |
ENABLE_DEVELOPMENT_LOGIN | false | Enables login without normal credentials, but only when NODE_ENV=development. Never enable it in a shared environment. |
DEVELOPMENT_LOGIN_USER_ID | none | Existing positive user ID selected by development login. It must be set when development login is enabled. |
ENABLE_HTTPS | false | When true, starts the server with certificates from server/cert. A reverse proxy is usually easier to operate. |
DISABLE_LISTENER | false | Prevents the HTTP listener from starting. This is intended for one-off crawl commands and tests. |
Authentication and API credential flows require these secrets. The supplied Compose files refuse to start without them. Keep both values stable across restarts and upgrades.
Administrators can change public registration in Settings → Server settings. Saved choices are stored in the server-wide server_settings table and take effect immediately. Use environment default removes the database override, falling back to ALLOW_REGISTRATION (or true when unset). This does not enable local authentication when LOCAL_AUTH_ENABLED=false or change OIDC auto-provisioning.
For a manual installation, add ALLOW_REGISTRATION=false to server/.env and restart the server. For Docker Compose, add ALLOW_REGISTRATION: "false" under the rssmonster service’s environment and recreate the container. Adding it only to the root .env does not pass it into the container automatically. Leave it unset or set it to true to allow registration again.
See First Login for administrator setup before disabling registration, the normal registration flow, and the security implications of enabling development login.
Authentication Policies and OIDC
For a provider-neutral setup guide, a Google example, and a preview of provider-only login, see OIDC Configuration.
Authentication configuration separates local account registration, local sign-in, and OIDC sign-in. OIDC account provisioning is an additional independent policy:
| Variable | Default | Meaning |
|---|---|---|
ALLOW_REGISTRATION | true | Allows visitors to create local accounts. Disabling it does not disable login. |
LOCAL_AUTH_ENABLED | true | Enables local password login, registration, password recovery/changes, development login, and password-derived Fever/Google Reader access. |
OIDC_ENABLED | false | Enables provider sign-in for linked identities and, when allowed, automatic provisioning. |
OIDC_AUTO_PROVISION | false | Allows a valid, previously unknown provider identity to create an ordinary OIDC-only account. Independent of public local registration. |
Values accept true or false, ignoring case and surrounding whitespace. Unset or blank values use the defaults. Other values stop server startup with a configuration error; this also applies to ALLOW_REGISTRATION.
OIDC uses a server-owned Authorization Code flow with PKCE, state, nonce, and ID-token signature validation. The browser receives a normal RSSMonster session after a short-lived, single-use handoff; provider tokens stay on the server. Sign in locally and link your identity provider from Settings → Account by confirming your current password. You can then sign out and use Sign in with identity provider. With automatic provisioning disabled, unknown identities are rejected, even if their email matches an existing account. Your local user ID, role, feeds and reading state remain unchanged. Existing email-verification requirements still apply.
Set OIDC_AUTO_PROVISION=true to create accounts after a valid provider login, including when ALLOW_REGISTRATION=false. Each identity is keyed by its exact, case-sensitive issuer and subject; a unique SHA-256 key enforces this independently of the database’s text collation. Account and identity creation are transactional, and concurrent first logins converge on the same account. Emails never link accounts automatically. A verified provider email is normalized and saved when creating an account; an unverified email is ignored. An email already used by an existing account causes provisioning to fail, so link that existing account explicitly instead. Later logins synchronize verified provider email for OIDC-only accounts. Linked local accounts retain their locally managed email settings.
Provisioned accounts receive a stable generated username, the ordinary user role, and no local password or Fever credential. Password recovery, account settings and administrator password updates cannot create local credentials for these accounts. Manage the provider password at the provider; legacy Google Reader and Fever password authentication are unavailable for OIDC-only accounts. Linking an existing local account preserves its credentials and API access.
By default, no additional claim restrictions apply to OIDC sign-in. Optional allowlists restrict every provider login, including existing linked accounts, new provisioning, and account linking:
| Variable | Default | Meaning |
|---|---|---|
OIDC_ALLOWED_EMAIL_DOMAINS | empty | Comma-separated exact email domains, e.g. example.com,example.org. Requires a valid ID-token email and boolean email_verified=true. Domain matching ignores case; subdomains and suffix matches are not included. Use ASCII/punycode domains, without wildcards or @. |
OIDC_ALLOWED_GROUPS | empty | Comma-separated group names. At least one must match exactly, including case, in an ID-token array of strings. |
OIDC_GROUPS_CLAIM | groups | Literal top-level ID-token claim containing that array. Dots or slashes in the name are literal, not a nested path. |
Blank allowlists disable that restriction. When both are configured, both must pass. Missing, unverified, or malformed required claims deny access before account lookup or creation. Configure your provider to include the necessary claims in its ID token; RSSMonster does not fetch groups from another endpoint. Restrictions never map provider groups or claims to administrator roles. Malformed nonempty lists stop startup when OIDC is enabled.
For an email-domain restriction during your Google test, set OIDC_ALLOWED_EMAIL_DOMAINS=example.com to your intended email domain. This checks verified email addresses, not organization membership. Allowing gmail.com permits any verified address in that domain subject to the other login/provisioning policies; it is not a list of individual test users. Keep provider-side application access restrictions in place as needed.
Policy changes invalidate pending OIDC callbacks and handoffs. Existing web sessions retain their normal expiry; claim restrictions apply at the next OIDC login and do not restrict local login when it remains enabled.
Set LOCAL_AUTH_ENABLED=false only after configuring and testing OIDC and linking your administrator account. Local registration is then unavailable even if ALLOW_REGISTRATION=true. Password reset requests and confirmations, account/admin password changes, development login, and password-confirmed provider linking are blocked. Existing Fever keys/cookies and Google Reader tokens are rejected. Passwords remain stored, so re-enabling local authentication restores their use. Existing web sessions retain their normal expiry and password version checks; this setting does not revoke them. Email verification and non-password account settings remain available.
Create the initial administrator locally before closing public registration. OIDC never promotes the first provider user. An existing administrator may explicitly promote an account through Manage Users.
The following provider settings stay on the server and are not exposed by the public authentication configuration endpoint:
| Variable | Default | Validation when OIDC_ENABLED=true |
|---|---|---|
OIDC_ISSUER_URL | none | Required HTTPS issuer URL; use the issuer, not its discovery-document URL. |
OIDC_CLIENT_ID | none | Required nonempty client identifier. |
OIDC_CLIENT_SECRET | none | Required nonempty secret, retained only on the server. |
OIDC_REDIRECT_URI | none | Required HTTPS callback URL with path /api/auth/oidc/callback. HTTP is accepted only for localhost, 127.0.0.1, or [::1] to support local testing. |
OIDC_FRONTEND_URL | callback origin | Optional frontend root URL for the final handoff. Must share the callback scheme and hostname; a different port is allowed. |
OIDC_SCOPES | openid profile email | Space-separated scopes; must include openid. |
Issuer and callback URLs cannot contain credentials, query parameters, or fragments. Provider settings are ignored while OIDC is disabled, so existing installations need no new configuration. By default, the callback origin is the fixed frontend return destination. Set OIDC_FRONTEND_URL when the frontend runs on a different port, such as http://localhost:8080 with the API on port
- This configured frontend origin is also the only accepted origin for credentialed OIDC linking and handoff requests. Scheme and hostname must match the callback so the browser-bound SameSite cookie works across ports. Arbitrary return URLs, different hostnames, and frontend subpaths are not supported.
Apply 20260910000000-add-oidc-login.js and 20260910001000-allow-oidc-only-users.js through the normal upgrade procedure before enabling OIDC. They add identity and temporary login tables and make local credentials nullable while preserving existing credential values. Rolling back the nullable-credential migration is refused while passwordless accounts exist. All server instances must share the database, JWT secret and OIDC configuration.
Test with Google
- Create an OAuth client of type Web application in Google Cloud, configure its consent screen/audience, and add your Google account as a test user if the application is in testing mode.
- Register the exact authorized redirect URI, for example
http://localhost:3000/api/auth/oidc/callbackfor a local installation. -
Configure the server (or the Compose service’s
environment):OIDC_ENABLED=true OIDC_ISSUER_URL=https://accounts.google.com OIDC_CLIENT_ID=your-google-client-id OIDC_CLIENT_SECRET=your-google-client-secret OIDC_REDIRECT_URI=http://localhost:3000/api/auth/oidc/callback OIDC_SCOPES=openid profile email OIDC_AUTO_PROVISION=false LOCAL_AUTH_ENABLED=true - Restart the server or recreate the container, open
http://localhost:3000, sign in to an existing RSSMonster account, and link Google in Settings → Account. Confirm your current RSSMonster password and choose your Google account. Then sign out of RSSMonster and test provider sign-in. - Verify that an unlinked Google account is rejected and that successful login returns to the same RSSMonster account and data. You may keep
ALLOW_REGISTRATION=falsethroughout linking and provider sign-in.
For remote deployments, use the public HTTPS address in both Google and OIDC_REDIRECT_URI. See Google’s OIDC documentation for client setup and redirect requirements.
Starting another login in the same browser replaces its pending browser cookie; finish the newest attempt. Abandoned attempts expire after ten minutes, and the handoff code expires after one minute. A provider outage leaves local login available only when LOCAL_AUTH_ENABLED=true. RSSMonster logout does not sign out of Google, and suspending a provider account does not immediately revoke existing RSSMonster JWTs; their normal expiry and password-change invalidation still apply.
Feed Crawling and Scheduling
Most installations should begin with the example defaults. Increase concurrency gradually while watching database load, crawl duration, HTTP 429 responses, and timeouts.
| Variable | Default | Unit | Effect |
|---|---|---|---|
FEED_MAX_COUNT | 100 | feeds | Maximum feeds claimed by one crawl invocation. |
FEED_PARALLEL_CONCURRENCY | 3 | feeds | Process-wide simultaneous feed workers. SQLite caps this at 1. |
FEED_TIMEOUT_MS | 60000 | ms | Overall processing deadline for one feed. |
FEED_LEASE_MS | 120000 | ms | Duration of a feed claim. The effective value is never less than twice FEED_TIMEOUT_MS. |
CRAWL_TIMEOUT_MS | 600000 | ms | Overall deadline for a crawl invocation. |
CRAWL_DUPLICATE_CACHE_DAYS | 30 | days | Article history loaded into the deterministic duplicate cache. Higher values use more memory and database work. |
CRAWL_RUN_HEARTBEAT_INTERVAL_MS | 30000 | ms | Renewal interval for an active crawl run’s ownership heartbeat. |
CRAWL_RUN_STALE_AFTER_MS | 120000 | ms | Age after which a missing crawl-run heartbeat is treated as stale. The effective value is at least three heartbeat intervals. |
CRAWL_USER_BATCH_SIZE | 5 | users | Users crawled concurrently on MySQL. SQLite always uses 1. |
CRAWL_PARALLELPROCESSFLAG | 0 | boolean integer | Set to 1 to allow parallel feed processing on MySQL. SQLite always forces 0. |
CRAWL_WORKER_INTERVAL_MS | 60000 | ms | Delay between dedicated worker polls. Must be a positive integer. |
PROCESSING_JOB_POLL_INTERVAL_MS | 1000 | ms | Delay when no optional processing work is available. |
PROCESSING_JOB_CONCURRENCY | 1 | jobs | Maximum optional jobs executed concurrently. A manually started AI worker on SQLite always forces this to 1. |
PROCESSING_JOB_SHUTDOWN_TIMEOUT_MS | 30000 | ms | Grace period for in-flight optional jobs before their abort signal is triggered. |
PROCESSING_JOB_REPORT_INTERVAL_MS | 60000 | ms | Interval for durable queue depth, retry, terminal outcome, and latency snapshots in AI-worker health state. |
CRAWL_PRIORITY_LEASE_MS | 90000 | ms | Duration of the renewable database gate that gives the crawl semantic pipeline priority over new optional claims. |
CRAWL_PRIORITY_HEARTBEAT_MS | 30000 | ms | Renewal interval for the crawl-priority lease. |
CRAWL_WORKER_HEALTH_MAX_FAILURES | 3 | failures | Consecutive failed crawl iterations allowed before the worker is unhealthy. |
CRAWL_WORKER_HEALTH_MAX_STALE_MS | 900000 | ms | Maximum age of the worker health state before the worker is unhealthy. |
CRAWL_WORKER_HEALTH_FILE | /tmp/rssmonster-crawl-worker-health.json | path | Crawl-worker heartbeat file. Compose overrides this with its shared health volume. |
AI_WORKER_HEALTH_MAX_FAILURES | 3 | failures | Consecutive failed AI-worker iterations allowed before its health check fails. |
AI_WORKER_HEALTH_MAX_STALE_MS | 900000 | ms | Maximum age of the AI-worker health state before it is considered stale. |
AI_WORKER_HEALTH_FILE | /tmp/rssmonster-ai-worker-health.json | path | AI-worker heartbeat file read by the status API and health check. Compose overrides this with its shared health volume. |
CRAWL_VERBOSE_LOGGING | false | boolean | Emits candidate, retry, and feed-discovery diagnostics in addition to final results. |
FEED_PARALLEL_CONCURRENCY is the main MySQL throughput control. Raising it increases simultaneous network, parser, AI, and database work. Keep FEED_LEASE_MS comfortably above realistic feed-processing time so another worker does not reclaim active work.
PM2 and the MySQL Compose profile run scheduled crawling in rssmonster-worker and optional jobs in rssmonster-ai-worker. A renewable database lease pauses new optional claims while the crawl, embedding, event and island-scoring pipeline is active; crawling never waits for the optional queue to drain. The lightweight SQLite Compose profile runs only rssmonster-worker and has no optional-job consumer by default. Remote inference connectivity is configured independently.
HTTP Fetch Behavior
| Variable | Default | Unit | Effect |
|---|---|---|---|
FEED_CONNECT_TIMEOUT_MS | 10000 | ms | Maximum time allowed to establish a feed TCP/TLS connection. |
FEED_BODY_TIMEOUT_MS | 30000 | ms | Maximum time allowed to download a feed body after response headers arrive. |
FEED_RESPONSE_MAX_BYTES | 10485760 | bytes | Maximum downloaded response body (10 MiB). |
FEED_ORIGIN_MAX_CONCURRENCY | 2 | requests | Simultaneous requests to the same origin. |
FEED_ORIGIN_MIN_SPACING_MS | 250 | ms | Minimum delay between requests to the same origin. Set to 0 to disable spacing. |
FEED_CACHE_FRESHNESS_MAX_MS | 86400000 | ms | Maximum accepted cache-freshness period from publisher headers (one day). |
FEED_RETRY_AFTER_MAX_MS | 604800000 | ms | Maximum accepted Retry-After delay (seven days). |
Lower per-origin concurrency and larger spacing are gentler on publishers. The connection and body limits apply to their individual HTTP phases, while FEED_TIMEOUT_MS remains the hard deadline for all acquisition, parsing, and persistence work for one feed. Increasing response or timeout limits can accommodate unusual feeds, but also increases the resources a slow or oversized response may consume. Prefer the phase-specific settings over the legacy FEED_HTTP_TIMEOUT_MS fallback when upgrading an existing manual installation.
Parser Safety Limits
Feeds are parsed in disposable worker threads with CPU and memory bounds. Entries that exceed the configured input limits are rejected before article enrichment and persistence.
| Variable | Default | Unit | Effect |
|---|---|---|---|
FEED_PARSER_TIMEOUT_MS | 2000 | ms | Parser worker deadline. |
FEED_PARSER_MEMORY_MB | 64 | MiB | Parser worker old-generation heap limit. |
FEED_MAX_ENTRIES | 1000 | entries | Maximum entries accepted in one feed response. |
FEED_MAX_GUID_BYTES | 4096 | bytes | Maximum normalized entry identifier size. |
FEED_MAX_URL_BYTES | 8192 | bytes | Maximum normalized entry URL size. |
FEED_MAX_TITLE_BYTES | 4096 | bytes | Maximum normalized title size. |
FEED_MAX_AUTHOR_BYTES | 2048 | bytes | Maximum normalized author size. |
FEED_MAX_CONTENT_BYTES | 2097152 | bytes | Maximum combined content and description size per entry (2 MiB). |
These are defensive limits, not routine performance targets. Raise one only when a trusted feed is known to exceed it and the added resource exposure is acceptable.
Proxy and Network Security
TRUST_PROXY
RSSMonster defaults to false, so forwarded client-address headers are ignored unless a trusted proxy is configured. It accepts loopback, a positive proxy hop count, or an Express trusted address/subnet expression. The value true is rejected because trusting every proxy can let clients spoof their address and affect rate limiting.
The supplied Docker profiles publish the application on host loopback by default. This keeps clients from bypassing a reverse proxy and sending forged forwarding headers directly to RSSMonster:
RSSMONSTER_BIND_ADDRESS=127.0.0.1
When a reverse proxy runs directly on the Docker host, Docker port forwarding usually makes the proxy connection appear to RSSMonster as a Docker bridge address, not as loopback. With the port restricted to host loopback and exactly one proxy hop, use:
RSSMONSTER_BIND_ADDRESS=127.0.0.1
TRUST_PROXY=1
When the reverse proxy runs in a container, attach it to the same Docker network and send traffic to rssmonster:3000. Keep the published host port on loopback, or remove the ports entry in a Compose override, and trust the exact proxy address/subnet when it is stable. TRUST_PROXY=1 is also appropriate when that container is the only possible route to RSSMonster and there is exactly one proxy hop.
For a manual, non-Docker installation where a same-host proxy connects directly to 127.0.0.1, TRUST_PROXY=loopback is appropriate. Prefer explicit trusted addresses or subnets when traffic can arrive through paths with different hop counts. Do not expose the application directly to untrusted clients while using a hop-count setting.
To intentionally expose Docker directly on every host interface, disable proxy trust and opt into the broader bind address:
RSSMONSTER_BIND_ADDRESS=0.0.0.0
TRUST_PROXY=false
RSSMONSTER_INTERNAL_HOST_ALLOWLIST
Outbound feed requests block loopback, private, link-local, and other non-public address ranges to reduce server-side request-forgery risk. If you intentionally subscribe to an internal feed, add only its exact host, IP, CIDR, or host:port to the space-separated allowlist:
RSSMONSTER_INTERNAL_HOST_ALLOWLIST=feeds.internal.example 10.20.30.40:8080
Keep exceptions narrow. An allowlisted destination becomes reachable by the server’s feed-fetching code.
Direct HTTPS and Certbot
For a manual installation that terminates TLS in Node, obtain a certificate with Certbot on the host:
certbot certonly --standalone -d yourdomain.com --agree-tos -q
The standalone challenge needs its validation port available. Place fullchain.pem and privkey.pem in server/cert/, readable by the server account, and protect the private key. Set this in server/.env:
ENABLE_HTTPS=true
Run the application from server: certificate paths are relative to its working directory. PORT still controls the listener (default 3000); enabling HTTPS does not automatically change it to 443. Rebuild the client with the actual public HTTPS origin in VITE_APP_HOSTNAME and restart the server.
For a host with Certbot installed, a weekly renewal schedule can be used as a starting point in the operator’s crontab:
0 0 * * 0 certbot renew --quiet
Configure a renewal deploy hook to copy the renewed fullchain.pem and privkey.pem from /etc/letsencrypt/live/yourdomain.com/ into /path/to/rssmonster/server/cert/, preserve restrictive key permissions, and restart the web service through its service manager. Node reads certificates at startup, so copying renewed files alone does not activate them. Adapt the hook to the service account and process manager; do not copy the entire certificate directory indiscriminately. Reverse-proxy TLS deployments should use the proxy’s certificate renewal and reload mechanism instead.
Rate Limiting
| Variable | Default | Description |
|---|---|---|
API_RATE_LIMIT_WINDOW_MS | 900000 | API rate-limit window in milliseconds (15 minutes). |
API_RATE_LIMIT_MAX | 600 | Requests allowed per client during the API window. |
ARTICLE_INTERACTION_RATE_LIMIT_MAX | 3000 | Separate allowance per client IP for article-list/detail loading and read/seen updates, using the API window. |
MCP_RATE_LIMIT_WINDOW_MS | 900000 | MCP rate-limit window in milliseconds. |
MCP_RATE_LIMIT_MAX | 100 | Requests allowed per client during the MCP window. |
All values must be positive integers. Health checks and OPTIONS requests are excluded. Configure TRUST_PROXY correctly before tuning limits behind a reverse proxy so client addresses are interpreted correctly.
Inference and OpenAI Features
| Variable | Default | Description |
|---|---|---|
INFERENCE_BASE_URL | unset | Deployment-managed inference endpoint. Takes precedence over Settings; without either, inference is not configured. |
INFERENCE_API_KEY | unset | Optional opaque shared secret; set the exact same value on server/workers and inference. No inference key disables authentication. |
INFERENCE_TIMEOUT_MS | 30000 | Timeout for embeddings, classification, recommendations, and feed rediscovery. |
INFERENCE_AGENT_TIMEOUT_MS | 300000 | Timeout for streamed assistant model requests. |
INFERENCE_CIRCUIT_FAILURE_THRESHOLD | 5 | Consecutive qualifying failures before a capability-specific server inference circuit opens. Must be a positive integer. |
INFERENCE_CIRCUIT_COOLDOWN_MS | 30000 | Minimum open-circuit cooldown before one half-open probe is allowed. Must be a positive integer. |
INFERENCE_AI_ENABLED | unset | Optional permission override. Explicit false prohibits inference; otherwise a configured endpoint is required. |
INFERENCE_ASSISTANT_ENABLED | unset | Optional permission override. Explicit false disables assistant use; otherwise readiness and advertised capability determine availability. |
SKIP_ARTICLE_CLASSIFICATION_ANALYSIS | false | When true, uses default article scores and feed-category tags without calling inference classification. |
SKIP_ARTICLE_EMBEDDINGS | false | When true, disables article vector generation and defaults new feeds to embeddings disabled. |
SKIP_SEMANTIC_LABELING | false | When true, skips generated event and island display labels while preserving deterministic names and labels. |
When INFERENCE_AI_ENABLED is explicitly false, it overrides the feature-specific settings: classification and embeddings remain local or disabled, semantic labeling remains disabled, and assistant, Smart Folder recommendation, and feed-rediscovery requests return INFERENCE_DISABLED without contacting an inference endpoint.
OpenAI credentials and model names belong only in inference/.env; see Model Usage. The server executes authenticated assistant tools locally, while inference performs every provider model call. The server uses the shared inference connection and advertised capabilities, respecting optional permission overrides, and never receives provider API keys.
The server process keeps independent circuit breakers for embeddings, classification, non-streaming assistant, Smart Folder, and feed-rediscovery requests. Connection failures, timeouts, HTTP 502/503/504, and startup not_ready responses count toward the affected capability’s threshold. Queue overload is intentional endpoint-level load shedding and does not trip a circuit. While a circuit is open, only that capability fails locally without contacting inference. After the cooldown—or a longer inference Retry-After—one half-open probe is allowed. Validation, authentication, ordinary 4xx, and caller cancellation do not trip the circuit. Requests are never automatically retried. During crawl ingestion, classification queue overload is recorded as a warning and falls back to the existing default article analysis so it does not prevent article or revision persistence.
Recommendations
| Variable | Default | Description |
|---|---|---|
ARTICLE_RECOMMENDATION_MIN_SIMILARITY | 0.64 | Minimum cosine similarity for recent article recommendations. Valid range is -1 through 1. Higher values return fewer, closer matches. |
ARTICLE_RECOMMENDATION_MAX_CANDIDATES | 600 | Number of recent vectorized articles considered. Values are capped at 600. Lower values reduce query and scoring work. |
Zero recommendations is valid. Tune the similarity threshold cautiously and evaluate results across several users and feed mixes.
Client Build Configuration
| Variable | Example | Description |
|---|---|---|
VITE_APP_HOSTNAME | http://localhost:3000 | Base URL used by the browser for API requests. Use the public server origin. |
VITE_BASE_URL | / | Reserved in the example configuration; the current client does not read this custom variable. |
Vite embeds these values during npm run build. Restarting an already-built client without rebuilding does not apply changes. Development-only client behavior is derived automatically from Vite’s active mode.
Example Manual Configurations
Minimal SQLite server/.env:
NODE_ENV=production
DB_DIALECT=sqlite
DB_STORAGE=/var/lib/rssmonster/rssmonster.sqlite
JWT_SECRET=replace-with-a-long-random-secret
FEVER_CREDENTIAL_SECRET=replace-with-a-different-long-random-secret
TRUST_PROXY=false
MySQL with modest parallel crawling:
NODE_ENV=production
DB_DIALECT=mysql
DB_HOSTNAME=127.0.0.1
DB_PORT=3306
DB_DATABASE=rssmonster
DB_USERNAME=rssmonster
DB_PASSWORD=replace-with-a-strong-password
JWT_SECRET=replace-with-a-long-random-secret
FEVER_CREDENTIAL_SECRET=replace-with-a-different-long-random-secret
CRAWL_PARALLELPROCESSFLAG=1
FEED_PARALLEL_CONCURRENCY=3
CRAWL_WORKER_INTERVAL_MS=60000
TRUST_PROXY=false
Start with defaults, change one group of settings at a time, and inspect crawl statistics and logs before increasing concurrency or resource limits.
Email and Web Push
Email and browser notifications are independent optional features:
- Email Configuration documents
EMAIL_ENABLED,PUBLIC_APP_URL, SMTP authentication/TLS, password-reset rate limits, and delivery diagnostics. Enabling email also changes account enrollment requirements. - Web App and Notifications documents
VAPID_PUBLIC_KEY,VAPID_PRIVATE_KEY, andVAPID_SUBJECTfor browser Push.
Neither feature is enabled merely by installing the web app. Pass the appropriate variables to each responsible process and recreate containers after changes.
See inference authentication for key generation, HTTPS, protected endpoints, and the legacy INFERENCE_URL fallback.
Inference connections can also be saved by an administrator in Settings → AI / Inference on MySQL, SQLite, or Desktop. A nonempty environment URL always takes precedence and hides editing controls; capability status remains visible. See the inference guide for encrypted key storage, keep/replace/remove semantics, and worker limitations.
SMTP overrides in Server settings
Administrators can open Settings → Server settings → SMTP options and check Override environment defaults for all SMTP options on EMAIL_ENABLED. This makes all displayed SMTP values authoritative together, even when email is set to disabled. Clearing that checkbox and saving restores the entire environment configuration, including the password. Restore environment defaults also removes every SMTP override. The masked password field retains an existing saved override when unchanged; entering a password replaces it, and clearing an edited field removes it. When enabling overrides for the first time, enter the SMTP password if authentication is required.
Override mode never inherits SMTP_PASSWORD or SMTP_PASSWORD_FILE from the environment. Password files are supported only with environment configuration, not through the form. Database passwords are encrypted at rest using ENCRYPTION_KEY and are never returned to the form, status APIs, or logs.
Use RSSMonster <reader@example.com> for a sender with a display name. Port 587 uses STARTTLS (SMTP_SECURE=false, SMTP_REQUIRE_TLS=true); port 465 uses implicit TLS (SMTP_SECURE=true, SMTP_REQUIRE_TLS=false). The public application URL is used in verification, password-reset, and briefing links.
Saved changes affect new account/email operations immediately. Delivery and daily briefing workers resolve configuration on their next poll, within five minutes; an already running batch may finish with its current configuration. Workers keep polling while email is disabled, so enabling delivery does not require a restart. Test SMTP connection checks the saved configuration without sending a message.
OIDC and local authentication overrides
Administrators can use Settings → Server → OIDC options to override OIDC_ENABLED and LOCAL_AUTH_ENABLED independently. Enabling the OIDC override also manages its provider options as one group: OIDC_AUTO_PROVISION, OIDC_ISSUER_URL, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, OIDC_REDIRECT_URI, OIDC_FRONTEND_URL, and OIDC_SCOPES. Clearing that override restores the entire provider configuration from the environment. Access-policy environment variables remain authoritative.
Changes apply immediately, including login availability, local password actions, Fever/Google Reader access, and the provider callback origin. At least one sign-in method must remain enabled; invalid provider URLs, scopes, and callback paths are rejected. Existing sessions remain valid. Provider configuration changes invalidate pending provider sign-ins, which must be started again. Test provider sign-in before disabling local authentication.
The existing server_settings table stores these overrides; no additional migration is required. Database client IDs and client secrets are encrypted at rest using ENCRYPTION_KEY. API responses only report whether these values are configured and overridden. An unchanged masked client ID retains its saved value (or the environment value if no override exists); editing replaces or clears it. Client secrets can be retained, replaced, or restored from the environment without being displayed.
Encryption of sensitive server settings
Set ENCRYPTION_KEY to a Base64-encoded 32-byte key, generated outside RSSMonster:
openssl rand -base64 32
Database overrides for SMTP_PASSWORD, OIDC_CLIENT_ID, OIDC_CLIENT_SECRET, VAPID_PUBLIC_KEY, and VAPID_PRIVATE_KEY use this key. The server uses Node.js AES-256-GCM with a new random 12-byte IV per encryption and stores enc:v1:<iv>:<authTag>:<ciphertext> with Base64 envelope components. Empty values represent cleared credentials. Other settings and environment-backed credentials are unchanged. The key is never stored in the database, generated automatically, or derived from JWT_SECRET. Keep it stable, secure, backed up separately, and identical across server/worker instances. Losing or changing it makes existing encrypted overrides unreadable; restore the original key or replace the overrides before using them.
Existing plaintext database credentials remain readable without a key. The next successful save of their SMTP or OIDC settings encrypts all retained/replacement nonempty credentials in that group under the existing transaction. Configure the key and re-save each group to complete this migration; until then those old values remain plaintext at rest. There is no schema migration or automatic startup write.
Missing/invalid keys do not prevent environment-only or legacy-plaintext operation, metadata reads, or startup with unused credentials in disabled services. Saving a nonempty database credential or using an encrypted credential fails with a clear encryption configuration error. Malformed, tampered, or wrong-key ciphertext is never treated as plaintext. Metadata contains presence/override flags only, allowing administrators to replace credentials or restore environment defaults even when the key is unavailable. Encrypted credentials are decrypted only for runtime use or to validate and re-encrypt retained values during a settings save.
Feed HTTP Basic Authentication passwords use the same key and encryption mechanism. Configure it on the server and crawl workers before saving or fetching authenticated subscriptions. Feed passwords are excluded from normal Feed API responses. In the feed editor, an empty password retains the saved credential; selecting None and saving clears it. See HTTP Basic Authentication for adding and editing protected feeds.
Web Push overrides in Server settings
Administrators can open Settings → Server settings → Web Push options to configure optional browser notifications. Override environment default manages VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, and VAPID_SUBJECT together. On first enabling the override, enter a matching P-256 VAPID key pair encoded as unpadded Base64url and a mailto: or HTTPS contact subject. Both keys are masked in the form and encrypted with ENCRYPTION_KEY in the pushConfiguration server setting; the subject is stored without encryption. Unchanged fields retain saved keys. Clear both keys to disable push under the override. Uncheck and save, or restore defaults, to remove the whole override and use the environment again.
Settings APIs return presence/override metadata for both keys, never their values. The authenticated browser subscription endpoint still returns the public key, as required by Web Push; it never returns the private key. Delivery resolves the current configuration for each batch and uses request-local VAPID details, so no restart is needed. Changing a key pair may require readers to unsubscribe and subscribe again. No keys or subscriptions are generated automatically.
Crawl overrides in Server settings
Administrators can open Settings → Server settings → Crawl options and enable Override environment default to manage the scheduling, request, parser, and entry-limit fields as one group. All 18 fields are validated as bounded integers; parallel mode permits only 0/1, and origin spacing permits zero. Restoring defaults removes only the crawlConfiguration row from server_settings.
The form displays inherited environment values. Use suggested values loads 60-second worker polling, a 60-minute stale-heartbeat threshold, a 100-feed batch, 10 MiB response limit, a 120-second requested lease, 2 requests per origin with 250 ms spacing, a 2-second/64 MiB parser, 2,000 entries, 4 KiB IDs/titles, 8 KiB URLs, 2 KiB authors, 2 MiB entry content, a 10-second HTTP fallback, and a 300-second feed deadline. These suggestions are not saved automatically. Existing code defaults of 1,000 entries and a 60-second deadline remain unchanged when no override or environment value is present.
New crawls read a saved snapshot, and active crawls retain their existing settings. Parser threads receive the same limits. The worker refreshes its polling interval between iterations; an existing sleep is not interrupted by a settings save. SQLite remains sequential. A feed lease is always at least twice the feed deadline, so a requested 120-second lease with a 300-second deadline becomes 600 seconds.
CRAWL_RUN_MAX_RUNNING_MINUTES controls how long a run can go without a heartbeat before existing recovery logic considers it abandoned; it does not expire healthy long-running work. Without this setting, CRAWL_RUN_STALE_AFTER_MS still applies. FEED_HTTP_TIMEOUT_MS supplies a fallback only when the separate FEED_CONNECT_TIMEOUT_MS or FEED_BODY_TIMEOUT_MS environment value is unset.