Skip to main content

Docker

TryPost ships two Compose files:

Requirements

  • Docker Desktop (Mac, Windows) or Docker Engine (Linux)
  • Docker Compose v2

Production self-hosting

No local PHP/Node install required. The published image includes the built frontend.

What you do vs what the container does

1. Clone the repository

You only need the repo for compose.prod.yaml (and optionally Caddyfile). The app image comes from GHCR.

2. Generate an app key and edit the compose file

Paste the value into APP_KEY in compose.prod.yaml. Also set:
  • APP_URL — your public URL (e.g. https://post.yourdomain.com)
  • DB_PASSWORD / POSTGRES_PASSWORD (must match)
  • REVERB_APP_SECRET
  • PASSPORT_PRIVATE_KEY / PASSPORT_PUBLIC_KEYrequired in production (file keys under storage/ are not on a persisted volume). Generate once:
Paste the PEM contents into the compose env vars (use \n for newlines). Do not regenerate after users have issued API or MCP tokens. Leave REVERB_APP_KEY as trypost-reverb-key unless you rebuild the image — it must match the key baked into the published frontend bundle. Queues, cache, session, and broadcast are already set to Redis / Reverb / database in compose.prod.yaml. Configure mail (defaults to log) and optional social/AI credentials in the same file — see Configuration.

3. Start the stack

On first boot the entrypoint:
  • waits for Postgres
  • runs migrations
  • creates storage:link if missing (unused if you later switch to S3 / R2 / Spaces)
  • loads Passport keys from PASSPORT_PRIVATE_KEY / PASSPORT_PUBLIC_KEY (exits if unset in production)
  • ensures the Passport Personal Access Client (PassportSeeder)
  • caches config / routes / views / events
  • starts nginx, php-fpm, Horizon, Reverb, and schedule:work
It does not create the admin user or plan rows — next step.

4. Seed plans and admin user (one-time)

Change the admin password on first login. Extra accounts come from workspace invites (Settings → Members).
Production Passport keys come from PASSPORT_* env vars (set before up). The entrypoint also runs PassportSeeder so API-key creation works without a manual client step. MCP uses OAuth (mcp:use) with those same keys — not API-key Bearer headers. Still run db:seed / UserSeeder for plans and the admin user.

5. Optional HTTPS with Caddy

  1. Point DNS at the host
  2. Set APP_URL to https://your-domain and APP_DOMAIN on the caddy service
  3. Start with the proxy profile:
Without the profile, the app listens on http://localhost:8000.

6. Verify

  1. Open APP_URL and sign in as admin
  2. Create an API key (Settings → API Keys) — proves Passport keys + Personal Access Client exist
  3. Connect an MCP client via OAuth (Settings → MCP), if you use assistants
  4. Schedule a post — Horizon must show as running

Access

Reverb on a custom domain

The published image bakes VITE_REVERB_* at build time (localhost:8080). For HTTPS on your domain, rebuild:
Point compose.prod.yaml at that image. Changing only runtime REVERB_HOST / PORT / SCHEME does not update the browser client.

Common production commands


Local development

Use compose.yaml when you want a bind-mounted source tree.

1. Clone and start

On first boot the entrypoint can seed .env from docker/.env.docker.example, install Composer/npm deps if missing, generate APP_KEY, migrate, storage:link, and passport:keys. Horizon, Reverb, the scheduler, and Vite start via Supervisor. For local media URLs, set FILESYSTEM_DISK=public in .env (the Docker example ships with local) and ensure storage:link ran.

2. Seed admin + Passport client

Or wipe-and-rebuild on a brand-new database:
migrate:fresh drops every table.

Access (dev)

Common development commands

Troubleshooting

Port conflicts

Permission issues (Linux)

Database connection refused

API key creation fails with “Personal access client not found”

Seed the Passport client:

Horizon not processing jobs

Next steps