Open-source project
gitroomhq/postiz-app avatar
gitroomhq/postiz-app

Postiz self-hosting: three stateful services, one developer app per channel, and a compose file that disagrees with .env.example

📨 The ultimate agentic social media scheduling tool 🤖

36,539 stars7,042 forksTypeScriptAGPL-3.0

At a glance

What is it?
Postiz is an AGPL-3.0 social media scheduler that runs as one Docker image but expects Postgres, Redis, and Temporal behind it, plus a developer app of your own on every platform you want to post to. The code is recent, the port configuration is contradictory, and several advertised features quietly need a third-party account you may not want.
Who is it for?
Postiz fits a team that already has developer apps approved on Meta, YouTube, or TikTok, or a solo operator willing to wait out that review, and it is free forever under AGPL-3.0 if you can run three stateful services. Skip it if you need analytics on day one, AI video clipping without vendor accounts, or a guarantee that nothing leaves your network, because avatars still go through Cloudflare.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The compose file and .env.example point at different ports

Two configuration sources ship in the same repository and they do not agree on where Postiz listens. The compose file pins the public side to 4007 and routes the API through that same port:

yaml
    image: ghcr.io/gitroomhq/postiz-app:latest
    container_name: postiz
    restart: always
    environment:
      MAIN_URL: 'http://localhost:4007'
      FRONTEND_URL: 'http://localhost:4007'
      NEXT_PUBLIC_BACKEND_URL: 'http://localhost:4007/api'
      BACKEND_INTERNAL_URL: 'http://localhost:3000'

The environment example, which points at a configuration reference page, uses 4200 for the frontend and 3000 for the backend:

bash
FRONTEND_URL="http://localhost:4200"
NEXT_PUBLIC_BACKEND_URL="http://localhost:3000"
BACKEND_INTERNAL_URL="http://localhost:3000"

So a value copied from one file into the other breaks callback URLs, and whichever file you happened to open decides whether your frontend is on 4007 or 4200. BACKEND_INTERNAL_URL is 3000 in both, which means the compose file describes two ports at once, one for the browser and one for service to service calls. Pick a single source, then set MAIN_URL, FRONTEND_URL, and NEXT_PUBLIC_BACKEND_URL to the same origin before connecting any channel, since OAuth redirect URIs are computed from these values.

The shipped JWT secret is a punchline and registration is open

The compose file arrives with a placeholder that reads like a joke:

yaml
      JWT_SECRET: 'random string that is unique to every install - just type random characters here!'
      IS_GENERAL: 'true'
      DISABLE_REGISTRATION: 'false'

Copy that block as shipped and every installation signs its tokens with the same secret. The environment example drops the punchline but not the risk, simply asking that the JWT secret be long, and it notes that FRONTEND_URL has to be exactly the URL you access Postiz on. DISABLE_REGISTRATION is false, so the first visitor who reaches the port creates the first account. For a tool that will hold API secrets for X, LinkedIn, Reddit, Facebook, Threads, and Apple, that first hour is the part worth getting right: replace the secret, set DISABLE_REGISTRATION to true, create the owner account yourself, and only then put the port somewhere reachable. Nothing in the compose file does this for you.

STORAGE_PROVIDER local does not make account avatars local

Local storage is the default in the compose file, with Cloudflare left commented out:

yaml
      STORAGE_PROVIDER: 'local'
      UPLOAD_DIRECTORY: '/uploads'
      NEXT_PUBLIC_UPLOAD_DIRECTORY: '/uploads'

That covers uploaded media, which lands under /uploads inside the container. The environment example complicates it with a line worth reading twice: Cloudflare is currently required to save things like social media avatars for accounts. So an install that never touches R2 can still be sending profile images to a third party, and a self-hoster whose reason for choosing this project is that data never leaves their environment has a gap the storage setting does not close. The same file also gates two optional media features on STORAGE_PROVIDER being set to cloudflare, which makes the Cloudflare switch the on or off control for the entire advanced media pipeline rather than a storage preference.

Video clipping needs RunPod, OpenAI, Deepgram, and an Oxylabs account

The AI features look uniform from the outside and are not. The self-hosted column says the AI copilot, images, and videos are available if you bring your own OpenAI and other provider keys, with no quota and the provider billed to you. Video clipping is listed on its own line and carries more, requiring your own provider keys and extra configuration. The environment example spells that out. Media normalization runs postiz-uploader on RunPod Serverless, transcoding web uploads to 1080p h264 mp4 and downsized images in the background, with the media record reporting status processing until the normalized file replaces the original. Clipping goes further and needs a CPU endpoint for ingest and a GPU endpoint for clips, an OpenAI key, and an Oxylabs account of its own inside postiz-uploader so it can fetch from YouTube, with Deepgram transcribing videos that have no usable captions. That is four vendors behind one button, and none of it runs on the local storage default.

Every channel needs your own developer app, and the wait is measured in weeks

The empty credential block is the honest picture of self-hosted Postiz. Each provider gets its own variables, and the compose file enumerates them from the top:

