Configuration
Postwright is configured with environment variables (for a self-managed deployment) or through the settings UI into an encrypted store (in a packaged install). Here are the ones that matter most, grouped by concern. The installer sets these for you.
Core
| Variable | What it does |
|---|---|
OBSIDIAN_VAULT_PATH | Your content folder. The one truly required setting — unset, Postwright starts in setup mode. |
PORT | Server port (default 8002). |
DB_PATH | Local database file (default ./data/postwright.db). |
Blog publishing
For the Git path (see How publishing works). Point Postwright at the repo your site builds from and enable pushing to trigger your host’s deploy.
| Variable | What it does |
|---|---|
STEP_ZERO_REPO_PATH | Local checkout of the repo your site deploys from. Unset → stub mode (nothing is written). |
STEP_ZERO_PUSH_ENABLED | When true, git-pushes after committing — which triggers your host’s deploy (default false). |
STEP_ZERO_BASE_URL | Your site’s public URL, used for canonical links and to confirm the post went live. |
These STEP_ZERO_* names are legacy (Postwright grew out of that project); a future release renames them. In the packaged product the publisher is chosen per brand — Git, folder, WordPress, Ghost, Beehiiv, webhook, or none.
Blog destinations and cross-posts (Pro)
WordPress, Ghost, Beehiiv, DEV, Hashnode, Kit, and Mailchimp have no environment variables. You set them for each brand in Settings > Brand, and the keys go into the encrypted store. Each has a Test connection button that signs in and checks access without posting anything. A brand with no blog destination can’t publish; choosing no blog is an explicit option.
| Setting | What it does |
|---|---|
| Blog destination: WordPress | The site’s address (https; a site in a subfolder keeps its path), your username, and an application password (WordPress: Users > Profile > Application Passwords). The user needs the Author, Editor, or Administrator role. |
| Blog destination: Ghost | The site’s address and a custom integration’s Admin API key (Ghost: Settings > Integrations; not the Content API key). Ghost(Pro) Starter has no custom integrations. Optionally, the slug of one of its newsletters: Ghost then sends the brand’s newsletter issues to its members. |
| Blog destination: Beehiiv | The Publication ID (it starts with pub_) and an API key, both from Beehiiv’s Settings > API. Beehiiv lets only publications on its Pro or Enterprise plan create posts through its API. Optionally, have Beehiiv email the brand’s newsletter issues to all, free, or premium subscribers. |
| Newsletter: Kit | A Kit API key (Kit: Settings > Developer > API keys, a v4 key), a test tag whose only subscriber is your test address, and optionally a segment or tag to send to and a Classic email template. Kit then sends the brand’s newsletter issues to its subscribers. Set in Settings > Brand > Newsletter. |
| Newsletter: Mailchimp | A Mailchimp API key (Mailchimp: Profile > Extras > API keys; keys made since 22 June 2026 expire after a year) and the audience, optionally a saved segment. The audience needs its postal address and a default From email address and name set in Mailchimp. Set in Settings > Brand > Newsletter. |
| Cross-post to DEV | A DEV API key (dev.to: Settings > Extensions > DEV Community API Keys). |
| Cross-post to Hashnode | The publication (its Hashnode address, such as you.hashnode.dev, or its id) and a personal access token (Hashnode: Account Settings > Developer). Hashnode’s API needs Hashnode Pro on that publication. |
| Back-links on the blog | A switch in Settings > Brand. It needs a Git, folder, WordPress, or Ghost destination; a webhook or Beehiiv destination can’t carry them. |
A WordPress or Ghost key is saved for the site address it was entered with and is only ever sent there. How each destination behaves is in How publishing works, and the sign-in steps are in Connections.
Newsletter
| Variable | What it does |
|---|---|
RESEND_API_KEY | Resend API key. |
RESEND_DOMAIN | Your verified Resend sending domain. |
MAILGUN_API_KEY / MAILGUN_DOMAIN | Use Mailgun instead of Resend, per brand. |
NEWSLETTER_TEST_EMAIL | Where the send-a-test-first preview goes. |
Social platforms
LinkedIn and Facebook post through a signed-in browser profile. Instagram and Threads use Meta tokens. Bluesky, Mastodon, the channels, X, and Pinterest are normally set up from the Connections page, which saves what you enter into the encrypted store; the variables below are for a hand-managed setup. Step-by-step sign-ins are in Connections.
| Variable | What it does |
|---|---|
BROWSER_PROFILE_PATH | Where the logged-in LinkedIn browser session is kept (default ./data/browser-profile). |
FB_PAGE_ID / FB_PAGE_ACCESS_TOKEN | Facebook Page publishing via the Meta Graph API. |
IG_BUSINESS_ACCOUNT_ID / IG_ACCESS_TOKEN | Instagram Reels and feed via the Meta Graph API. |
THREADS_ACCESS_TOKEN / THREADS_USER_ID | Threads via the Threads Graph API. |
YOUTUBE_CLIENT_ID / _SECRET / _REFRESH_TOKEN | YouTube uploads via the YouTube Data API. |
BLUESKY_HANDLE / BLUESKY_APP_PASSWORD | Bluesky. Use an app password, never your account password. BLUESKY_SERVER (default https://bsky.social) is only for an account on its own server, and must be https on the public internet. |
MASTODON_INSTANCE_URL / MASTODON_ACCESS_TOKEN | Mastodon, or a server that speaks its API. The address must be https on the public internet. The token needs the scopes read:accounts, read:statuses, and write:statuses. |
TELEGRAM_CHANNEL_ID / TELEGRAM_CHANNEL_BOT_TOKEN | Broadcasts to a Telegram channel: its @username, or its numeric id for a private channel. The bot must be the channel’s admin with “Post messages”. The token defaults to the approvals bot’s. |
DISCORD_WEBHOOK_URL | Broadcasts to a Discord channel through its webhook. Only Discord’s own webhook addresses are accepted. |
SLACK_BOT_TOKEN / SLACK_CHANNEL_ID | Broadcasts to a Slack channel through your own Slack app: its Bot User OAuth Token (xoxb-…, with chat:write) and the channel’s id. |
X_CLIENT_ID / X_REFRESH_TOKEN / X_USERNAME | X, through your own X developer app. Normally saved by Sign in with X in Connections instead. X replaces the refresh token on every refresh, so a value set here stops working after the first one; prefer the sign-in. |
PINTEREST_APP_ID / PINTEREST_APP_SECRET / PINTEREST_REFRESH_TOKEN | Pinterest, through your own Pinterest app. Normally saved by Sign in with Pinterest in Connections. PINTEREST_SANDBOX=true marks the token as the sandbox’s (an app with Trial access), and PINTEREST_BOARD_ID names the board. |
ASSISTED_EXPIRY_DAYS | How long a post drafted for you to post by hand (a Substack Note) waits for you to paste its address or skip it (default 7, at most 90). |
Substack Notes have no setting to connect: Postwright never posts to Substack. The Connections > Substack Notes (by hand) switch has a Note drafted for you to post yourself. After removing X or Pinterest in Connections, delete its variables from the environment too, or the next start saves them again.
Client review links (Team)
A client’s approval recorded by hand needs no configuration at all. Review links (the page a client opens) need only your public media URL, and nothing else to set.
| Setting | What it does |
|---|---|
| Public base URL | The MEDIA_PUBLIC_BASE_URL address, or Connections > Public media URL. Without it no review-link route exists and the Client reviews page says links are unavailable. |
| Link lifetime | 3 to 30 days, chosen when you send (default 7). The database’s expiry is authoritative, and a link signed for longer than 30 days is refused. |
| Reviewers | Each brand’s list is under Settings > Brand > Client reviewers, up to 25 per brand. Archiving or deleting a reviewer revokes their links. |
| Tunnel check | Run from Approvals > Client reviews and re-run every 6 hours. Links can be sent only while the last check passed, and a pass is kept for 24 hours. It fails if your tunnel forwards more than the review and Reel paths. |
| Signing secret | Generated by Postwright, never read from the environment, and separate from the other link secrets. End all review links in Approvals replaces it and ends every link at once. |
| Demo mode | Never sends a link. The demo brand has two sample reviewers you can see but not change. |
Reviews, their answers, and a copy of the images each revision showed stay on your Mac (at most 40 images and 50 MB per revision). The hold stays in force if a Team license lapses; you can still publish the post anyway from the Schedule page. More in Client reviews.
Agent access (Pro and Team)
Nothing in Postwright’s own environment turns the MCP server on. You create an agent token in Settings > Agent access; Postwright keeps only its SHA-256 hash and shows the token once. Tokens are listed and revoked in the same place. The MCP server process reads two variables from its own environment:
| Variable | What it does |
|---|---|
POSTWRIGHT_AGENT_TOKEN | Required. The pw_agent_… token shown when you create it. |
POSTWRIGHT_URL | Where Postwright runs on this computer (default http://127.0.0.1:8002). Plain http only to this computer; otherwise https to a local reverse proxy under .internal. The server refuses anything else before sending the token. |
An agent token works only on the content, schedule, cadence, and approval routes its scopes allow; never on connections, credentials, settings, auth, or the license; only from this computer, never through your public tunnel; and never to send a newsletter. On a Studio install you can’t create a token. Setup and scopes are in Agent access.
Wake for scheduled posts (every tier)
The optional wake helper has no setting in the environment. You turn it on in Settings > Wake for scheduled posts, and its limits are fixed. What it does and doesn’t do is in Getting started.
| Setting | What it does |
|---|---|
| Wake time | 2 minutes before a scheduled post is due, to the minute. |
| Merged wakes | Wakes less than 10 minutes apart become one. |
| Looks ahead | 7 days. A wake must be more than 10 minutes away, so a post due in under about 12 minutes needs none; Postwright keeps an awake Mac awake for the next 15 minutes anyway. |
| Held at once | The first 12 wakes. |
| Which posts | Scheduled posts that Postwright publishes itself. Not sequence emails or LinkedIn-scheduled video uploads. |
| Variable | What it does |
|---|---|
POSTWRIGHT_WAKE_HELPER | Development only. The absolute path of a locally built and signed helper to use instead of the one inside the installed app. A source checkout, or a Mac that isn’t running macOS, has none. |
A missing or broken helper only shows in the status and never delays a post. Demo mode never runs it and shows sample wakes.
AI & media
| Variable | What it does |
|---|---|
CLAUDE_CLI_PATH | Path to the Claude Code CLI (default claude). Postwright’s drafting uses your existing Claude Code subscription — no separate API key. |
GOOGLE_GENAI_API_KEY | Optional. Powers AI social art (Gemini / Imagen). |
UNSPLASH_ACCESS_KEY / PIXABAY_API_KEY | Optional stock-photo sources for blog hero images. |
Where your keys live
Keys you enter on the Connections page go into an encrypted store on your Mac, not into a settings file. The one thing Postwright keeps in the macOS Keychain is the master key that unlocks that store. A value saved in the app wins over the same setting in .env, and the programs Postwright launches never receive your keys.
The master key is kept per data folder. The packaged install’s folder keeps the original Keychain item; any other folder (a source checkout, a custom DB_PATH) gets its own. If Postwright finds saved credentials but no key that opens them, it stops and tells you rather than making a new key that would orphan them. A Keychain that is locked is waited out for up to 10 minutes, not crashed on.
If you see “found saved credentials but no master key that decrypts them”:
- Unlock your login keychain (open Keychain Access, or log out and back in) and restart Postwright. Choose Always Allow if a prompt appears.
- Did you move or rename the data folder? Move it back, or copy the matching
master.keyinto it. - If the folder holds a
master.keyfrom another install, delete that file. - Restore the Keychain item from a backup, such as Time Machine.
- Last resort: clear only the saved credentials and sign in to each connection again. Your posts, schedule, and settings stay; links already emailed (unsubscribe, preferences, confirmation) stop working. Never delete or move the database itself. The exact steps are in the app’s own configuration reference, and if you are unsure, contact support first.
docs/configuration.md.