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 OAuthstate 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, seeDISABLE_SSRF_PROTECTION.
- Confirm the backend container can resolve and reach the provider
hostname:
curl https://provider.example.com/from inside the container. - Behind a corporate egress proxy, set
HTTPS_PROXYandHTTP_PROXYfor 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 fromFRONTEND_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 fromGET /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
--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
DATABASE_URLhost: is itlocalhostwhen the backend is in a container that doesn’t share host networking? Use the service name (postgres) or the container IP instead.- Postgres ready?
docker compose logs postgresshould showdatabase system is ready to accept connections. - Port mapping: if you exposed Postgres only on the Docker network,
external
psqlwon’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-organisationdisabled
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
IfSTORAGE_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 onTEMPORAL_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.
