
Deploy Postiz | (Just Updated) Buffer Alternative, Admin Seeded and API Actually Works
Self-hosted social scheduler. Admin seeded, API works, media persists
postgres
Just deployed
/var/lib/postgresql
elasticsearch
Just deployed
/usr/share/elasticsearch/data
redis
Just deployed
/data
Just deployed
/uploads
temporaldb
Just deployed
/var/lib/postgresql
Just deployed
Deploy and Host Postiz on Railway
Postiz is an open-source social media scheduling tool — a self-hosted Buffer / Hootsuite alternative. One calendar for ~20 platforms (X, LinkedIn, Instagram, Threads, TikTok, YouTube, Bluesky, Mastodon, Reddit, Discord, Slack, Telegram and more), an AI copilot for drafting, a media library, analytics, a team workspace and a public API.
This template deploys Postiz v2.23.0 with its whole Temporal stack wired up, an admin account that already exists when the URL goes live, and the Railway-specific defects that break the other listings in this category already fixed. Every claim below was reproduced against the upstream image before this template was published.
About Hosting
The API works. Postiz's own nginx has a hardcoded listen 5000; and no templating; its
backend binds process.env.PORT || 3000. Railway injects PORT, so on a stock deploy the backend
takes the edge port and nginx is unreachable. Measured on v2.23.0 with PORT=8080:
GET / -> 200 App is running! <- the backend, not the app UI
GET /api/ -> 404 Cannot GET /api/
GET /uploads/ -> 404 Cannot GET /uploads/…
The instance is green, the healthcheck passes, and there is no product. This template's image
rewrites nginx's listener to the injected port at boot and pins the backend back to 3000, then
health-checks /api/ — with the trailing slash, because location /api/ does not match /api.
Nobody else can claim your instance. Postiz's canRegister() returns true for every caller
until an organization exists, so a fresh deploy is claimable by whoever loads the URL first —
anonymous POST /api/auth/register returns 200 with an auto-activated account. This template
generates an admin password, seeds the organization and its SUPERADMIN through Postiz's own
registration handler on loopback while nginx is still down, verifies the login, and only then
opens the port. On the live deploy:
[railway-entrypoint] seeded organization 'My Workspace' with SUPERADMIN admin@postiz.local
[railway-entrypoint] verified: admin@postiz.local can log in with the current POSTIZ_ADMIN_PASSWORD
[railway-entrypoint] registration open to the public: {"register":false}
POST /api/auth/register (anonymous) -> 400 Registration is disabled
POST /api/auth/login (admin) -> 200 {"login":true}
Log in with POSTIZ_ADMIN_EMAIL (default admin@postiz.local) and the generated
POSTIZ_ADMIN_PASSWORD, both in the service's variables.
Brute force gets throttled. Postiz applies no rate limit to its auth routes: 20 wrong
passwords answer 400 twenty times. This deployment answers 400 x4 then 429 x16 on the same
burst, keyed on the real client address (the first entry of X-Forwarded-For, because Railway's
edge hop rotates per request).
Your media survives a redeploy. Uploads live on a volume at /uploads, and nginx serves them
from there. Verified end to end on this template: an image uploaded through the app was still
served with the same bytes after a full redeploy.
No unauthenticated Temporal console. Postiz 2.x schedules through Temporal, and Temporal's web UI has no authentication of its own. Other listings publish it on a public domain, which exposes every workflow — scheduled post bodies and connected accounts included — to anyone with the URL. This template simply does not ship it; scheduling does not need it.
Also: every image is pinned (Postiz v2.23.0 by digest, Temporal 1.28.1, Elasticsearch
7.17.27), because Postiz runs prisma db push --accept-data-loss on every boot and a floating
tag makes each redeploy an unrequested schema change. Elasticsearch's JVM heap is sized from the
container's cgroup limit instead of a fixed -Xmx512m.
Why Deploy Postiz on Railway?
Scheduling is an always-on workload: a Temporal worker per platform, a poller for analytics, and a
media store that has to be there when a post fires. Railway gives it private networking between
the six services, persistent volumes, and per-second usage billing instead of a fixed VPS —
without asking you to run docker compose on a box you have to patch. Deploys are one click, the
public domain and TLS come with it, and every service scales on its own.
Common Use Cases
- Run your own Buffer/Hootsuite replacement for a personal brand or a client roster, with no per-seat or per-channel pricing.
- Give an agency team one shared calendar and approval workflow across every client account.
- Drive scheduling from code or an AI agent through Postiz's public API, using the deployment as the posting backend for a product of your own.
Dependencies for Postiz Hosting
Postiz needs Postgres, Redis and — from 2.x onward, where BullMQ was removed — a Temporal server,
which itself needs a database and Elasticsearch. This template provisions all of it: postiz,
postgres, redis, temporal, temporaldb and elasticsearch.
Deployment Dependencies
- Postiz: https://github.com/gitroomhq/postiz-app
- Postiz docs: https://docs.postiz.com
- Temporal: https://docs.temporal.io
- Wrapper images and sources: https://github.com/bon5co/postiz-railway
Implementation Details
The admin account is seeded through Postiz's own /auth/register handler on 127.0.0.1 before
nginx starts, so the bcrypt hash, the organization row, the SUPERADMIN role and the encrypted API
key are exactly what the application would have written itself:
[railway-entrypoint] gateway will listen on 8080, auth throttle 10r/m burst 5
[railway-entrypoint] temporal reachable at temporal.railway.internal:7233
[railway-entrypoint] backend answering on 127.0.0.1:3000
[railway-entrypoint] seeded organization 'My Workspace' with SUPERADMIN admin@postiz.local
[railway-entrypoint] gateway up on 8080
On any later boot it leaves the existing organization alone and re-verifies the login. To connect
social channels, add the relevant provider's client ID and secret to the postiz service's
variables (X_API_KEY, LINKEDIN_CLIENT_ID, …) as documented in Postiz's docs — the template
deliberately does not publish them as empty required fields.
Template Content
postgres
postgres:17.10-trixieelasticsearch
ghcr.io/bon5co/postiz-railway-elasticsearch:7.17.27redis
redis:8.2.1temporaldb
postgres:17.10-trixie