# Postiz

Schedules posts to social media accounts from one web app.

- Replaces: [Buffer](https://openlogs.fyi/alternatives/buffer), [Hootsuite](https://openlogs.fyi/alternatives/hootsuite)
- Summary: Paid social media scheduling services, such as Buffer and Hootsuite.
- Stack: TypeScript (language), PostgreSQL (database), Redis (database)
- Deployment: docker_compose
- License: AGPL-3.0 (osi) [source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/LICENSE)
- Business model: unknown. The README at the pinned commit does not state a business model, so it stays unknown. ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/README.md), read 2026-10-22)
- Difficulty: hard. 9 services (4 database or broker: postgres, redis, elasticsearch), 7 volume mounts, no env values to set, no elevated host access. Heuristic score 7 of 11: hard.
- Requirements: not stated
- Repository: [gitroomhq/postiz-app](https://github.com/gitroomhq/postiz-app)

## Install

1. Docker with the Compose plugin ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml))

   ```text
   A host with Docker and the Docker Compose plugin. This entry runs from a compose file.
   ```

2. Save the compose file as docker-compose.yaml ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml))

   ```yaml
   services:
     postiz:
       image: ghcr.io/gitroomhq/postiz-app:latest
       container_name: postiz
       restart: always
       environment:
         # === Required Settings
         MAIN_URL: 'http://localhost:4007'
         FRONTEND_URL: 'http://localhost:4007'
         NEXT_PUBLIC_BACKEND_URL: 'http://localhost:4007/api'
         JWT_SECRET: 'random string that is unique to every install - just type random characters here!'
         DATABASE_URL: 'postgresql://postiz-user:postiz-password@postiz-postgres:5432/postiz-db-local'
         REDIS_URL: 'redis://postiz-redis:6379'
         BACKEND_INTERNAL_URL: 'http://localhost:3000'
         TEMPORAL_ADDRESS: 'temporal:7233'
         IS_GENERAL: 'true'
         DISABLE_REGISTRATION: 'false'
   
         # === Storage Settings
         STORAGE_PROVIDER: 'local'
         UPLOAD_DIRECTORY: '/uploads'
         NEXT_PUBLIC_UPLOAD_DIRECTORY: '/uploads'
   
         # === Cloudflare (R2) Settings
         # STORAGE_PROVIDER: 'cloudflare'
         # CLOUDFLARE_ACCOUNT_ID: 'your-account-id'
         # CLOUDFLARE_ACCESS_KEY: 'your-access-key'
         # CLOUDFLARE_SECRET_ACCESS_KEY: 'your-secret-access-key'
         # CLOUDFLARE_BUCKETNAME: 'your-bucket-name'
         # CLOUDFLARE_BUCKET_URL: 'https://your-bucket-url.r2.cloudflarestorage.com/'
         # CLOUDFLARE_REGION: 'auto'
   
         # === Social Media API Settings
         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: ''
         BEEHIIVE_API_KEY: ''
         BEEHIIVE_PUBLICATION_ID: ''
         THREADS_APP_ID: ''
         THREADS_APP_SECRET: ''
         FACEBOOK_APP_ID: ''
         FACEBOOK_APP_SECRET: ''
         YOUTUBE_CLIENT_ID: ''
         YOUTUBE_CLIENT_SECRET: ''
         TIKTOK_CLIENT_ID: ''
         TIKTOK_CLIENT_SECRET: ''
         TIKTOK_BUSINESS_CLIENT_ID: ''
         TIKTOK_BUSINESS_CLIENT_SECRET: ''
         PINTEREST_CLIENT_ID: ''
         PINTEREST_CLIENT_SECRET: ''
         DRIBBBLE_CLIENT_ID: ''
         DRIBBBLE_CLIENT_SECRET: ''
         TUMBLR_CLIENT_ID: ''
         TUMBLR_CLIENT_SECRET: ''
         DISCORD_CLIENT_ID: ''
         DISCORD_CLIENT_SECRET: ''
         DISCORD_BOT_TOKEN_ID: ''
         SLACK_ID: ''
         SLACK_SECRET: ''
         SLACK_SIGNING_SECRET: ''
         MASTODON_URL: 'https://mastodon.social'
         MASTODON_CLIENT_ID: ''
         MASTODON_CLIENT_SECRET: ''
   
         # === OAuth & Authentik Settings
         # NEXT_PUBLIC_POSTIZ_OAUTH_DISPLAY_NAME: 'Authentik'
         # NEXT_PUBLIC_POSTIZ_OAUTH_LOGO_URL: 'https://raw.githubusercontent.com/walkxcode/dashboard-icons/master/png/authentik.png'
         # POSTIZ_GENERIC_OAUTH: 'false'
         # POSTIZ_OAUTH_URL: 'https://auth.example.com'
         # POSTIZ_OAUTH_AUTH_URL: 'https://auth.example.com/application/o/authorize'
         # POSTIZ_OAUTH_TOKEN_URL: 'https://auth.example.com/application/o/token'
         # POSTIZ_OAUTH_USERINFO_URL: 'https://authentik.example.com/application/o/userinfo'
         # POSTIZ_OAUTH_CLIENT_ID: ''
         # POSTIZ_OAUTH_CLIENT_SECRET: ''
         # POSTIZ_OAUTH_SCOPE: "openid profile email"  # Optional: uncomment to override default scope
   
         # === Sentry
   
         # NEXT_PUBLIC_SENTRY_DSN: 'http://spotlight:8969/stream'
         # SENTRY_SPOTLIGHT: '1'
   
         # === Misc Settings
         OPENAI_API_KEY: ''
         # Only this OAuth client can receive verified user email claims via OIDC.
         OPENAI_OAUTH_CLIENT_ID: ''
         NEXT_PUBLIC_DISCORD_SUPPORT: ''
         NEXT_PUBLIC_POLOTNO: ''
         API_LIMIT: 30
   
         # === Payment / Stripe Settings
         FEE_AMOUNT: 0.05
         STRIPE_PUBLISHABLE_KEY: ''
         STRIPE_SECRET_KEY: ''
         STRIPE_SIGNING_KEY: ''
         STRIPE_SIGNING_KEY_CONNECT: ''
   
         # === Developer Settings
         NX_ADD_PLUGINS: false
   
         # === Short Link Service Settings (Optional - leave blank if unused)
         # DUB_TOKEN: ""
         # DUB_API_ENDPOINT: "https://api.dub.co"
         # DUB_SHORT_LINK_DOMAIN: "dub.sh"
         # SHORT_IO_SECRET_KEY: ""
         # KUTT_API_KEY: ""
         # KUTT_API_ENDPOINT: "https://kutt.it/api/v2"
         # KUTT_SHORT_LINK_DOMAIN: "kutt.it"
         # LINK_DRIP_API_KEY: ""
         # LINK_DRIP_API_ENDPOINT: "https://api.linkdrip.com/v1/"
         # LINK_DRIP_SHORT_LINK_DOMAIN: "dripl.ink"
   
       volumes:
         - postiz-config:/config/
         - postiz-uploads:/uploads/
       ports:
         - '4007:5000'
       networks:
         - postiz-network
         - temporal-network
       depends_on:
         postiz-postgres:
           condition: service_healthy
         postiz-redis:
           condition: service_healthy
   
     postiz-postgres:
       image: postgres:17-alpine
       container_name: postiz-postgres
       restart: always
       environment:
         POSTGRES_PASSWORD: postiz-password
         POSTGRES_USER: postiz-user
         POSTGRES_DB: postiz-db-local
       volumes:
         - postgres-volume:/var/lib/postgresql/data
       networks:
         - postiz-network
       healthcheck:
         test: pg_isready -U postiz-user -d postiz-db-local
         interval: 10s
         timeout: 3s
         retries: 3
     postiz-redis:
       image: redis:7.2
       container_name: postiz-redis
       restart: always
       healthcheck:
         test: redis-cli ping
         interval: 10s
         timeout: 3s
         retries: 3
       volumes:
         - postiz-redis-data:/data
       networks:
         - postiz-network
   
     # For Application Monitoring / Debugging
     spotlight:
       pull_policy: always
       container_name: spotlight
       ports:
         - 8969:8969/tcp
       image: ghcr.io/getsentry/spotlight:latest
       networks:
         - postiz-network
   
     # -----------------------
     # Temporal Stack
     # -----------------------
     temporal-elasticsearch:
       container_name: temporal-elasticsearch
       image: elasticsearch:7.17.27
       environment:
         - cluster.routing.allocation.disk.threshold_enabled=true
         - cluster.routing.allocation.disk.watermark.low=512mb
         - cluster.routing.allocation.disk.watermark.high=256mb
         - cluster.routing.allocation.disk.watermark.flood_stage=128mb
         - discovery.type=single-node
         - ES_JAVA_OPTS=-Xms256m -Xmx256m
         - xpack.security.enabled=false
       networks:
         - temporal-network
       expose:
         - 9200
       volumes:
         - /var/lib/elasticsearch/data
   
     temporal-postgresql:
       container_name: temporal-postgresql
       image: postgres:16
       environment:
         POSTGRES_PASSWORD: temporal
         POSTGRES_USER: temporal
       networks:
         - temporal-network
       expose:
         - 5432
       volumes:
         - /var/lib/postgresql/data
   
     temporal:
       container_name: temporal
       ports:
         - '7233:7233'
       image: temporalio/auto-setup:1.28.1
       depends_on:
         - temporal-postgresql
         - temporal-elasticsearch
       environment:
         - DB=postgres12
         - DB_PORT=5432
         - POSTGRES_USER=temporal
         - POSTGRES_PWD=temporal
         - POSTGRES_SEEDS=temporal-postgresql
         - DYNAMIC_CONFIG_FILE_PATH=config/dynamicconfig/development-sql.yaml
         - ENABLE_ES=true
         - ES_SEEDS=temporal-elasticsearch
         - ES_VERSION=v7
         - TEMPORAL_NAMESPACE=default
       networks:
         - temporal-network
       volumes:
         - ./dynamicconfig:/etc/temporal/config/dynamicconfig
       labels:
         kompose.volume.type: configMap
   
     temporal-admin-tools:
       container_name: temporal-admin-tools
       image: temporalio/admin-tools:1.28.1-tctl-1.18.4-cli-1.4.1
       environment:
         - TEMPORAL_ADDRESS=temporal:7233
         - TEMPORAL_CLI_ADDRESS=temporal:7233
       networks:
         - temporal-network
       stdin_open: true
       depends_on:
         - temporal
       tty: true
   
     temporal-ui:
       container_name: temporal-ui
       image: temporalio/ui:2.34.0
       environment:
         - TEMPORAL_ADDRESS=temporal:7233
         - TEMPORAL_CORS_ORIGINS=http://127.0.0.1:3000
       networks:
         - temporal-network
       ports:
         - '8080:8080'
   
   volumes:
     postgres-volume:
       external: false
   
     postiz-redis-data:
       external: false
   
     postiz-config:
       external: false
   
     postiz-uploads:
       external: false
   
   networks:
     postiz-network:
       external: false
     temporal-network:
       driver: bridge
       name: temporal-network
   ```

