
PostHog Dev Stack Runner
FreeEfficiently manage and verify your PostHog development environment.
Free · Opens the source repo
What PostHog Dev Stack Runner does
The PostHog Dev Stack Runner skill is designed for developers working with the PostHog analytics platform. This skill provides essential commands to start, inspect, and verify the health of the PostHog development stack, which is based on a combination of Django, Vite, Celery, and various backend services like PostgreSQL and Redis running in Docker containers. By utilizing this skill, developers can streamline their workflow, ensuring that they can quickly launch the application and verify UI changes in a local environment.
The skill operates primarily through two commands: /run and /verify. The /run command is responsible for making the application reachable, ensuring that the core components are ready for interaction. It checks the health of the application by verifying that the necessary services are operational and that the application is accessible via http://localhost:8010. On the other hand, the /verify command allows developers to confirm that UI changes are reflected in the live application by driving the UI through a browser MCP, providing a straightforward way to validate changes without the need for complex testing setups.
This skill is particularly useful for developers who need to frequently iterate on their PostHog projects. By providing clear commands to manage the stack and verify changes, it eliminates much of the manual overhead typically involved in local development. The built-in checks and logging capabilities also help developers quickly identify and address issues that may arise during development, making it a valuable tool for maintaining a healthy development environment.
Overall, the PostHog Dev Stack Runner skill is an essential addition for anyone working with the PostHog platform, as it simplifies the process of managing the development stack and ensures that developers can focus on building and testing features effectively.
When to use it
Use this skill when you need to start the PostHog development stack or verify UI changes in your local environment.
When not to use it
This skill is not suitable for production environments or for users not working with the PostHog platform.
What you can build with it
Launching the Development Stack
Use the `/run` command to quickly start the PostHog development stack and ensure it's ready for interaction.
Verifying UI Changes
After making changes to the UI, use the `/verify` command to drive the application and confirm that the changes are reflected correctly.
Troubleshooting Stack Issues
Utilize the `hogli doctor` command to identify and resolve any issues with the development stack before proceeding with development.
How to install PostHog Dev Stack Runner
View source1. Install with the skills CLI
npx skills add posthog/posthog/run-posthog --agent claude-code2. Or install it manually
Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.
Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs
Inside SKILL.md
Written by posthogPostHog is Django + Vite + Celery + plugin-server, backed by Postgres, ClickHouse, Kafka, Redis, and Temporal in Docker, fronted by an Envoy-style proxy at http://localhost:8010. The dev stack runs in detached mode under phrocs so the running processes are inspectable from this session via the phrocs MCP server. Browser MCP servers (chrome-devtools-mcp, playwright) drive the UI; nothing about this skill ships its own driver.
For /run: get the app reachable so the user can drive it. Success = http://localhost:8010 serves and the core units are ready. Do not chase crashed migration units, do not seed data unless asked.
For /verify: build (Vite HMR handles this automatically for frontend; backend changes need no build), run the same launch as /run, then drive the live app via a browser MCP to observe the change. The observation is the verification — tests and type checks don't substitute. For frontend/UI changes, the deep version of this workflow is the repo-local qa-frontend skill in local mode: scenario design from the diff, reproducibility retries, and annotated evidence.
All paths below are relative to the repo root.
Prerequisites
- flox 1.12+ provisions the toolchain —
curl -L https://downloads.flox.dev/by-env/stable/install.sh | sudo bash - Docker — OrbStack preferred on macOS (
brew install --cask orbstack) - 1Password CLI (optional) —
brew install 1password-cli, only if.env.localcontainsop://refs
Launch
hogli dev:setup # first time only — interactive wizard picks which workers to run
hogli up -d -y # start the stack detached under phrocs
hogli services:ready -y # wait for Docker services (Postgres, CH, Kafka, Redis)
hogli wait -y # wait for phrocs-managed units (Django, Vite, Celery, plugin-server)
hogli doctor # optional: stale migrations, zombie phrocs, disk pressure
First boot is 60-90s while Django imports and Vite warms. Stop with hogli down -y (leaves Docker services running for fast restart).
Is the stack ready?
There are two readiness bars. Don't conflate them.
Ready for /run — the app is reachable and you can drive it:
curl -sf http://localhost:8010/_health # 200
curl -sf -o /dev/null -w '%{http_code}' http://localhost:8010/ # 200 or 302
curl -sS http://localhost:8010/api/projects/@current | grep -q '"code":"not_authenticated"' # API + DB reachable
Plus these phrocs units ready:true: backend, frontend, nodejs, capture, ingestion. Stop here. Do not chase crashed migrate-* units when only /run was asked — the stack is usable for launch, screenshot, and most UI scenes (home, login, settings, feature flags) regardless of migration state.
Ready for /verify of HogQL-backed scenes (insights, dashboards, web analytics) and for POST /api/setup_test/...:
- Also requires
mcp__phrocs__get_process_status process="migrate-clickhouse"to showstatus:"done" exit_code:0. If it'scrashed, see the gotcha below.
phrocs MCP tools — for either bar:
mcp__phrocs__get_process_status(no args) — all units, withready,exit_code, memory, CPU.mcp__phrocs__get_process_status process="frontend"— single unit.mcp__phrocs__get_process_logs process="backend"— tail recent stdout/stderr.mcp__phrocs__toggle_process process="<unit>"— restart one unit. Auto-mode blocks this on shared stacks (treated as restart-of-shared-infra). If blocked, ask the user to approve, or runphrocs stop && hogli up -dfor a full clean restart.
Drive the UI for /verify
Every empty PostHog scene looks broken because no events exist. To verify a UI change, you need a workspace with realistic data. The canonical path is the same one the Playwright suite uses: POST /api/setup_test/organization_with_team/. Gated on DEBUG=True | E2E_TESTING | CI | TEST, all of which local dev satisfies via DEBUG=True. Implementation: posthog/api/playwright_setup.py + posthog/test/playwright_setup_functions.py:create_organization_with_team — 3 clusters via HedgeboxMatrix, ~5-10s end to end. Reference call site: playwright/utils/playwright-setup.ts:251.
Avoid hogli dev:demo-data for /run and /verify — it calls generate_demo_data with n_clusters=500 (default at posthog/management/commands/generate_demo_data.py:59) and takes 5-30 minutes. It exists for humans who want a big realistic dataset to play with; it's the wrong tool for automated launch-and-screenshot.
The full browser-MCP recipe:
-
new_pagehttp://localhost:8010/login. -
evaluate_scriptthe workspace bootstrap. Per-call email so reruns don't collide; password fixed at12345678; response gives back the team_id and a personal API key.const r = await fetch('/api/setup_test/organization_with_team/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ data: { skip_onboarding: true } }), }) const { result } = await r.json() // result: { user_email, team_id, personal_api_key, organization_id, ... } return result -
evaluate_scriptthe in-page login. Runs in the page context so Django's CSRF middleware sees the right cookies (playwright/utils/playwright-setup.ts:282-291).const r = await fetch('/api/login/', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: workspace.user_email, password: '12345678' }), }) return r.status // 200 = logged in; 403 = you're not in-page; 400 = workspace setup didn't actually create the user -
navigate_pagetohttp://localhost:8010/project/{team_id}{scene-path}. -
wait_fortext or a[data-attr]element. PostHog attachesdata-attrto virtually every rendered element; use it as the "page hydrated" signal.networkidleandloadnever settle because PostHog.js and Vite HMR keep polling. -
take_screenshot. The accessibility snapshot fromwait_foris also enough on its own for most "is this element present?" verifications — no pixel comparison needed.
For API-only calls (creating fixtures, hitting endpoints without a browser), use the personal_api_key from the setup_test response as Authorization: Bearer <key> — no login dance.
For deeper interaction (clicking, filling forms, inspecting console), the same MCP toolkit (chrome-devtools-mcp:* / playwright:*) covers it. No project-specific wrapper needed.
For /verify: diff → URL mapping
The frontend uses kea-router. The mapping rule:
| Edited path | Scene URL (under /project/{team_id}/) |
|---|---|
frontend/src/scenes/<name>/** | usually /<name> (e.g. insights/ → /insights) |
frontend/src/scenes/activity/** | /activity/explore (and other ActivityTabs) |
frontend/src/scenes/data-management/** | /data-management/<sub> |
frontend/src/scenes/settings/** | /settings/<section> |
frontend/src/scenes/authentication/** | /login, /signup, /preflight (un-scoped) |
products/<name>/frontend/** | per the urls: block in products/<name>/manifest.tsx |
frontend/src/layout/** | renders everywhere; verify on any scene |
frontend/src/lib/components/<X>.tsx | grep usages, screenshot the scene that mounts it |
Canonical sources to grep when the path isn't obvious:
frontend/src/scenes/urls.ts— top-level URL helpers (urls.insights(),urls.dashboard(...), etc.).frontend/src/scenes/scenes.ts— scene-to-route registry.products/<name>/manifest.tsx— per-product routes.
Gotchas
migrate-clickhouseandmigrate-persons-dboften crash on a coldhogli up -ddue to a startup race. They start in parallel withmigrate-postgres, and if Postgres isn't ready yet they crash. This is the hard prereq forPOST /api/setup_test/...and for HogQL-backed scenes (insights, dashboards, web analytics) — but not for/run. Don't fix it unless the task needs HogQL/setup_test. When you do need to fix it, the canonical sequence is: wait formigrate-postgresto showstatus:"done"viamcp__phrocs__get_process_status, then restart the crashed migrations.mcp__phrocs__toggle_processis the surgical tool but auto-mode blocks it on shared stacks — fall back tophrocs stop && hogli up -dwhich re-runs everything in order, or runpython manage.py migrate_clickhousedirectly (you'll needset -a; source .env.services; set +afirst soCLICKHOUSE_DATABASE=posthog, otherwise it targetsdefault). If neither works (corrupted CH replica state in ZooKeeper from a partial run),hogli dev:resetis the only path — it wipes Docker volumes, destructive.hogli waitexits 0 even when phrocs is unreachable. Don't trust its return code as ground truth — confirm withmcp__phrocs__get_process_statusor thecurlprobes.- Vite serves on
:8234, not the URL you browse. You browsehttp://localhost:8010(the Envoy-style proxy). The proxy reverse-proxies Vite for/static/*and Django for everything else. Hitting:8234/directly returns 404 because Vite has no index route at the dev-server root. - Worktrees share Docker containers but compete for ports. All worktrees on the same machine resolve to the same
posthog-clickhouse-1/posthog-db-1containers, so DB state is global. But ports 8000/8010/8234 can only be held by one worktree at a time — kill the granian/vite/phrocs of the other worktree beforehogli up -dhere. - CSP warnings and 401s in the browser console are normal pre-auth. The preflight/login page tries to fetch
/api/projects/@current,/api/users/@me/, and PostHog.js remote config — all 401 until you sign up. WASM/CSP "Report Only" warnings come from the dev CSP. - Direct
curl -X POST /api/login/returns 403 (CSRF). Session login must run in-page viabrowser_evaluateso cookies and CSRF tokens flow. For non-browser API calls, use thepersonal_api_keyfrom thesetup_testresponse asAuthorization: Bearer <key>— no CSRF on token auth. .env.localmay use 1Password refs. Withoutopinstalled, refs become literal strings (e.g.OPENAI_API_KEY=op://...) and downstream services fail with cryptic auth errors. Installopor replace with literals.
Troubleshooting
hogli up -dexits withAnother instance of bin/start is already running— previous run still active or crashed without cleanup.mcp__phrocs__get_process_statusshows what's there; if nothing's running,rm bin/start.lockand retry.docker infofails withdial unix /Users/<you>/.orbstack/run/docker.sock: ... no such file— OrbStack is stopped.open -a OrbStack./api/projects/@currentreturns 500 instead of 401 — Postgres or ClickHouse unreachable.docker ps | grep posthog-and look for unhealthy containers;hogli services:readywaits for all of them.POST /api/login/returns 400invalid_credentials— you're logging in as a user thesetup_testworkspace didn't actually create (CH crash usually). Check the response of the setup_test call; if it 500'd, fix CH first (see themigrate-clickhousegotcha).POST /api/setup_test/organization_with_team/returns 404 —DEBUG,E2E_TESTING,CI, andTESTare all false. Local dev hasDEBUG=Trueby default; if it's not set,.env.localis missing orDJANGO_SETTINGS_MODULEpoints at a prod-like settings module.POST /api/setup_test/organization_with_team/returns 500Table posthog.person does not exist—migrate-clickhousecrashed during boot. See the gotcha above.- In-page
fetch('/api/login/')returns 403 — you're calling it from outside the page context (e.g.page.request.postrather thanpage.evaluate). Useevaluate_script/browser_evaluateso the call originates in-page.
Frequently asked questions about PostHog Dev Stack Runner
Similar skills
Turborepo
Optimized build system for JavaScript/TypeScript monorepos.
Azure Pipelines Validation
Streamline your Azure DevOps pipeline changes locally.
Azure Developer CLI
Streamline your Azure project workflows with best practices.
Azure Container Registry CLI
Manage Azure Container Registry resources with ease.
Aspire
Build and orchestrate polyglot distributed applications seamlessly.
Vercel CLI
Manage and deploy Vercel projects from the command line.
