The backend reads its settings from backend/.env, the admin from frontend/.env.local. A new project gets both files from the CLI. backend/.env.example and frontend/.env.local.example document the same variables.
The backend checks its variables at startup. When one is missing or invalid, it prints the problem and exits instead of starting with a broken setup. These combinations are also refused:
STORAGE_DRIVER=s3 without STORAGE_S3_BUCKET, STORAGE_S3_ACCESS_KEY and STORAGE_S3_SECRET_KEY
SEARCH_DRIVER=meilisearch without MEILISEARCH_URL
CAPTCHA_PROVIDER without CAPTCHA_SECRET_KEY
OIDC_ISSUER without OIDC_CLIENT_ID and OIDC_CLIENT_SECRET
a CDN_PURGE_DRIVER without its credentials
Boolean variables take the strings true and false.
PostgreSQL connection URL the app uses. With a connection pooler, the pooled URL.
DIRECT_URL
Direct (non-pooled) PostgreSQL URL, used by migrations. Without a pooler, the same as DATABASE_URL.
JWT_SECRET
Signs access tokens and draft preview tokens. At least 32 characters. Generate one with node -e "console.log(require('crypto').randomBytes(32).toString('hex'))".
CORS_ORIGIN
The admin’s origin, e.g. https://admin.example.com, without a trailing slash. Comma-separate several. Outside production, any localhost / 127.0.0.1 origin is also allowed.
Port the API listens on. In development the admin picks it up from backend/.env automatically.
NODE_ENV
development
development, production or test.
APP_URL
http://localhost:<PORT> in development
The backend’s public URL. Local media URLs, the OpenAPI server URL and logo links in emails are built from it.
SITE_URL
http://localhost:5173
The admin’s public URL. Used in password reset and invitation links, the sitemap and the SSO callback.
TRUST_PROXY
loopback, linklocal, uniquelocal
Which proxies may set X-Forwarded-For, so rate limits and the audit log see the real client IP. The default trusts private networks (Docker nginx, Kubernetes ingress). Behind a CDN on a public IP, set a hop count such as 2.
LOG_LEVEL
debug in development, info in production
debug, info, warn or error.
JWT_EXPIRES_IN
7d
Access token lifetime. Refresh tokens always last 30 days.
SETTINGS_SECRET
JWT_SECRET
Key for secrets saved in the database (SMTP password, S3 secret key). At least 32 characters. Changing it makes saved secrets unreadable, so re-enter them.
CORS_ORIGIN=* sends a literal wildcard, which browsers refuse for signed-in (credentialed) requests. Never put the backend’s own URL in CORS_ORIGIN.
e.g. redis://localhost:6379. Optional for one instance, required for several: it shares the cache, the rate-limit counters and real-time events between instances. Without it, each instance keeps them in memory.
SMTP server. When set, email settings come from these variables and are read-only in the admin. When unset, the settings saved in Settings → Email & storage apply.
SMTP_PORT
587
SMTP port.
SMTP_SECURE
false
true for implicit TLS (port 465), false for STARTTLS (port 587).
SMTP_USER
none
SMTP user name.
SMTP_PASS
none
SMTP password.
SMTP_FROM
none
Sender, e.g. NodePress <noreply@example.com>.
Email is needed for password resets, invitations and form email actions. See Email.
signed accepts only packages signed by a trusted key, unsigned accepts any valid package, off blocks uploads and deletes (installed plugins still load).
PLUGIN_TRUSTED_KEYS
none
Comma-separated Ed25519 public keys (base64url) whose signatures are accepted.
PLUGIN_MAX_UPLOAD_MB
25
Maximum plugin package size.
PLUGINS_DIR
backend/.plugins
Where uploaded packages are unpacked.
PLUGIN_REGISTRY_URL
the NodePress plugins-registry.json on GitHub
Plugin directory shown on the Plugins page under Browse. Fetched by the server and cached for an hour. An empty value turns it off.
The backend’s URL as the admin server reaches it. The admin proxies /api, /uploads and /graphql to it. In Docker this can be an internal name such as http://backend:3000.
NEXT_PUBLIC_API_URL
the page’s origin
The backend’s URL as the browser reaches it, for the real-time (Socket.io) connection. Set it when the backend is on another host.
NEXT_PUBLIC_BACKEND_URL
none
Fallback for BACKEND_URL in server code.
SITE_URL
http://localhost:5173
The public site origin, used for absolute canonical and Open Graph URLs on public pages.
NEXT_PUBLIC_SENTRY_DSN
none
Sends admin errors to Sentry. Needs the Sentry packages, which projects get with --sentry.
NEXT_PUBLIC_* values are built into the admin’s JavaScript, so set them before npm run build.
The Compose files read a root .env with their own variables (DB_PASSWORD, JWT_SECRET, CORS_ORIGIN, APP_URL, SITE_URL, and DOMAIN for the HTTPS setup) and pass them to the containers. See Docker.