yaml
      X_URL: ''
      X_API_KEY: ''
      X_API_SECRET: ''
      LINKEDIN_CLIENT_ID: ''
      LINKEDIN_CLIENT_SECRET: ''
      REDDIT_CLIENT_ID: ''
      REDDIT_CLIENT_SECRET: ''
      GITHUB_CLIENT_ID: ''
      GITHUB_CLIENT_SECRET: ''
      APPLE_CLIENT_ID: ''
      APPLE_TEAM_ID: ''
      APPLE_KEY_ID: ''
      APPLE_PRIVATE_KEY: ''

Beehive, Threads, and Facebook follow, and each pair of identifiers is only half the work. The comparison table states the real cost: you create your own developer apps on each platform and go through their approval, and Meta, YouTube, and TikTok can take weeks. Analytics is marked included on both sides, with the self-hosted note that it requires your own app credentials with analytics scopes, so a channel can publish successfully and still show no numbers until the app is reviewed for read access. If you need a working channel today, the cloud edition with its pre-approved apps is the faster route.

Temporal, Postgres, and Redis are three stateful services behind one image

The stack list names NextJS and NestJS for the application, Prisma with PostgreSQL as the default database, Temporal for long running work, and Resend for email notifications. The compose environment block encodes three of those as hostnames you have to resolve: a PostgreSQL URL pointing at postiz-postgres on port 5432, REDIS_URL pointing at postiz-redis on 6379, and TEMPORAL_ADDRESS pointing at temporal on 7233. The credentials carried in that default are postiz-user and postiz-password, which suit a laptop and nothing else. The comparison table puts the same requirement in plain words, that you configure Postgres, Redis, storage, and environment variables yourself, and names Docker, Coolify, Railway, or any VPS as the routes. Three stateful services behind a single application image is the concrete reason the project calls its own deployment something that might be hard at times.

The root manifest reads 1.0.0 while the newest tag is v2.24.0

Version metadata is split across files that disagree. The root package.json is named gitroom rather than postiz, and its version field reads 1.0.0 while the newest release tag is v2.24.0, dated September 22, 2026. A separate version.txt sits at the repository root, and the compose file pulls ghcr.io/gitroomhq/postiz-app:latest rather than a pinned tag, so the image you deploy is whatever the last push produced. The node engine range is narrow, >=22.12.0 <23.0.0, which means a host on a different major version refuses the workspace instead of warning. This is a pnpm workspace with the package manager pinned to [email protected], shared code under libraries/, and a dev script that starts four apps in parallel: extension, orchestrator, backend, and frontend. The production build runs the three server apps with workspace concurrency set to one, so builds are serial on purpose.

AGPL-3.0 with no feature gating, and a sponsor that resells a build of it

The license is AGPL-3.0, the project states that it does not gate features or limit the license, and the self-hosted cost column reads free forever, which is a genuine advantage over per tier limits on channels, posts per month, and team members. The condition that comes with it matters more here than in most repositories. Network copyleft reaches a modified version offered over a network, so a team that customises the scheduler and serves it to its own users takes on obligations a permissive license would not impose. The sponsor list shows the ecosystem is already commercial around it: Hostinger, Virlo, RapidProxy, and ChatbotX, described as a ManyChat alternative you can self-host, white-label, and resell to your clients with your own agents. One of those sponsors sells residential IP addresses marketed for social media and browser automation, so read the license terms yourself before building anything commercial on top.

Editorial conclusion

Postiz fits a team that already has developer apps approved on Meta, YouTube, or TikTok, or a solo operator willing to wait out that review, and it is free forever under AGPL-3.0 if you can run three stateful services. Skip it if you need analytics on day one, AI video clipping without vendor accounts, or a guarantee that nothing leaves your network, because avatars still go through Cloudflare. Before deploying, change the shipped JWT secret, set DISABLE_REGISTRATION to true, and reconcile the 4007 and 4200 ports before you connect a single channel.

Frequently asked questions

How much does Postiz cost?

Self-hosted Postiz is free forever under the AGPL-3.0 license, and you pay only for your own infrastructure: a server plus PostgreSQL, Redis, and Temporal. Postiz Cloud is sold as a subscription per plan with a seven day free trial, and its tiers cap channels, posts per month, and team members while the self-hosted edition lists all three as unlimited.

How does Postiz work?

You deploy the app with Docker, Coolify, Railway, or any VPS, configure PostgreSQL, Redis, storage, and environment variables, and then connect channels. Each channel needs a developer app you register with that platform yourself, which is why Meta, YouTube, and TikTok reviews of weeks are called out as the main delay. Analytics needs your own app credentials with analytics scopes.

Which is better, Buffer or Postiz?

Nothing in this repository compares Postiz with Buffer, so that comparison cannot be settled from the project's own documentation. The comparison it does make is between Postiz Cloud and the self-hosted edition, and it states the tradeoff plainly: unlimited channels, posts, and team members on your own server, in exchange for owning the infrastructure and getting platform apps approved yourself.

What are people saying about Postiz in reviews?

The repository contains no user reviews, so nothing here reflects what users report. What it does contain is a self description of over 7M downloads and 20k views per month, a sponsor table, and a feature matrix that marks analytics as requiring your own app credentials with analytics scopes when self-hosted, which is the limitation most likely to surface in practice.

Official sources

  1. Official documentation
  2. Official README
  3. Project repository
  4. Release notes
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/gitroomhq-postiz-app.svg)](https://hysenlabs.com/projects/gitroomhq-postiz-app)