> ## Documentation Index
> Fetch the complete documentation index at: https://docs.postqueen.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Configuration Reference

> Environment variables reference for PostQueen

She reads everything she needs from environment variables. Change one and restart her
before you expect it to take effect. Under Docker Compose that means
`docker compose down` followed by `up`, because a plain restart reuses the container and
its old environment.

<Note>
  **`NEXT_PUBLIC_` variables do not need a rebuild here.** That is the usual rule for a
  Next.js app, and it does not apply to this image. Her layouts are server rendered on every
  request and hand these values to the browser at runtime, so setting one in Compose and
  restarting is enough. The single genuine exception is `NEXT_PUBLIC_VERSION`, which is baked
  in when the image is built.
</Note>

Start from the [example env file](https://github.com/GkhanKINAY/postqueen-app/blob/main/.env.example)
in the app repository, which is the file to copy on a fresh install. This page then groups the
variables by purpose and explains what each one does. Neither list is a strict superset of the
other, so if you meet a variable in one and not the other, it is real either way.

<Warning>
  Variables marked **Required** are checked on boot, but only warned about. The check runs
  *after* the server is already listening and every problem it finds is logged as a
  `Configuration issue` line. Nothing throws and nothing exits.

  So a running container is not proof that the configuration is right. Read the first
  minute of `docker compose logs` on a fresh deployment and look for that phrase.
</Warning>

## Required core

The boot check treats eight variables as non-optional: the six below, plus
[`MAIN_URL`](#main-url) and [`STORAGE_PROVIDER`](#storage-provider), which are listed
under their own sections further down.

### `DATABASE_URL` <sup>Required</sup>

PostgreSQL connection string used by Prisma.

```
DATABASE_URL="postgresql://postqueen-user:postqueen-password@localhost:5432/postqueen-db-local"
```

### `REDIS_URL` <sup>Required</sup>

Redis connection string used for queues, rate limiting, and short-lived caches.

```
REDIS_URL="redis://localhost:6379"
```

### `JWT_SECRET` <sup>Required</sup>

A long random string used to sign session JWTs.

It has a second job that decides how dangerous rotating it is. When `ENCRYPTION_KEY` is unset, this same value also encrypts the secrets PostQueen holds for you: your API keys, the OAuth client secrets and codes she issues, saved provider credentials, and cookie-based logins such as Skool. Change it in that state and those rows can no longer be decrypted, so the connections that depend on them break and re-entering each one by hand is the only way back.

<Note>
  **What this does not cover.** A channel's own access and refresh tokens, the ones a social network hands back at the end of its OAuth flow, are stored as the network returned them. They are not encrypted with this key, so rotating it does not invalidate them, and the database is the security boundary for those. Treat a database dump accordingly. [Compliance](/compliance) says the same thing from the other direction.
</Note>

Read [`ENCRYPTION_KEY`](#encryption-key) before you rotate this. Setting it is what makes rotating this variable safe, and on a fresh install it costs you nothing to do now.

### `ENCRYPTION_KEY`

The key used to encrypt stored secrets: API keys, the OAuth client secrets and codes PostQueen issues, saved provider credentials and cookie logins. Unset, it falls back to `JWT_SECRET`, which is why every install that has never set it has the two jobs welded together. It does not cover a channel's own access and refresh tokens, as described above.

Set it once, to the value `JWT_SECRET` currently holds. Nothing re-encrypts and nothing breaks, because the fallback was already using that value. What changes is that `JWT_SECRET` becomes a signing key you can rotate whenever you like, and the worst it can then do is log everyone out.

### `PREVIOUS_ENCRYPTION_KEY`

The old value, while you are rotating `ENCRYPTION_KEY`. Decryption tries the current key first and falls back to this one, so rows written under the old key keep opening.

Rotate in this order: copy the current key into `PREVIOUS_ENCRYPTION_KEY`, put the new key in `ENCRYPTION_KEY`, restart. Leave both set afterwards. A row is only rewritten under the new key when something happens to rewrite it, so a refresh token for a channel nobody touches stays on the old key indefinitely, and dropping `PREVIOUS_ENCRYPTION_KEY` too early is what loses it.

### `FRONTEND_URL` <sup>Required</sup>

The URL the **browser** uses to reach the PostQueen frontend. Used as the OAuth redirect base and for email links.

```
FRONTEND_URL="https://postqueen.example.com"
```

### `NEXT_PUBLIC_BACKEND_URL` <sup>Required</sup>

The URL the **browser** uses to reach the PostQueen backend. What goes here depends on how you
deployed her, and the two shapes are not interchangeable.

**One address, which is what the official image and Compose file do.** A proxy inside the
container sends anything under `/api` to the backend, so the backend is a path on the same
origin:

```
NEXT_PUBLIC_BACKEND_URL="https://postqueen.example.com/api"
```

**Separate hostnames**, which applies when you run the frontend and backend as separate
services, from source or in Kubernetes:

```
NEXT_PUBLIC_BACKEND_URL="https://api.postqueen.example.com"
```

<Warning>
  Dropping the `/api` suffix on a Compose install is a common mistake. Requests then land on the
  frontend instead of the backend and everything after sign-in fails. If you are unsure which
  shape you have, check the shipped `docker-compose.yaml`: it uses the `/api` form.
</Warning>

Note that a trailing slash on any of these URLs is reported as a configuration issue on boot.

### `BACKEND_INTERNAL_URL` <sup>Required</sup>

The URL the **frontend server** uses to reach the backend, server to server. This is not a
public address. On the official image both run in the same container, so it stays
`http://localhost:3000` no matter what your domain is. In a split deployment it is the
backend's internal address, such as `http://backend:3000`.

***

## Application behavior

<Warning>
  **Writing `false` turns most of these on.** Only five variables compare against the literal
  string `true`: `DISABLE_REGISTRATION`, `PASSWORDLESS_LOGIN`, `DISABLE_SSRF_PROTECTION`,
  `TEMPORAL_TLS` and `EMAIL_SECURE`. Every other switch on this page is read as
  `!!process.env.NAME`, and in JavaScript the string `"false"` is truthy.

  So for `IS_GENERAL`, `POSTQUEEN_GENERIC_OAUTH`, `DISABLE_IMAGE_COMPRESSION`, `RUN_CRON`
  and their neighbours: set the variable to enable, and **remove the line** to disable.
  Setting it to `false`, `0` or `no` enables it just as surely as `true` does.
</Warning>

### `DISABLE_REGISTRATION`

Set to `true` and only the first account can be created. After that, sign-up is refused. Useful for self-host where you want full control.

It gates **account creation only**, never signing in. Everyone who already has an account keeps logging in normally, because the check is reached only on the branch that would create a new user.

<Note>
  One exception is worth knowing before you rely on this. Your own OIDC provider is **exempt**: a person who signs in through it for the first time still gets an account, even with this set. GitHub and Google are not exempt. So if your goal is to close the door completely, close it at your identity provider as well.
</Note>

### `API_LIMIT`

Per-hour limit on the public-API create-post endpoint. Defaults to `90`. Channel and post quotas are tiered separately by plan.

### `RUN_CRON`

When set, the backend process runs the scheduled-task workers. Leave unset on API-only instances when workers are deployed separately.

### `RESTRICT_UPLOAD_DOMAINS`

A single substring that every media URL in a `POST /public/v1/posts` body must contain, usually your own host or CDN. Unset, any URL passes.

Two things about it surprise people. It is **one value, not a list**: the check is a plain substring test against the whole URL, so `a.com,b.com` demands that literal text and nothing ever matches. And it does **not** guard `/public/v1/upload-from-url`, which has its own rules: the URL must end in one of six extensions and must not point at a private address.

### `DISALLOW_PLUS`

When set, any email address containing a `+` is refused at the door. It exists to stop one person opening account after account with plus-addressed aliases.

Read that carefully before you switch it on, because it applies to **signing in as well as signing up**. Anyone who already has an account at an address like `you+postqueen@example.com` is locked out of it the moment this is set. Only email and password are affected: sign-in through GitHub, Google or your own OIDC provider goes around the check entirely, and nothing in the interface changes.

### `IS_GENERAL`

Switches the frontend between routes available to the open-source build (`/launches`) and the hosted build (`/analytics`). Set to `"true"` on self-host.

### `DISABLE_IMAGE_COMPRESSION`

When truthy, the frontend skips client-side image compression on upload. Set this if you need pixel-exact originals at the cost of larger uploads.

### `NOT_SECURED`

<Warning>
  Dev only. Never set in production: it disables security checks that exist for a reason.
</Warning>

### `MAIN_URL`

Primary application URL used for absolute links in some emails and SEO metadata. Falls back to `FRONTEND_URL` when not set.

### `EXTENSION_ID`

The Chrome Extension ID for cookie-based platform integrations (e.g. Skool). Setup: [Chrome Extension guide](/configuration/chrome-extension).

### `MOBILE_APP_SCHEME`

URL scheme used for deep-linking from emails into the mobile app.

***

## Storage

See also: [Cloudflare R2](/configuration/r2) and [Uploads & Storage](/configuration/uploads).

| Variable                              | Purpose                                                                                                                                                                                                                                                                                                                                                                                    |
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `STORAGE_PROVIDER`                    | `local` or `cloudflare`. **Set it explicitly.** Writing falls back to `local`, but the frontend rewrite that serves `/uploads/...` tests for the literal string `local`, so leaving it unset uploads your files successfully and then serves every one of them as a 404.                                                                                                                   |
| `UPLOAD_DIRECTORY`                    | Filesystem path for `local` storage writes.                                                                                                                                                                                                                                                                                                                                                |
| `NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY` | The URL path segment for uploaded files, **with no leading slash**. Defaults to `uploads`. It is joined after a slash that is already there, so `/uploads` gives you `https://host//uploads/...`.                                                                                                                                                                                          |
| `CLOUDFLARE_ACCOUNT_ID`               | R2 account ID.                                                                                                                                                                                                                                                                                                                                                                             |
| `CLOUDFLARE_ACCESS_KEY`               | R2 access key.                                                                                                                                                                                                                                                                                                                                                                             |
| `CLOUDFLARE_SECRET_ACCESS_KEY`        | R2 secret access key.                                                                                                                                                                                                                                                                                                                                                                      |
| `CLOUDFLARE_BUCKETNAME`               | R2 bucket name.                                                                                                                                                                                                                                                                                                                                                                            |
| `CLOUDFLARE_BUCKET_URL`               | Where media is read from. A custom domain on the bucket, the r2.dev development URL, or `https://your-host/api/uploads` to have PostQueen serve it from a private bucket. **Not** the S3 API endpoint, which refuses unsigned reads. Stored with every media row and fetched by the social networks themselves, so the choice matters — see [Uploads and Storage](/configuration/uploads). |
| `CLOUDFLARE_REGION`                   | R2 region (usually `auto`).                                                                                                                                                                                                                                                                                                                                                                |
| `CLOUDFLARE_JURISDICTION`             | Only for buckets created with a jurisdiction, such as `eu` for data residency. Those buckets answer on their own endpoint (`<account>.eu.r2.cloudflarestorage.com`) and are invisible on the account-wide one, so omitting it makes an existing bucket look missing. Leave blank otherwise.                                                                                                |
| `UPLOAD_VIA_SERVER`                   | `true` posts files to the backend, which writes them to storage, instead of the browser uploading straight to the bucket with a presigned URL. Only affects `cloudflare` storage. Costs server bandwidth, but survives anything that rewrites the request in transit — see [Uploads and Storage](/configuration/uploads).                                                                  |
| `UPLOAD_TMP_DIR`                      | Where uploads are spooled while being written to storage. Defaults to the system temp directory. Files are deleted once storage has them.                                                                                                                                                                                                                                                  |

***

## Email

See also: [Email configuration](/configuration/emails).

| Variable             | Purpose                                                                                              |
| -------------------- | ---------------------------------------------------------------------------------------------------- |
| `EMAIL_PROVIDER`     | `resend` or `nodemailer`. There is no default: leave it unset and the install sends no email at all. |
| `RESEND_API_KEY`     | Resend API key, required when `EMAIL_PROVIDER=resend`.                                               |
| `EMAIL_HOST`         | SMTP host, required when `EMAIL_PROVIDER=nodemailer`.                                                |
| `EMAIL_PORT`         | SMTP port.                                                                                           |
| `EMAIL_USER`         | SMTP user.                                                                                           |
| `EMAIL_PASS`         | SMTP password.                                                                                       |
| `EMAIL_SECURE`       | `true` for SMTPS / implicit TLS.                                                                     |
| `EMAIL_FROM_ADDRESS` | From-address used on all outgoing email.                                                             |
| `EMAIL_FROM_NAME`    | From-name used on all outgoing email.                                                                |

<Warning>
  `EMAIL_PROVIDER` decides more than email. It also decides whether a new email-and-password account has to be activated before it can be used: with a provider named, the account is created inactive and waits for the activation link, and with none named it is active immediately.

  The trap is naming a provider you have not finished configuring. `EMAIL_PROVIDER=resend` with no `RESEND_API_KEY` counts as a provider, so every new signup is created inactive, the activation mail fails quietly, and nobody can get in. Either configure the provider fully or leave `EMAIL_PROVIDER` unset. The key on its own changes nothing.
</Warning>

***

## OAuth sign-in (OIDC)

See also: [OAuth configuration](/configuration/oauth).

| Variable                                   | Purpose                                           |
| ------------------------------------------ | ------------------------------------------------- |
| `POSTQUEEN_GENERIC_OAUTH`                  | `true` to enable a generic OIDC sign-in provider. |
| `POSTQUEEN_OAUTH_URL`                      | Base URL of the identity provider.                |
| `POSTQUEEN_OAUTH_AUTH_URL`                 | Authorization endpoint.                           |
| `POSTQUEEN_OAUTH_TOKEN_URL`                | Token endpoint.                                   |
| `POSTQUEEN_OAUTH_USERINFO_URL`             | UserInfo endpoint.                                |
| `POSTQUEEN_OAUTH_CLIENT_ID`                | Client ID issued by the IdP.                      |
| `POSTQUEEN_OAUTH_CLIENT_SECRET`            | Client secret issued by the IdP.                  |
| `NEXT_PUBLIC_POSTQUEEN_OAUTH_DISPLAY_NAME` | Label shown on the sign-in button.                |
| `NEXT_PUBLIC_POSTQUEEN_OAUTH_LOGO_URL`     | Icon shown on the sign-in button.                 |

***

## Temporal (workflow orchestration)

PostQueen uses Temporal for scheduled posts and background workflows.
Self-host deployments need to run a Temporal stack (the official docker-compose
ships with one). The scheduling architecture: [How PostQueen works](/howitworks).

| Variable                     | Purpose                                                                                                                                                                                                                                  |
| ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TEMPORAL_ADDRESS`           | host:port of the Temporal frontend.                                                                                                                                                                                                      |
| `TEMPORAL_NAMESPACE`         | Temporal namespace to run workflows in.                                                                                                                                                                                                  |
| `TEMPORAL_API_KEY`           | API key for Temporal Cloud. Leave unset for self-host.                                                                                                                                                                                   |
| `TEMPORAL_TLS`               | `true` to require TLS to the Temporal frontend.                                                                                                                                                                                          |
| `ORCHESTRATOR_PORT`          | Port the in-process orchestrator binds to.                                                                                                                                                                                               |
| `WORKER_CONCURRENCY_DIVIDER` | Divides each provider's concurrency limit across this many worker servers, so the total never exceeds what the network allows. Set it to the number of servers you run. Leave unset on a single-server install.                          |
| `EXCLUDE_QUEUE`              | Comma separated list of provider queues this server should not process, for example `reddit,x,twitch`. Used to pin a queue to one server when a provider's concurrency limit is too low to split. Only relevant across multiple workers. |

***

## Public API & MCP

| Variable        | Purpose                                                               |
| --------------- | --------------------------------------------------------------------- |
| `MCP_URL`       | URL the frontend uses to advertise the MCP endpoint to clients.       |
| `AGENT_API_KEY` | Shared secret used by the agent runtime to call privileged endpoints. |
| `BACKEND_URL`   | Legacy alias for `NEXT_PUBLIC_BACKEND_URL` in a few server contexts.  |

***

## AI / generation

| Variable               | Purpose                                                              |
| ---------------------- | -------------------------------------------------------------------- |
| `OPENAI_API_KEY`       | OpenAI key used for the copilot and AI image generation.             |
| `OPENAI_APP_CHALLANGE` | Verification challenge string for the OpenAI custom GPT integration. |
| `ELEVENSLABS_API_KEY`  | ElevenLabs API key for voice generation.                             |
| `FAL_KEY`              | fal.ai API key for image/video models.                               |
| `TAVILY_API_KEY`       | Tavily search API key used by the research tool.                     |
| `KIEAI_API_KEY`        | KieAI API key.                                                       |
| `TRANSLOADIT_AUTH`     | Transloadit auth key for video pipelines.                            |
| `TRANSLOADIT_SECRET`   | Transloadit signing secret.                                          |
| `TRANSLOADIT_TEMPLATE` | Transloadit template ID used by the video generator.                 |

***

## Short-link providers

Pick one set. If more than one is configured the first in this order wins silently, and the choice
is made once at startup. Setup walkthrough: [Short Links](/configuration/short-links).

### Dub.co

```env theme={"system"}
DUB_TOKEN=""
DUB_API_ENDPOINT="https://api.dub.co"
DUB_SHORT_LINK_DOMAIN="dub.sh"
```

### Short.io

```env theme={"system"}
SHORT_IO_SECRET_KEY=""
```

### Kutt.it

```env theme={"system"}
KUTT_API_KEY=""
KUTT_API_ENDPOINT="https://kutt.it/api/v2"
KUTT_SHORT_LINK_DOMAIN="kutt.it"
```

### LinkDrip

```env theme={"system"}
LINK_DRIP_API_KEY=""
LINK_DRIP_API_ENDPOINT="https://api.linkdrip.com/v1/"
LINK_DRIP_SHORT_LINK_DOMAIN="dripl.ink"
```

***

## Payments

| Variable                     | Purpose                                                                                     |
| ---------------------------- | ------------------------------------------------------------------------------------------- |
| `STRIPE_PUBLISHABLE_KEY`     | Stripe publishable key.                                                                     |
| `STRIPE_SECRET_KEY`          | Stripe secret key.                                                                          |
| `STRIPE_SIGNING_KEY`         | Stripe webhook signing key for subscription events.                                         |
| `STRIPE_SIGNING_KEY_CONNECT` | Separate signing key for Stripe Connect webhooks, used by the marketplace payout flow.      |
| `STRIPE_DISCOUNT_ID`         | A Stripe coupon ID offered as a win-back discount. Unset means no discount is ever applied. |
| `FEE_AMOUNT`                 | Platform fee taken on marketplace transactions, as a fraction. `0.05` is five percent.      |

Payments are only relevant if you are running PostQueen as a commercial service. A private
self-hosted install needs none of this, and leaving it unset simply means no billing.

***

## Abuse protection

Rate limiting on the sign-in and sign-up endpoints is on by default and needs no configuration.
The `GUARD_*` family tunes it, `TURNSTILE_SITE_KEY` and `TURNSTILE_SECRET` add a captcha, and
`PASSWORDLESS_LOGIN` switches sign-in to emailed one-time codes. All of them, with their
defaults, are on [Abuse Protection and Rate Limits](/configuration/rate-limits).

***

## Deployment identity

Cosmetic links and addresses she shows in the interface. Every one is optional.

| Variable                    | Purpose                                                      |
| --------------------------- | ------------------------------------------------------------ |
| `LEGAL_URL`                 | Where the "terms" and "privacy" links point.                 |
| `REPOSITORY_URL`            | Overrides the source-code link in the interface.             |
| `AFFILIATE_URL`             | Destination of the affiliate link, when you run a programme. |
| `AGENCY_NOTIFICATION_EMAIL` | Address notified about agency directory submissions.         |
| `EXTENSION_STORE_URL`       | Where the "install the extension" button sends people.       |
| `ONBOARDING_VIDEO_URL`      | Video embedded in the first-run experience.                  |

<Info>
  **`AFFILIATE_URL` points at your programme, not ours.** An **Affiliate** entry appears in the left
  rail only when you set it, and it sends people to whatever address you give it. It also needs
  billing switched on, so an install with no Stripe keys never shows it.

  There is no PostQueen-run programme behind this. The rail entry used to point at the vendor's, which
  sent every self-hosted install's users somewhere their operator never chose, and that was removed.
</Info>

***

## Analytics & error tracking

All of this is optional, and a self-hosted install runs perfectly well with none of it set.
Set the keys for the service you already run and leave the rest alone.

| Variable                                              | Purpose                                                                                                                                        |
| ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_SENTRY_DSN`                              | Sentry project DSN. Set it and both frontend and backend report errors there.                                                                  |
| `SENTRY_SPOTLIGHT`                                    | Sends errors to a local Spotlight debugger instead of Sentry. Development only; the official Compose file has a `debug` profile that runs one. |
| `SENTRY_AUTH_TOKEN`, `SENTRY_ORG`, `SENTRY_PROJECT`   | Upload source maps at build time, so stack traces are readable. Only relevant if you build your own image.                                     |
| `NEXT_PUBLIC_POSTHOG_KEY`, `NEXT_PUBLIC_POSTHOG_HOST` | PostHog product analytics. Both are needed.                                                                                                    |
| `PLAUSIBLE_DOMAIN`                                    | Loads the Plausible script for this domain. Privacy friendly and cookie free.                                                                  |
| `NEXT_PUBLIC_GTM_ID`                                  | Google Tag Manager container ID.                                                                                                               |
| `NEXT_PUBLIC_FACEBOOK_PIXEL`                          | Meta Pixel ID for the browser side.                                                                                                            |
| `FACEBOOK_PIXEL_ACCESS_TOKEN`                         | Server side companion to the pixel, for the Conversions API.                                                                                   |
| `DATAFAST_API_KEY`, `DATAFAST_WEBSITE_ID`             | Datafast analytics.                                                                                                                            |
| `DUB_PUBLISHABLE_KEY`                                 | Dub's client side analytics, separate from `DUB_TOKEN` used for short links.                                                                   |
| `NEXT_PUBLIC_TRACKING_TRIAL`                          | Fires a conversion event when a trial starts. Relevant to the hosted product rather than self-hosting.                                         |

All of these are read at runtime, so setting one and recreating the container is enough. The
build-time exceptions are the three Sentry source map variables, which only matter when you
build the image yourself.

***

## Misc frontend

| Variable                               | Purpose                                                                                                                                                                                                                                                                                                                |
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `NEXT_PUBLIC_POLOTNO`                  | Polotno API key for the image editor.                                                                                                                                                                                                                                                                                  |
| `NEXT_PUBLIC_VERSION`                  | Version string shown in the footer.                                                                                                                                                                                                                                                                                    |
| `NEXT_PUBLIC_APP_VERSION`              | Mobile/web version label.                                                                                                                                                                                                                                                                                              |
| `CHATBASE_BOT_ID` and `CHATBASE_TOKEN` | The in-app support widget. It loads only when both are set, and the bot has to be one you own: its agent can offer the signed-in user a refund on their own subscription, behind a confirmation dialog showing the amount. Leave both unset and no widget loads.                                                       |
| `DEV_BACKEND_PROXY`                    | Local development only. Makes the Next dev server proxy `/api` to a backend running elsewhere, such as a Compose stack on port 4007, so you can work on the frontend alone. Pair it with `NEXT_PUBLIC_BACKEND_URL="/api"`. Ignored in the built image.                                                                 |
| `NEXT_PUBLIC_OVERRIDE_BACKEND_URL`     | Narrower than the name suggests. It replaces `NEXT_PUBLIC_BACKEND_URL` in one place only: the `token_endpoint` the MCP server advertises in its OAuth discovery document. Set it when your MCP clients have to reach that endpoint at a different address, such as through a tunnel. Nothing else in the app reads it. |

***

## Social provider keys

You came here to find out which variable belongs to which network. This table is
that lookup, and the setup page in the third column is where the key-by-key
walkthrough lives.

Not every network appears below, and that is the point. Eight of them ask for no
environment variable at all, because they take their credentials in the connect
form itself. You type the key once in Add Channel and never open your `.env` file
for them:

* Bluesky
* Dev.to
* Hashnode
* Lemmy
* Listmonk
* Medium
* Nostr
* WordPress

Moltbook needs no environment variable either. It has a connect screen of its own
that registers the channel and issues its key for you.

Every network in [Providers](/providers/overview) connects from Add Channel whether
or not it has a row in this table.

| Provider                  | Variables                                                                               | Setup                                                                                                                                                |
| ------------------------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| X (Twitter)               | `X_API_KEY`, `X_API_SECRET`, `X_URL`, `DISABLE_X_ANALYTICS`, `STRIP_LINKS_FROM_X_POSTS` | [/installation/providers/x-twitter](/installation/providers/x-twitter)                                                                               |
| LinkedIn                  | `LINKEDIN_CLIENT_ID`, `LINKEDIN_CLIENT_SECRET`                                          | [/installation/providers/linkedin](/installation/providers/linkedin), [/installation/providers/linkedin-page](/installation/providers/linkedin-page) |
| Facebook / Instagram (FB) | `FACEBOOK_APP_ID`, `FACEBOOK_APP_SECRET`                                                | [/installation/providers/facebook](/installation/providers/facebook), [/installation/providers/instagram](/installation/providers/instagram)         |
| Instagram (Standalone)    | `INSTAGRAM_APP_ID`, `INSTAGRAM_APP_SECRET`                                              | [/installation/providers/instagram](/installation/providers/instagram)                                                                               |
| Threads                   | `THREADS_APP_ID`, `THREADS_APP_SECRET`                                                  | [/installation/providers/threads](/installation/providers/threads)                                                                                   |
| YouTube                   | `YOUTUBE_CLIENT_ID`, `YOUTUBE_CLIENT_SECRET`                                            | [/installation/providers/youtube](/installation/providers/youtube)                                                                                   |
| Google My Business        | `GOOGLE_GMB_CLIENT_ID`, `GOOGLE_GMB_CLIENT_SECRET`                                      | [/installation/providers/google-my-business](/installation/providers/google-my-business)                                                             |
| TikTok                    | `TIKTOK_CLIENT_ID`, `TIKTOK_CLIENT_SECRET`                                              | [/installation/providers/tiktok](/installation/providers/tiktok)                                                                                     |
| Reddit                    | `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`                                              | [/installation/providers/reddit](/installation/providers/reddit)                                                                                     |
| Pinterest                 | `PINTEREST_CLIENT_ID`, `PINTEREST_CLIENT_SECRET`                                        | [/installation/providers/pinterest](/installation/providers/pinterest)                                                                               |
| Discord                   | `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN_ID`                    | [/installation/providers/discord](/installation/providers/discord)                                                                                   |
| Slack                     | `SLACK_ID`, `SLACK_SECRET`, `SLACK_SIGNING_SECRET`                                      | [/installation/providers/slack](/installation/providers/slack)                                                                                       |
| Telegram                  | `TELEGRAM_TOKEN`, `TELEGRAM_BOT_NAME`                                                   | [/installation/providers/telegram](/installation/providers/telegram)                                                                                 |
| Mastodon                  | `MASTODON_URL`, `MASTODON_CLIENT_ID`, `MASTODON_CLIENT_SECRET`                          | [/installation/providers/mastodon](/installation/providers/mastodon)                                                                                 |
| Dribbble                  | `DRIBBBLE_CLIENT_ID`, `DRIBBBLE_CLIENT_SECRET`                                          | [/installation/providers/dribbble](/installation/providers/dribbble)                                                                                 |
| Farcaster                 | `NEYNAR_CLIENT_ID`, `NEYNAR_SECRET_KEY`                                                 | [/installation/providers/farcaster](/installation/providers/farcaster)                                                                               |
| MeWe                      | `MEWE_HOST`, `MEWE_APP_ID`, `MEWE_API_KEY`                                              | [/installation/providers/mewe](/installation/providers/mewe)                                                                                         |
| Twitch                    | `TWITCH_CLIENT_ID`, `TWITCH_CLIENT_SECRET`                                              | [/installation/providers/twitch](/installation/providers/twitch)                                                                                     |
| Kick                      | `KICK_CLIENT_ID`, `KICK_SECRET`                                                         | [/installation/providers/kick](/installation/providers/kick)                                                                                         |
| VK                        | `VK_ID`                                                                                 | [/installation/providers/vk](/installation/providers/vk)                                                                                             |
| Whop                      | `WHOP_CLIENT_ID`                                                                        | [/installation/providers/whop](/installation/providers/whop)                                                                                         |
| Tumblr                    | `TUMBLR_CLIENT_ID`, `TUMBLR_CLIENT_SECRET`                                              | [/installation/providers/tumblr](/installation/providers/tumblr)                                                                                     |
| GitHub (sign-in)          | `GITHUB_CLIENT_ID`, `GITHUB_CLIENT_SECRET`                                              | [/configuration/oauth](/configuration/oauth)                                                                                                         |

<Note>
  Two variable names break the pattern and cost people an afternoon. Kick signs its
  secret as `KICK_SECRET`, not `KICK_CLIENT_SECRET`. Dribbble spells its variables
  with three `b` characters, `DRIBBBLE_CLIENT_ID` and `DRIBBBLE_CLIENT_SECRET`, while
  the network itself is spelled with two.
</Note>

***

## Product newsletter sender

This block trips people up, so read it before you copy anything into your `.env`
file. These variables have nothing to do with posting. They subscribe each new
PostQueen signup to a mailing list you own, and that is all they do.

When someone registers, PostQueen calls one newsletter sender with their email
address. It picks Beehiiv if `BEEHIIVE_API_KEY` is set, otherwise Listmonk if
`LISTMONK_API_KEY` is set, otherwise nobody. Leave all of them unset and no
signup email is ever forwarded anywhere.

| Variable                       | Purpose                                                          |
| ------------------------------ | ---------------------------------------------------------------- |
| `BEEHIIVE_API_KEY`             | Beehiiv API key. Setting this selects Beehiiv as the sender.     |
| `BEEHIIVE_PUBLICATION_ID`      | Beehiiv publication the new signup is added to.                  |
| `LISTMONK_DOMAIN`              | Base URL of your Listmonk server, used for the subscriber call.  |
| `LISTMONK_USER`                | Listmonk API user, sent as HTTP basic auth.                      |
| `LISTMONK_API_KEY`             | Listmonk API token. Setting this selects Listmonk as the sender. |
| `LISTMONK_LIST_ID`             | Numeric ID of the list the new signup is added to.               |
| `LISTMONK_WELCOME_TEMPLATE_ID` | Template ID for the welcome email sent to that new subscriber.   |

<Warning>
  `LISTMONK_*` does **not** configure the Listmonk channel. The channel you connect
  in Add Channel to publish campaigns asks for its URL, username and password in the
  connect form, and it ignores these variables completely. Setting them will not make
  the channel appear, and leaving them unset will not stop it working. See
  [Listmonk](/providers/listmonk) for the channel. There is no Beehiiv channel at
  all: Beehiiv exists only as a sender for the list above.
</Warning>

***

## Runtime & build

These are read from the environment but are typically set by your runtime,
hosting platform, or framework rather than set by hand.

| Variable                | Purpose                                                                                                                                                                                                                                                                                               |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PORT`                  | Port the backend HTTP server binds to. Defaults to `3000`.                                                                                                                                                                                                                                            |
| `ORCHESTRATOR_PORT`     | Port the orchestrator binds to. Defaults to `3002`.                                                                                                                                                                                                                                                   |
| `PRISMA_MIGRATE`        | Apply the schema with `prisma migrate deploy` instead of `db push`. Unset, empty, `false` or `0` keeps `db push`; any other value switches to migrations, so a typo errs toward the safer path. See [Backups and Upgrades](/installation/backups-and-upgrades) before switching an existing database. |
| `NX_ADD_PLUGINS`        | Set to `false` in the shipped configuration. A build-tooling flag, not something to change.                                                                                                                                                                                                           |
| `TZ`                    | Process timezone. The backend forces this to `UTC` on boot.                                                                                                                                                                                                                                           |
| `NODE_ENV`              | Standard Node environment flag (`development` / `production`). Toggles dev-only behavior like sourcemaps.                                                                                                                                                                                             |
| `NEXT_RUNTIME`          | Set by Next.js (`nodejs` / `edge`) to select the instrumentation hook. Framework-injected. Do not set manually.                                                                                                                                                                                       |
| `VERCEL_GIT_COMMIT_SHA` | Commit SHA used as the Sentry release tag on the frontend. Injected by Vercel.                                                                                                                                                                                                                        |
| `GITHUB_SHA`            | Fallback commit SHA for the Sentry release tag when not on Vercel. Injected by GitHub Actions.                                                                                                                                                                                                        |
