Skip to content

Configuration

Every environment variable Postmill recognises, sourced from .env.example. All variables are read at boot time. Most feature-specific provider credentials (channel OAuth apps, AI providers, storage, short links) are configured per-organization in-app; this page covers the deployment-level variables.

Required

VariableDefaultPurpose
DATABASE_URLPostgreSQL connection string for the application database
REDIS_URLRedis connection string. Use redis:// for local Redis or rediss:// for Upstash / TLS endpoints
JWT_SECRETSecret key for signing JWT tokens; also used as the encryption key fallback
FRONTEND_URLPublic-facing URL of the application (e.g. https://postmill.example.com)
NEXT_PUBLIC_BACKEND_URLPublic URL of the backend API (e.g. https://postmill.example.com/api)
BACKEND_INTERNAL_URLInternal URL for backend-to-backend calls (e.g. http://localhost:3000)
MAIN_URLAlternative public URL, used in Docker Compose alongside FRONTEND_URL
IS_GENERALMust be true for standard self-hosted deployments

Storage

VariableDefaultPurpose
UPLOAD_DIRECTORYLocal path for file uploads (e.g. /uploads). Avatars and app-internal images always use LOCAL
NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORYPublic URL path serving uploads (e.g. /uploads)
MEDIA_UPLOAD_MAX_BYTES1073741824Maximum upload file size for /files/upload-server (default 1 GB)
LOCAL_STORAGE_QUOTA_GB5Default soft quota for each org's local storage, in GB

Email

Pluggable provider system with 6 adapters: Resend, SendGrid, Mailgun, Postmark, Amazon SES, and SMTP. The active provider is selected globally via EMAIL_PROVIDER; unset/unknown → email is off (users activate automatically).

VariableDefaultPurpose
EMAIL_PROVIDERresend, sendgrid, mailgun, postmark, ses, or smtp
EMAIL_API_KEYAPI key for Resend, SendGrid, Mailgun, or Postmark
EMAIL_FROM_ADDRESSSender email address
EMAIL_FROM_NAMESender display name
EMAIL_WEBHOOK_SECRETSigning secret / verification key for the active provider's webhook
EMAIL_MAILGUN_DOMAINMailgun sending domain (required for mailgun)
EMAIL_REGIONusAPI region for Mailgun (us/eu) and SES region
EMAIL_SES_ACCESS_KEY_IDSES IAM access key (falls back to AWS_* env vars)
EMAIL_SES_SECRET_ACCESS_KEYSES IAM secret key (falls back to AWS_* env vars)
EMAIL_SMTP_HOSTSMTP server hostname (required for smtp)
EMAIL_SMTP_PORT587SMTP server port
EMAIL_SMTP_SECUREfalseUse TLS for SMTP
EMAIL_SMTP_USERSMTP authentication username (optional for open relays)
EMAIL_SMTP_PASSSMTP authentication password
EMAIL_LOG_RETENTION_DAYS90Days to keep email log metadata before pruning
DISABLE_REGISTRATIONfalseSet to true to disable self-registration. The first user of an empty instance can still register, and GENERIC OIDC sign-ins still provision
DISALLOW_PLUSfalseSet to true to reject email/password (LOCAL) registrations whose address contains a +

The webhook endpoint is at POST /webhooks/email, signature-verified, and registered outside CSRF (same as Stripe). SES uses SNS topic verification; EMAIL_WEBHOOK_SECRET can optionally hold the expected SNS TopicArn.

Provider credential setup

ProviderAPI key sourceWebhook signing secret
ResendCreate an API key at resend.comIn webhook settings on the Resend dashboard
SendGridCreate a "Full Access" API key at sendgrid.com/settings/api_keysEnable Event Webhook and copy the Verification Key
MailgunUse SMTP credentials or create a Mailgun API keySent via the Mailgun webhook setup page. Also set EMAIL_MAILGUN_DOMAIN and EMAIL_REGION
PostmarkCreate a server API token at postmarkapp.comGenerated when creating a webhook in the server settings
Amazon SESIAM credentials, or fall back to AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY / AWS_SESSION_TOKENSES uses SNS. Subscribe POST /webhooks/email to the SNS topic; EMAIL_WEBHOOK_SECRET can pin the TopicArn
SMTPNo API key — set host/port/secure/user/passWebhooks are not supported

Newsletter signups

New registrants can be subscribed to a marketing newsletter list. Beehiiv is used when BEEHIIVE_API_KEY is set, otherwise Listmonk when LISTMONK_API_KEY is set; with neither set, no signup is sent.

VariableDefaultPurpose
BEEHIIVE_API_KEYBeehiiv API key
BEEHIIVE_PUBLICATION_IDBeehiiv publication ID the subscriber is added to
LISTMONK_API_KEYListmonk API key (HTTP Basic password, paired with LISTMONK_USER)
LISTMONK_USERListmonk API username (HTTP Basic user)
LISTMONK_DOMAINBase URL of the Listmonk instance. A self-hosted Listmonk on a private network must be allowlisted via SSRF_ALLOWED_PRIVATE_CIDRS
LISTMONK_LIST_IDNumeric list ID to subscribe new registrants to
LISTMONK_WELCOME_TEMPLATE_IDNumeric transactional template ID for the welcome email

Campaign Hub

VariableDefaultPurpose
CAMPAIGN_PURGE_DAYS30Days after a campaign's endDate before its CampaignItem tags are purged

Push notifications

Browser and mobile push notifications are sent via Firebase Cloud Messaging (FCM) v1. Push is globally disabled when any of these is unset.

VariableDefaultPurpose
FCM_PROJECT_IDFirebase project ID
FCM_CLIENT_EMAILFirebase service account client email
FCM_PRIVATE_KEYFirebase service account private key (PEM)

Analytics & background jobs

VariableDefaultPurpose
USE_INNGESTSet to true to enable Inngest-driven background jobs
INNGEST_EVENT_KEYInngest Cloud event key (required unless INNGEST_DEV=1)
INNGEST_SIGNING_KEYInngest Cloud signing key (required unless INNGEST_DEV=1)
INNGEST_SIGNING_KEY_FALLBACKOptional fallback signing key for rotation
INNGEST_ENVOptional branch environment name
INNGEST_DEVSet to 1 to use the local Inngest dev server
INNGEST_BASE_URLhttp://localhost:8288Local dev server URL (only used with INNGEST_DEV=1)
INNGEST_SERVE_ORIGINPublic backend origin served to Inngest
INNGEST_SERVE_PATH/api/inngestPath where the backend serves the Inngest handler
ANALYTICS_DAILY_RETENTION_DAYS548Days to keep daily channel snapshots before rolling up to weekly
ANALYTICS_POST_RETENTION_DAYS90Days to keep per-post snapshots before pruning
ANALYTICS_ANOMALY_Z3Z-score threshold for anomaly (spike/drop) detection in the daily sweep
ANALYTICS_ANOMALY_COOLDOWN_DAYS3Suppress repeat anomaly notifications for the same (channel, metric) for this many days
COMMENTS_SWEEP_INTERVAL_MINUTES30Minutes between comment collection sweeps
POST_DAYS_BACK30Days back to look for posts when fetching comments
SOCIAL_COMMENT_RETENTION_DAYS90Days before social comments are soft-deleted
AGENT_DIGEST_ENABLEDSet to true to enable the Monday 07:00 ET headless AI digest

AI Budget Enforcement

Per-provider AI budgets are configured per-organization in Settings → AI. The deployment-level kill switch below controls whether provider caps are actually enforced.

VariableDefaultPurpose
AI_PROVIDER_BUDGET_ENFORCEtrue (unset = on)When true, BudgetService.checkBudget enforces per-provider monthly/daily caps from AIOrgProviderConfig. When false, provider budget checks always allow the call (alerts and spend logging continue). Set to false to roll back enforcement during an incident without losing cap configuration or alerts.

API

VariableDefaultPurpose
API_LIMIT600Public API rate limit per hour
OPENAI_APP_CHALLENGEChallenge string for OpenAI apps, served at /.well-known/openai-apps-challenge
MOBILE_APP_SCHEMEpostmill://auth/callbackDeep-link scheme the mobile OAuth callback (GET /auth/oauth-mobile-callback) redirects to
NEXT_PUBLIC_OVERRIDE_BACKEND_URLOverrides the token endpoint advertised in the MCP OAuth discovery document (/.well-known/oauth-authorization-server); falls back to the backend URL
AGENT_MEDIA_SSO_KEYJWT signing key for the optional agent-media.ai SSO integration. Unset = GET /user/agent-media-sso returns { url: null }

Security

VariableDefaultPurpose
ENCRYPTION_KEY(derived from JWT_SECRET)32-byte base64 or hex key for AES-256-GCM encryption at rest. Falls back to SHA-256 of JWT_SECRET if unset. See Security. Warning: introducing ENCRYPTION_KEY on a deployment that previously derived the key from JWT_SECRET makes all existing v2:-prefixed secrets undecryptable (single-key model) — set it from the first deploy, or plan a re-encryption of stored secrets
INTEGRATION_RETURN_URL_ALLOWLISTComma-separated allowed partner origins for integration/enterprise return URLs
SSRF_ALLOWED_PRIVATE_CIDRSComma-separated private CIDRs to allow for self-hosted provider instances (opt-in SSRF exception)
RESTRICT_UPLOAD_DOMAINSWhen set, media attached to a post must contain this domain in its path; saving a post with externally-hosted media is rejected (HTTP 400). Use the domain of your own upload endpoint
NOT_SECUREDDev-only toggle. Skips Helmet, HSTS, CSRF enforcement, and CopilotKit policy gating. Never set in production

Payments (Stripe)

VariableDefaultPurpose
STRIPE_PUBLISHABLE_KEYStripe publishable key
STRIPE_SECRET_KEYStripe secret key
STRIPE_SIGNING_KEYStripe webhook signing secret
STRIPE_DISCOUNT_IDStripe coupon ID. When set, eligible existing paying customers on a monthly plan (no yearly plan, no existing discount) can have the coupon applied to their subscription
ADDON_STORAGE_GB_PER_PACK25Gigabytes added by one storage add-on pack
ADDON_VIDEO_EXPORTS_PER_PACK50Video exports added by one video-exports add-on pack
ADDON_CHANNELS_PER_PACK5Channels added by one channels add-on pack
ADDON_TEAM_SEATS_PER_PACK5Team seats added by one team-seats add-on pack
ADDON_POSTS_PER_PACK500Posts per month added by one posts add-on pack
ADDON_BRAND_KITS_PER_PACK5Brand kits added by one brand-kits add-on pack
ADDON_WEBHOOKS_PER_PACK10Webhooks added by one webhooks add-on pack
ADDON_COMPETITORS_PER_PACK10Competitors added by one competitors add-on pack
ADDON_STORAGE_PRICE_CENTS1900Price per storage add-on pack, in USD cents per month
ADDON_VIDEO_EXPORTS_PRICE_CENTS1900Price per video-exports add-on pack, in USD cents per month
ADDON_CHANNELS_PRICE_CENTS1900Price per channels add-on pack, in USD cents per month
ADDON_TEAM_SEATS_PRICE_CENTS1500Price per team-seats add-on pack, in USD cents per month
ADDON_POSTS_PRICE_CENTS900Price per posts add-on pack, in USD cents per month
ADDON_BRAND_KITS_PRICE_CENTS900Price per brand-kits add-on pack, in USD cents per month
ADDON_WEBHOOKS_PRICE_CENTS900Price per webhooks add-on pack, in USD cents per month
ADDON_COMPETITORS_PRICE_CENTS900Price per competitors add-on pack, in USD cents per month
NEXT_PUBLIC_ADDON_STORAGE_GB_PER_PACK25Browser-visible mirror of ADDON_STORAGE_GB_PER_PACK
NEXT_PUBLIC_ADDON_VIDEO_EXPORTS_PER_PACK50Browser-visible mirror of ADDON_VIDEO_EXPORTS_PER_PACK
NEXT_PUBLIC_ADDON_CHANNELS_PER_PACK5Browser-visible mirror of ADDON_CHANNELS_PER_PACK
NEXT_PUBLIC_ADDON_TEAM_SEATS_PER_PACK5Browser-visible mirror of ADDON_TEAM_SEATS_PER_PACK
NEXT_PUBLIC_ADDON_POSTS_PER_PACK500Browser-visible mirror of ADDON_POSTS_PER_PACK
NEXT_PUBLIC_ADDON_BRAND_KITS_PER_PACK5Browser-visible mirror of ADDON_BRAND_KITS_PER_PACK
NEXT_PUBLIC_ADDON_WEBHOOKS_PER_PACK10Browser-visible mirror of ADDON_WEBHOOKS_PER_PACK
NEXT_PUBLIC_ADDON_COMPETITORS_PER_PACK10Browser-visible mirror of ADDON_COMPETITORS_PER_PACK
NEXT_PUBLIC_ADDON_STORAGE_PRICE_CENTS1900Browser-visible mirror of ADDON_STORAGE_PRICE_CENTS
NEXT_PUBLIC_ADDON_VIDEO_EXPORTS_PRICE_CENTS1900Browser-visible mirror of ADDON_VIDEO_EXPORTS_PRICE_CENTS
NEXT_PUBLIC_ADDON_CHANNELS_PRICE_CENTS1900Browser-visible mirror of ADDON_CHANNELS_PRICE_CENTS
NEXT_PUBLIC_ADDON_TEAM_SEATS_PRICE_CENTS1500Browser-visible mirror of ADDON_TEAM_SEATS_PRICE_CENTS
NEXT_PUBLIC_ADDON_POSTS_PRICE_CENTS900Browser-visible mirror of ADDON_POSTS_PRICE_CENTS
NEXT_PUBLIC_ADDON_BRAND_KITS_PRICE_CENTS900Browser-visible mirror of ADDON_BRAND_KITS_PRICE_CENTS
NEXT_PUBLIC_ADDON_WEBHOOKS_PRICE_CENTS900Browser-visible mirror of ADDON_WEBHOOKS_PRICE_CENTS
NEXT_PUBLIC_ADDON_COMPETITORS_PRICE_CENTS900Browser-visible mirror of ADDON_COMPETITORS_PRICE_CENTS

Build-time/runtime desync warning: the NEXT_PUBLIC_ADDON_* values are baked into the frontend bundle at build time and duplicate the backend defaults. If you change a backend ADDON_* variable, you must rebuild the frontend with matching NEXT_PUBLIC_ADDON_* values — otherwise the UI shows stale pack sizes and prices while the backend enforces the new ones. Changing an ADDON_*_PRICE_CENTS variable creates a new Stripe Price for new purchases only; existing add-on subscriptions keep billing the old price (see Subscriptions & Stripe).

Plan and add-on prices are created dynamically from pricing.ts; no STRIPE_PRICE_* IDs are read from the environment. See Subscriptions & Stripe.

SSO / OIDC login

Login providers are managed by the separate administration app (a distinct repository) and stored encrypted in AuthProviderConfig. This repo reads that config DB-first and ships no /admin frontend or login-provider write API. The variables below remain supported as the bootstrap fallback when no enabled DB config exists for that provider. Email/password (LOCAL) login is always available regardless of provider config.

See OAuth / SSO for a complete setup walkthrough.

VariableDefaultPurpose
POSTMILL_GENERIC_OAUTHfalseSet to true to enable generic OIDC login
POSTMILL_OAUTH_AUTH_URLOIDC provider authorization endpoint
POSTMILL_OAUTH_TOKEN_URLOIDC provider token endpoint
POSTMILL_OAUTH_USERINFO_URLOIDC provider userinfo endpoint
POSTMILL_OAUTH_CLIENT_IDOIDC client ID
POSTMILL_OAUTH_CLIENT_SECRETOIDC client secret
POSTMILL_OAUTH_SCOPEopenid profile emailOIDC scopes to request
NEXT_PUBLIC_POSTMILL_OAUTH_DISPLAY_NAMEName shown on the login button
NEXT_PUBLIC_POSTMILL_OAUTH_LOGO_URLLogo URL shown on the login button

Social login bootstrap

These env vars are used only when no enabled DB config exists for the provider.

VariableDefaultPurpose
GITHUB_CLIENT_IDGitHub OAuth app client ID
GITHUB_CLIENT_SECRETGitHub OAuth app client secret
YOUTUBE_CLIENT_IDGoogle OAuth login client ID. Also the platform channel app for YouTube posting (see below); Settings → Channels remains the per-org BYO override
YOUTUBE_CLIENT_SECRETGoogle OAuth login client secret (dual-use, same as above)
NEYNAR_SECRET_KEYFarcaster (Neynar) login only

Channel OAuth apps (platform click-connect)

Setting a provider's platform OAuth app credentials here gives every organization one-click Connect without per-org key entry. Leaving a provider unset requires each org to add its own app via Settings → Channels. A per-org config always takes precedence. Step-by-step portal setup for each provider: Platform Channel Apps.

These channel variables are the only provider credentials read from the environment. AI provider keys, short-link, and storage credentials are configured per-org in-app and have no env fallback; login-provider variables must never be used as AI credentials.

VariableProvider
X_API_KEY / X_API_SECRETX
LINKEDIN_CLIENT_ID / LINKEDIN_CLIENT_SECRETLinkedIn (also LinkedIn Page)
FACEBOOK_APP_ID / FACEBOOK_APP_SECRETFacebook (also Instagram via Facebook login)
FACEBOOK_CONFIG_IDFacebook Login for Business Configuration ID (optional; required for FBfB-only Meta apps)
INSTAGRAM_APP_ID / INSTAGRAM_APP_SECRETInstagram standalone
DISCORD_CLIENT_ID / DISCORD_CLIENT_SECRETDiscord
SLACK_ID / SLACK_SECRETSlack
TIKTOK_CLIENT_ID / TIKTOK_CLIENT_SECRETTikTok
YOUTUBE_CLIENT_ID / YOUTUBE_CLIENT_SECRETYouTube channel posting (shared with Google login)
PINTEREST_CLIENT_ID / PINTEREST_CLIENT_SECRETPinterest
REDDIT_CLIENT_ID / REDDIT_CLIENT_SECRETReddit
TWITCH_CLIENT_ID / TWITCH_CLIENT_SECRETTwitch
THREADS_APP_ID / THREADS_APP_SECRETThreads
DRIBBBLE_CLIENT_ID / DRIBBBLE_CLIENT_SECRETDribbble
MASTODON_CLIENT_ID / MASTODON_CLIENT_SECRETMastodon (optional — tenants can also just enter their instance hostname)
MEWE_APP_ID / MEWE_API_KEYMewe
KICK_CLIENT_ID / KICK_SECRETKick
GOOGLE_GMB_CLIENT_ID / GOOGLE_GMB_CLIENT_SECRETGoogle Business Profile
NEYNAR_CLIENT_ID / NEYNAR_SECRET_KEYFarcaster (Wrapcast) channel posting
VK_IDVK (id only, no secret)
WHOP_CLIENT_IDWhop (id only, PKCE)
TELEGRAM_TOKENTelegram bot token (token-only)
LINE_CHANNEL_ACCESS_TOKENLINE Messaging API channel access token (token-only)
POSTMILL_OAUTH_CLIENT_ID / POSTMILL_OAUTH_CLIENT_SECRETCustom OAuth channel (shared with generic OIDC login)

Three opt-in flags make a channel app dual-use as a login provider (the matching channel creds above are required; the login page never advertises a provider whose channel app is unconfigured). See Platform Channel Apps → SSO dual-use.

VariableDefaultPurpose
FACEBOOK_SSO_ENABLEDfalseSet to true to add a "Continue with Facebook" login button (requires FACEBOOK_APP_ID / FACEBOOK_APP_SECRET)
X_SSO_ENABLEDfalseSet to true to add a "Continue with X" login button (requires X_API_KEY / X_API_SECRET)
LINKEDIN_SSO_ENABLEDfalseSet to true to add a "Continue with LinkedIn" login button (requires LINKEDIN_CLIENT_ID / LINKEDIN_CLIENT_SECRET)

X channel behaviour

VariableDefaultPurpose
DISABLE_X_ANALYTICSSet to skip X (Twitter) analytics collection
X_ANALYTICS_MAX_PAGE_DEPTH10Maximum timeline pages fetched per X analytics sync
STRIP_LINKS_FROM_X_POSTSSet to strip links from X posts before publishing

Browser extension

VariableDefaultPurpose
EXTENSION_IDChrome extension ID for cookie-based platform integrations (Skool)

Monitoring

VariableDefaultPurpose
NEXT_PUBLIC_SENTRY_DSNSentry DSN for error tracking (frontend)
SENTRY_SPOTLIGHTSet to 1 to enable Spotlight debug proxy
DATAFAST_API_KEYDatafast API key. Posts a register goal to datafa.st when a new user signs up carrying a datafast_visitor_id
FACEBOOK_PIXEL_ACCESS_TOKENMeta Conversions API access token for server-side pixel events
NEXT_PUBLIC_FACEBOOK_PIXELMeta pixel ID (browser-visible). Server-side pixel events fire only when both this and FACEBOOK_PIXEL_ACCESS_TOKEN are set

AI Designer chatbot

VariableDefaultPurpose
AI_DESIGNER_MESH_STOREredisAgent-mesh session/breaker store: redis (default) or postgres (opt-in, runs its own DDL)
AI_DESIGNER_MESH_DATABASE_URLDedicated Postgres URL for the opt-in mesh Postgres store. Never the Prisma DATABASE_URL
AI_DESIGNER_MESH_CONNECTION_LIMIT10Connection pool size for the opt-in mesh Postgres store
AI_DESIGNER_AGENT_REGISTRYDirectory of per-agent YAML files overriding the bundled registry
AI_DESIGNER_AGENT_TIMEOUT_MS120000Per-agent LLM dispatch deadline
AI_DESIGNER_ASSET_TIMEOUT_MS90000Asset (image generation/stock) step deadline
AI_DESIGNER_STUCK_SESSION_MINUTES15Planning/executing sessions untouched longer than this roll back to awaiting_plan
TRUST_PROXY_HOPS(disabled)Number of XFF-appending reverse proxies in front of the backend. Unset/invalid = key on the socket peer address (safe default, not spoofable). When set, per-IP rate buckets key on the Nth-from-right x-forwarded-for entry. Applies to the /ai-designer connect-rate bucket and the HTTP throttler's per-IP buckets (login/register/enterprise/public-report) and the MCP rate limits. Operators behind a proxy must set it to the exact number of appending proxies — overestimating lands in attacker-controlled left-most XFF entries and makes the limits spoofable
SESSION_TTL_MINUTESagent-mesh package env var — no Postmill effect (see caveat below)
ENABLE_CIRCUIT_BREAKERagent-mesh package env var — forced off (see caveat below)
MCP_MAX_RETRIESagent-mesh package env var — no Postmill effect (see caveat below)

SESSION_TTL_MINUTES, ENABLE_CIRCUIT_BREAKER, and MCP_MAX_RETRIES belong to the bundled @reaatech/agent-mesh package's env schema, not to Postmill itself. agent-mesh-env.stash.ts reads, neutralizes, and restores them around the package's import-time env parse: out-of-range values are stashed aside so they cannot crash the boot, and ENABLE_CIRCUIT_BREAKER is forced off because the package's global breaker is keyed by agent id only — Postmill uses the conductor's per-(org, agent) breaker instead. Setting these vars does not configure Postmill behaviour.

Local development feature flags

Set any of these to true or 1 to disable the corresponding subsystem during local development. All features remain enabled by default.

VariablePurpose
DEV_DISABLE_AISkip AI adapter registration
DEV_DISABLE_MCPSkip Mastra/MCP/A2A server startup
DEV_DISABLE_MEDIASkip media-generation adapter registration
DEV_DISABLE_SHORTLINKSSkip short-link adapter registration
DEV_DISABLE_EMAILSkip email-provider adapter registration
DEV_DISABLE_VIDEOSkip video-generation adapter registration
DEV_DISABLE_AGENTSkip agent-graph services
DEV_DISABLE_CRONSkip ScheduleModule.forRoot()
DEV_DISABLE_SENTRYSkip Sentry initialization
DEV_DISABLE_OPENTELEMETRYSkip OpenTelemetry exporter setup
AGENT_SUPERVISOR_ENABLEDtrue (default) uses the supervisor + specialists agent model
CONTENT_PIPELINE_TOTAL_TIMEOUT_MS300000
CONTENT_PIPELINE_AGENT_TIMEOUT_MS120000
BACKEND_URLServer-side backend URL used by the MCP surface. Falls back to NEXT_PUBLIC_BACKEND_URL
MEDIA_MCP_AUDIT_LOG_PATH/tmp/media-mcp-audit.log
SENTRY_PROFILINGSet to 1 to enable browser profiling in dev
FRONTEND_PROFILINGSet to 1 to enable Document-Policy: js-profiling in dev
DEV_SEED_DEMOPopulate the target org with placeholder demo data at backend boot (dev only)
DEV_SEED_DEMO_RESETWipe and reseed demo data (dev only)
DEV_SEED_DEMO_EMAILDemo seeder account email
DEV_SEED_DEMO_PASSWORDDemo seeder account password

Production, scaling & observability

VariableDefaultPurpose
CONFIG_CHECK_STRICTFail fast on fatal-missing secrets even in dev
BACKEND_LISTEN_HOST(Node default)Explicit bind host for the backend HTTP server (e.g. 0.0.0.0 to force IPv4 in CI/containers). Unset = Node's default bind address
COLLAB_SINGLE_INSTANCEtrueCollaboration websocket keeps Yjs state in memory; must be true unless COLLAB_REDIS_ADAPTER is set
COLLAB_REDIS_ADAPTERReserved for the future Yjs-over-Redis adapter
OUTBOUND_HTTP_TIMEOUT_MS30000Bound provider and webhook calls
WEBHOOK_TIMEOUT_MS10000Outbound webhook delivery timeout
WEBHOOK_SIGNING_SECRETHMAC secret for X-Postmill-Signature. When unset, derives from JWT_SECRET
OTEL_EXPORTER_OTLP_ENDPOINTOTLP/HTTP tracing endpoint. Off unless set
OTEL_SERVICE_NAMEpostmill-backendOpenTelemetry service name
DATABASE_CONNECTION_LIMITPrisma connection pool size appended to DATABASE_URL
DATABASE_POOL_TIMEOUTPrisma pool timeout appended to DATABASE_URL
ALLOW_DESTRUCTIVE_SCHEMAfalseAllow prisma db push to perform destructive diffs
ERRORS_RETENTION_DAYS90Retention for Errors rows
NOTIFICATIONS_RETENTION_DAYS180Retention for Notifications (+ NotificationRead)
MULTIPART_UPLOAD_RETENTION_DAYS7Retention for abandoned multipart uploads
MASTRA_TRACE_RETENTION_DAYS30Retention for Mastra traces/scorers
SOFT_DELETE_RETENTION_DAYS30Hard-purge window for soft-deleted posts/files
IP_RETENTION_DAYS90Null User/Session IP and agent after this window
AI_DESIGNER_SESSION_RETENTION_DAYS90Retention for AI Designer chat sessions

Stock media

VariableDefaultPurpose
UNSPLASH_ACCESS_KEYUnsplash API access key for stock photos
PEXELS_API_KEYPexels API key for stock videos
PIXABAY_API_KEYPixabay API key for vectors/illustrations
GIPHY_API_KEYGIPHY API key for stickers
JAMENDO_CLIENT_IDJamendo API client ID for stock audio
JAMENDO_CLIENT_SECRETJamendo API client secret

Iconify (SVG icons) does not require an API key.

Content Packs

Premium stock sources are configured per-organization in-app via Settings → Content Packs. BYOK packs take precedence over the free catalogs above for the capabilities they support. See Storage and the user-facing settings docs.

Video rendering

See Video Rendering for the full list of VIDEO_RENDER_* variables.

Headless Chromium (Puppeteer)

VariableDefaultPurpose
PUPPETEER_EXECUTABLE_PATH(bundled Chromium)Path to a system/distro Chromium binary for design frame capture (used by the render-worker container). Unset = Puppeteer's bundled Chromium
PUPPETEER_DISABLE_SANDBOXfalseSet to true to launch the campaign-report PDF Chromium with --no-sandbox (for CI/containers without user namespaces; also disabled automatically when CI=true). Production defaults to sandboxed mode

Verified against v1.0.0

The AI-native social media management platform — postmill.ai