Skip to main content
Problems that affect everyone (OAuth connect errors, upload limits, login issues) are in the general troubleshooting section. This page is only the problems you can hit because you run Postiz yourself.

Connect failures

”Invalid state” caused by split domains

Postiz stores the OAuth state token against your session cookie. If FRONTEND_URL and NEXT_PUBLIC_BACKEND_URL resolve to different parent domains (for example app.example.com and api.example.com), the browser treats the backend cookie as third-party and the token does not round-trip, so every connect ends in “Invalid state”. Fix: serve both from the same parent domain, ideally through a single reverse proxy on one hostname. See Reverse proxies.

”Failed to fetch” when connecting a provider

The backend could not reach the provider’s API at the network level. Common causes
  • A self-hosted Mastodon or WordPress instance your backend cannot reach (firewall, missing DNS).
  • The Google Business metrics endpoint blocked by egress filtering.
  • A reverse proxy in front of Postiz rewriting the outbound request.
  • The logs show Error: Blocked IP: the URL resolved to a private IP and Postiz’s SSRF guard rejected it. For trusted private networks, see DISABLE_SSRF_PROTECTION.
Fix
  1. Confirm the backend container can resolve and reach the provider hostname: curl https://provider.example.com/ from inside the container.
  2. Behind a corporate egress proxy, set HTTPS_PROXY and HTTP_PROXY for the backend process.

Everyone is logged out after a restart

JWT_SECRET signs session tokens. Regenerating it invalidates every existing session. Fix: set JWT_SECRET once during initial deployment and do not rotate it lightly. Store it in a secret manager (Docker or Kubernetes secret, AWS/GCP Secret Manager, Doppler, 1Password) or an untracked .env file on the host. Do not commit it to version control, including “private” deploy repos, since anyone with repo access then holds the signing key for every session token Postiz has ever issued.

”Origin not allowed” on the calendar

The backend’s CORS allowlist is built from FRONTEND_URL, plus MAIN_URL if set. Accessing Postiz on a hostname or port outside that list makes the backend reject the call. Fix: make FRONTEND_URL exactly match the URL you use in the browser, protocol and port included.

Login endpoints return 404 when logged in

If you are logged in and still get 404 from GET /api/health or GET /auth/me, NEXT_PUBLIC_BACKEND_URL does not match the URL your browser uses to reach the backend. Mismatched protocols or ports break the session cookie.

pnpm heap out of memory during install

The Postiz monorepo is large enough that the default Node heap can overflow during pnpm install on small VMs. Fix
If 4 GB still isn’t enough and your host has the RAM to spare, raise it to --max-old-space-size=8192. Don’t set the heap higher than your VM’s available memory, that just trades OOM in Node for OOM-killed by the kernel. If you’re stuck on a 2 GB VM, build the image elsewhere and deploy the prebuilt artifact instead. See System requirements for minimum RAM.

postgres ECONNREFUSED

Backend can’t reach Postgres. Checklist
  1. DATABASE_URL host: is it localhost when the backend is in a container that doesn’t share host networking? Use the service name (postgres) or the container IP instead.
  2. Postgres ready? docker compose logs postgres should show database system is ready to accept connections.
  3. Port mapping: if you exposed Postgres only on the Docker network, external psql won’t work but the backend will. That’s fine.

”Hide a provider from the UI”

There is no env-var-based way to hide a provider from the frontend today. The only mechanism is to set the per-organisation disabled field on the Integration row in the database after a channel is connected. We’re tracking the request, see the GitHub issues tagged enhancement.

Default ports

It depends on how you’re running Postiz. Official Docker image (ghcr.io/gitroomhq/postiz-app): the container bundles frontend + backend and exposes a single port 5000. The official compose maps that to host 4007:5000, so you reach Postiz at http://localhost:4007/. Running from source (pnpm dev / pnpm start): frontend listens on 4200, backend on 3000 (overridable via PORT). Connect a reverse proxy in front to give your users a single URL, see Reverse Proxies. Temporal listens on 7233 (gRPC) and the Temporal UI on 8080 in either mode.

Mounting the uploads volume

If STORAGE_PROVIDER=local, point UPLOAD_DIRECTORY at the path Postiz should write to, mount that path into your container, and also set NEXT_PUBLIC_UPLOAD_STATIC_DIRECTORY so the frontend knows where the files are served from. The Next.js config rewrites /uploads/:path* to /api/uploads/:path* when local storage is active. See Uploads & storage. For multi-container deployments, either share the same volume across all containers that need to read or write, or move to Cloudflare R2.

Can I deploy Postiz on Vercel?

Frontend: yes, it’s a standard Next.js app and deploys cleanly to Vercel. Backend: no. The Postiz backend is a NestJS server that also embeds a Temporal worker. It needs a long-running Node host (Fly.io, Render, Railway, a VM, a Kubernetes pod, etc.), serverless functions won’t work.

Email isn’t sending

By default, Postiz uses Resend (EMAIL_PROVIDER=resend) and only becomes active when RESEND_API_KEY is set. To use SMTP instead, set EMAIL_PROVIDER=nodemailer and provide EMAIL_HOST, EMAIL_PORT, EMAIL_USER, EMAIL_PASS, EMAIL_SECURE. See Email configuration. If no email provider is configured, user activation emails won’t go out, but Postiz auto-activates users in that mode, so signup still works.

Temporal isn’t reachable

Since v2.12.0, Postiz schedules posts through Temporal. The backend needs to reach the Temporal frontend on TEMPORAL_ADDRESS. If you’re upgrading from v1.x, follow the migration guide it walks through running both stacks side by side while you copy Postgres data over.