3. Start it ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml))

   ```bash
   docker compose up -d
   ```

4. Open the app ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml))

   ```text
   Open http://localhost:4007. The compose file publishes host port 4007 for the postiz service.
   ```

Compose file check: config_ok on 2026-10-22.

## Gotchas and maintenance

### Gotchas

- Some images use the latest tag or no tag: Images without a pinned version: postiz: ghcr.io/gitroomhq/postiz-app:latest, spotlight: ghcr.io/getsentry/spotlight:latest. A later pull can get a different version. ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml), checked 2026-10-22)
- The compose file publishes host ports: Host ports: 4007 (postiz), 8969 (spotlight), 7233 (temporal), 8080 (temporal-ui). A port without a host address listens on all host network interfaces. ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml), checked 2026-10-22)
- The compose file mounts host folders: Host paths mounted into containers: ./dynamicconfig at /etc/temporal/config/dynamicconfig (temporal). ([source](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml), checked 2026-10-22)

### Signals

Computed from a GitHub snapshot taken 2026-10-22.

- Open issues and pull requests: 222
- Stars: 37561, forks: 7326
- Last push: 2026-10-21 (0 days ago)
- Latest release: v2.25.1 on 2026-10-11 (10 days ago)

## Sources

- [Repository: https://github.com/gitroomhq/postiz-app](https://github.com/gitroomhq/postiz-app)
- [License: https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/LICENSE](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/LICENSE)
- [Business model: https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/README.md](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/README.md)
- [Compose file: https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml](https://github.com/gitroomhq/postiz-app/blob/c06abc3d786b8f719ced5716ccc70dd8a7251a05/docker-compose.yaml)

---
Canonical: https://openlogs.fyi/tools/postiz  
Last updated: 2026-10-22
