Claude Cursor Skill

polar-local-environment

This skill should be used when setting up or managing Polar local development environment with Docker.

LLM Mart · 0 points · 6 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download fcakyon-claude-codex-settings-plugins_polar-skills_skills_polar-local-environment-4632eb3.zip · 15 KB
Part of fcakyon/claude-codex-settings — 83 skills

Install

skills CLI npx skills add https://github.com/fcakyon/claude-codex-settings/tree/main/plugins/polar-skills/skills/polar-local-environment
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install fcakyon-claude-codex-settings@llmmart
Git git clone https://github.com/fcakyon/claude-codex-settings.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole fcakyon/claude-codex-settings collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Local Environment Skill

Helps manage the Polar local development environment through the dev docker CLI. Use it to start, stop, debug, or reason about the local stack.

The two-part model

dev docker deliberately splits the stack so many worktrees can share one set of heavy infra:

  • Shared infra — one copy per machine, Docker project polar-shared: postgres, redis, minio, tinybird, and optional prometheus/grafana. Postgres and redis publish no host ports; MinIO exposes 9000/9001 for browser uploads. Use dev docker exec <service> ... for services without host ports.
  • Per-instance app stack — one per worktree, project polar-app-<N>: api, worker, web. Only api and web publish host ports, offset per instance so worktrees don't collide.

Knowing this prevents the most common confusion: there is no localhost:5432 for the database (it lives in the shared stack and is reached through dev docker exec db ...). MinIO does expose ports 9000/9001 for browser uploads and console access.

Instance auto-detection

dev docker auto-detects the instance for the current worktree, so -i is rarely needed. Priority:

  1. POLAR_DOCKER_INSTANCE pinned in dev/docker/.env.docker (dev docker set-instance N)
  2. CONDUCTOR_PORT env var → (port - 55000) / 10 + 1
  3. The cross-worktree registry (~/.config/polar/docker-instances.json)
  4. Otherwise the lowest free number, then registered

Run dev docker ports to see the resolved instance and its URLs (add --json for tooling). To wire this worktree into Claude Code's preview, run dev docker launch-json, which writes a per-instance .claude/launch.json with the correct ports (it's gitignored, so regenerate after set-instance).

When to use

  • Start / stop / restart the local environment
  • View logs or debug a service that won't come up
  • Run several isolated worktree instances in parallel
  • Understand the service architecture or find a service's real port
  • Diagnose container or first-boot errors

Quick reference

Task Command
Start full stack (background) dev docker up -d
Start and block until healthy dev docker up -d --wait
Start in foreground (stream logs) dev docker up --no-detach
Rebuild with fresh base images dev docker up -b --pull -d
Show this instance's ports/URLs dev docker ports (--json for tooling)
Write Claude Code preview config dev docker launch-json
Stop app stack dev docker down
Stop app and shared infra dev docker down --all
Follow logs dev docker logs -f [service]
Print logs and exit dev docker logs --no-follow [service]
Status dev docker ps
Restart a service dev docker restart <service>
Shell into a service dev docker shell <service>
One-off command in a service dev docker exec <service> <cmd>
Reset this instance dev docker cleanup -f
Wipe ALL shared data dev docker cleanup --all -f
List every instance dev docker list
With monitoring dev docker up --monitoring -d

Services

Service Project Host port (instance 0 / N) Notes
api polar-app-<N> 8000 / 8100+N FastAPI; /healthz healthcheck
web polar-app-<N> 3000 / 3100+N Next.js; healthchecked
worker polar-app-<N> none Background jobs
db polar-shared none (exec) PostgreSQL; DB polar_dev_<N>
redis polar-shared none (exec) Redis DB index = N
minio polar-shared 9000, 9001 S3; buckets polar-s3-<N>; console at 9001
tinybird polar-shared none (exec) Analytics
prometheus / grafana polar-shared none (exec) --monitoring only

Discover the exact host ports for the current worktree with dev docker ports.

Instance port mapping

Only api and web get host ports: Port = Base + Instance (Base 8100 for api, 3100 for web) for instances 1–99. Instance 0 uses the legacy 8000 / 3000.

Instance API Web
0 8000 3000
1 8101 3101
2 8102 3102
5 8105 3105

Everything else is per-instance but not on a host port: database polar_dev_<N>, redis DB index <N>, buckets polar-s3-<N> / polar-s3-public-<N>. Reach them via dev docker exec <service> or docker exec polar-shared-<service>-1.

Rules index

Rule Category Description
service-architecture Reference Service details, ports, healthchecks
start-environment Operations Starting the stack (flags, --wait, --pull)
stop-environment Operations Stopping and cleanup (app vs shared)
manage-instances Operations Parallel worktree instances
view-logs Debugging Viewing service logs
shell-and-workflows Operations Shell access and common dev workflows
troubleshooting Debugging Common errors and fixes
payment-testing Operations Login codes, Stripe webhooks, backoffice
Files (claude-codex-settings)
  • rules
    • manage-instances.md 2.7 KB
      ---
      title: Managing Multiple Instances
      category: Operations
      tags: instances, parallel, isolation
      ---
      
      # Managing Multiple Instances
      
      ## What are instances?
      
      An instance is one worktree's isolated app stack (project `polar-app-<N>`). All
      instances share a single infra stack (`polar-shared`), but each gets:
      
      - Its own api/web **host ports** (offset per instance)
      - Its own database `polar_dev_<N>` on the shared postgres
      - Its own redis DB index `<N>` on the shared redis
      - Its own S3 buckets `polar-s3-<N>` / `polar-s3-public-<N>` on the shared minio
      
      So instances are cheap: only api/worker/web containers are duplicated, not the
      infra.
      
      ## Port mapping
      
      Only api and web publish host ports. Everything else is shared and reached by
      container name, so there is **no** per-instance `5532`/`6479`/`9100` — those
      ports don't exist in this model.
      
      | Service | Instance 0 | Instance 1 | Instance 2 |
      |---------|------------|------------|------------|
      | API (host) | 8000 | 8101 | 8102 |
      | Web (host) | 3000 | 3101 | 3102 |
      | DB (logical) | `polar_dev_0` | `polar_dev_1` | `polar_dev_2` |
      | Redis (DB index) | 0 | 1 | 2 |
      | S3 bucket | `polar-s3-0` | `polar-s3-1` | `polar-s3-2` |
      
      **Host-port formula (api/web only):** `Port = Base + Instance` (Base 8100 for
      api, 3100 for web), for instances 1–99. Instance 0 uses the legacy `8000`/`3000`.
      So instance 5 is api `8105` / web `3105`.
      
      Run `dev docker ports` in a worktree to print its resolved instance and URLs.
      
      ## Pinning and inspecting instances
      
      Auto-detection usually picks the right instance, but you can pin or inspect:
      
      ```bash
      dev docker set-instance 5     # pin this worktree to instance 5 (writes .env.docker + registry)
      dev docker clear-instance     # back to auto-detect
      dev docker list               # every registered instance: number, status, path
      dev docker prune              # drop registry entries whose worktree is gone, and their data
      ```
      
      Pinning makes ports deterministic, which is what you want when wiring a worktree
      into tooling like `.claude/launch.json`.
      
      ## Commands (explicit instance)
      
      Most commands auto-detect, but `-i N` targets one explicitly:
      
      ```bash
      dev docker up -i 1 -d       # start instance 1
      dev docker ps -i 1          # status
      dev docker logs -i 1 api    # logs
      dev docker down -i 1        # stop
      dev docker shell -i 1 api   # shell
      ```
      
      ## Use cases
      
      - **Branch A vs branch B** side by side, each in its own worktree.
      - **Run a long test suite** in one instance while developing in another.
      - **Before/after comparisons** without tearing down your main stack.
      
      ## Resource notes
      
      Each app stack adds ~2 GB and its own build/cache volumes; the shared infra is
      paid once. Two or three instances is comfortable on a typical machine. Use
      `dev docker prune` to reclaim data from worktrees you've deleted.
      
    • payment-testing.md 6.2 KB
      ---
      title: Testing Auth and Payment Flows Locally
      category: Operations
      tags: stripe, dramatiq, auth, backoffice, checkout
      ---
      
      # Testing Auth and Payment Flows Locally
      
      Most non-trivial bug reports against the local stack touch one of: login,
      Stripe webhooks, subscription renewals, refunds, or the backoffice. These
      all have small environmental quirks that aren't obvious from the source —
      this rule collects them in one place.
      
      ## Logging In
      
      Email login codes are printed in the api container logs as a banner:
      
      ```
      ╔══════════════════════════════════════════════════════════╗
      ║                   🔑 LOGIN CODE: ABCDE1                  ║
      ╚══════════════════════════════════════════════════════════╝
      ```
      
      Grab the latest one:
      
      ```bash
      docker logs --since 30s polar-app-<N>-api-1 2>&1 | grep -A1 "LOGIN CODE"
      ```
      
      Use `admin@polar.sh` as the default test account — the seed creates it with
      an approved org (`admin-org`) that already has a payout account, identity
      verification, and at least one product. That lets you go straight to
      checkout testing without onboarding work.
      
      ## Stripe Sandbox and Webhooks
      
      ```bash
      dev stripe --listen
      # or, for a non-default instance, pass the API port from `dev docker ports`:
      dev stripe --listen --port <api-port>
      ```
      
      Local environments always run against a **personal Stripe sandbox** — never a
      live account, and never a shared team account. `dev` refuses both with one
      structural check: the linked profile must hold test keys, and since a sandbox
      has no live mode at all, the Stripe CLI must have stored no live key for it.
      A shared team account is a live account, so it fails that check.
      
      The Stripe CLI profile is always named `polar-sandbox`, so `dev` never
      depends on whichever account the CLI happens to have active. Pass
      `-p polar-sandbox` when running `stripe` by hand.
      
      `dev stripe --listen` handles the full setup in one step: installs the
      Stripe CLI if missing, walks through creating/linking a sandbox, writes
      `POLAR_STRIPE_SECRET_KEY`, `POLAR_STRIPE_PUBLISHABLE_KEY`, and
      `POLAR_STRIPE_WEBHOOK_SECRET` into the central secrets file, runs
      `dev/setup-environment` to propagate them, and then starts `stripe listen`
      forwarding to both the regular webhook endpoint and the Stripe Connect
      endpoint (`/v1/integrations/stripe/webhook` and
      `/v1/integrations/stripe/webhook-connect`). Re-running it later just starts
      the listener. `dev stripe --relink` switches to a different sandbox.
      
      Stripe CLI keys expire after 90 days. An expired key shows up as
      `The API key provided has expired` — `dev stripe` detects it and re-runs the
      link flow.
      
      `--port` defaults to `8000`. Conductor worktrees and multi-instance setups
      land outside the 0–2 base-port table, so read the api port from
      `dev docker ports` rather than computing it.
      
      Leave it running and `stripe listen` will log each event with the API's
      2xx response. Missing webhook → confirm the api port matches
      `dev docker ports`.
      
      One listener signs both endpoints, so `POLAR_STRIPE_WEBHOOK_SECRET` and
      `POLAR_STRIPE_CONNECT_WEBHOOK_SECRET` hold the same value. If they already
      differ, they came from dashboard endpoints and `dev stripe` leaves them
      alone.
      
      ## Taxes
      
      `dev stripe` reports on Stripe Tax, because checkout can't price an order
      without it:
      
      - **inactive/pending** — checkout fails with a tax calculation error. Activate
        Stripe Tax in the sandbox (it needs a head office address).
      - **active, no registrations** — checkout works, every order is taxed at 0.
      - **active with registrations** — the countries are listed.
      
      To test real tax, add a registration under Tax > Registrations in the sandbox:
      pick "I've already registered", then "Non-Union One-Stop Shop (OSS)" for
      Ireland, starting immediately. EU countries then get VAT (Ireland 23%,
      Sweden 25%, and so on). Tax applies per the customer's billing country, so a
      US-only registration leaves EU orders at 0.
      
      ## Checkout Email Validation
      
      The checkout form rejects email addresses whose domain looks fake. Two
      common gotchas:
      
      - `.local` TLDs fail with "reserved name that cannot be used with email"
      - `example.com` fails with "domain does not accept email"
      
      Use a real domain with a `+tag` to keep tests isolated:
      `yourname+test-foo@polar.sh`.
      
      ## Triggering Dramatiq Actors Manually
      
      Some flows (notably subscription renewals) are driven by background jobs
      that normally fire on a schedule. To force one immediately, enqueue the
      actor from inside the api container:
      
      ```bash
      docker exec polar-app-<N>-api-1 sh -c 'cd /app/server && uv run python -c "
      import asyncio, dramatiq
      import polar.tasks  # registers every actor as a side-effect of import
      from polar.worker import JobQueueManager, enqueue_job
      from polar.redis import create_redis
      
      async def main():
          redis = create_redis(\"worker\")
          async with JobQueueManager.open(dramatiq.get_broker(), redis):
              enqueue_job(\"<actor.name>\", *args)
      
      asyncio.run(main())
      "'
      ```
      
      Two non-obvious bits:
      
      - `import polar.tasks` is required. Without it, the broker has no registered
        actors and `enqueue_job` raises `dramatiq.errors.ActorNotFound`.
      - The `JobQueueManager.open(...)` context manager is what flushes the queued
        message to Redis. Without it, `enqueue_job` raises `LookupError` on the
        `polar.job_queue_manager` context var.
      
      ### Useful actors
      
      | Actor | Args | Notes |
      |-------|------|-------|
      | `subscription.cycle` | `subscription_id, force` | Advances one period. `force=True` ignores `current_period_end` — use it to fake renewals or to drive `cancel_at_period_end` subscriptions to their final cancel. |
      
      ## Inspecting the Backoffice
      
      The backoffice is mounted at `http://localhost:<api-port>/backoffice/` and
      uses the same session cookie as the dashboard, so logging into the dashboard
      also authenticates you here. Useful for verifying merchant-side state
      (balance, review status, transactions, audit logs) without writing SQL.
      
      A direct DB cross-check is still cheap and worth running when investigating
      balance/transaction issues:
      
      ```bash
      dev docker exec db psql -U polar -d polar_dev_<N> -c \
        "SELECT total_balance FROM organizations WHERE slug='admin-org';"
      ```
      
    • service-architecture.md 3.1 KB
      ---
      title: Service Architecture Reference
      category: Reference
      tags: architecture, services, infrastructure
      ---
      
      # Service Architecture Reference
      
      Two Docker projects: shared infra (`polar-shared`, one per machine) and a
      per-instance app stack (`polar-app-<N>`, one per worktree). Shared infra
      publishes **no host ports** — reach it with `dev docker exec <service> ...`.
      
      ## Infrastructure services (project `polar-shared`)
      
      ### db (PostgreSQL 15.1)
      - Primary database. Reached at `db:5432` on the `polar-shared` network.
      - Credentials: `polar` / `polar`.
      - One logical database per instance: `polar_dev_<N>`.
      - Volume: `postgres_data`. Health check: `pg_isready`.
      
      ### redis (Redis Alpine)
      - Cache and job-queue backend at `redis:6379`.
      - One DB index per instance (`<N>`); launched with `--databases 100`.
      - Health check: `redis-cli ping`.
      
      ### minio (S3-compatible storage)
      - File storage at `minio:9000` (container) or `localhost:9000` (host).
      - Console at `localhost:9001` with credentials `polar-development` / `polar123456789`.
      - Per-instance buckets: `polar-s3-<N>`, `polar-s3-public-<N>`.
      - Volume: `minio_data`.
      
      ### tinybird
      - Analytics engine at `tinybird:7181` (admin `7182`). Token is auto-discovered
        from the running container on api startup.
      
      ## Application services (project `polar-app-<N>`)
      
      ### api (FastAPI backend)
      - `python:3.14.6-slim` + uvicorn, hot-reload on.
      - Host port: `8000` (instance 0) or `8100+N`.
      - Healthcheck: `curl /healthz` (generous `start_period` covers first-boot sync +
        migrations + seed).
      - Startup: `uv sync`, build email templates, bootstrap DB/buckets, run
        migrations, load seed data on first run.
      
      ### worker (background jobs)
      - Same image as api. No host port.
      - Dramatiq with the priority queues (`high_priority`, `medium_priority`,
        `low_priority`), hot-reload on.
      - Waits for api before starting.
      
      ### web (Next.js frontend)
      - `node:22-slim` + Turbopack, hot-reload on.
      - Host port: `3000` (instance 0) or `3100+N`. Memory limit: 6 GB.
      - Healthcheck via Node's built-in `fetch` (the image has no curl/wget).
      - Waits for api to be **healthy** before starting (it proxies SSR to api).
      
      ## Optional monitoring (project `polar-shared`, `--monitoring`)
      
      - **prometheus** — metrics, 1-day retention, at `prometheus:9090` (no host port).
      - **grafana** — dashboards at `grafana:3000` internally, login `polar` / `polar`
        (no host port; port-forward if you need the UI).
      
      ## Container dependencies
      
      ```
      minio-setup → minio (healthy)
      api         → db, redis, minio-setup
      worker      → api (started)
      web         → api (healthy)
      grafana     → prometheus
      ```
      
      ## Volume persistence
      
      | Volume | Purpose |
      |--------|---------|
      | postgres_data | Database (shared) |
      | minio_data | Files (shared) |
      | server_uv_cache | Python package cache |
      | api_venv / worker_venv | Per-service virtualenv |
      | pnpm_store | Node package store |
      | web_node_modules | Frontend deps |
      | web_next_cache | Build cache |
      
      ## Networking
      
      App containers join a per-instance `default` bridge plus `polar-shared`. Infra
      is addressed by name (`db:5432`, `redis:6379`, `minio:9000`, `api:8000`). Web
      stays off `polar-shared` so `http://api:8000` resolves to *this* instance's api.
      
    • shell-and-workflows.md 2.6 KB
      ---
      title: Shell Access and Common Workflows
      category: Operations
      tags: shell, exec, workflows, testing
      ---
      
      # Shell Access and Common Workflows
      
      ## Shell into a container
      
      ```bash
      dev docker shell api      # Python env (api)
      dev docker shell worker   # Python env (worker)
      dev docker shell web      # Node env
      dev docker shell db       # postgres container (shared)
      ```
      
      For one-off commands without an interactive shell, use `exec`:
      
      ```bash
      dev docker exec api uv run alembic current
      dev docker exec db psql -U polar -d polar_dev_<N> -c "SELECT 1"
      dev docker exec redis redis-cli -n <N> dbsize
      ```
      
      `exec`/`shell` auto-route by service name: app services hit this instance's
      `polar-app-<N>` project, infra services hit `polar-shared`.
      
      ## Useful in-container commands
      
      **api / worker (Python):**
      ```bash
      uv run alembic upgrade head                             # run migrations
      uv run alembic revision --autogenerate -m "message"     # create a migration
      uv run alembic current                                  # show current revision
      ```
      (Backend tests are the exception — they don't run in this container; see below.)
      
      **web (Node):**
      ```bash
      pnpm test
      pnpm lint
      ```
      
      **db (PostgreSQL):** the database is per-instance, so pass `polar_dev_<N>`, not
      `polar`:
      ```bash
      psql -U polar -d polar_dev_<N>
      ```
      
      ## Common workflows
      
      **Daily development:**
      ```bash
      dev docker up -d --wait     # start and wait until serving
      dev docker ps               # confirm status
      dev docker logs -f api      # watch as you work
      dev docker down             # stop when done (data persists)
      ```
      
      **After a git pull:** hot-reload handles most changes. If deps changed:
      ```bash
      dev docker restart api worker web        # picks up new deps via startup uv sync / pnpm
      # only if a Dockerfile or system dep changed:
      dev docker up -b -d api worker web
      ```
      
      **Run tests:**
      
      Frontend unit tests run in the web container:
      ```bash
      dev docker exec web pnpm test
      ```
      
      Backend tests do **not** run in the `dev docker` api container. The suite's
      session-wide `empty_test_bucket` fixture hard-requires S3 at
      `http://127.0.0.1:9000` with `testing`-prefixed buckets, but containers can't
      reach `localhost:9000` on the host — they use `minio:9000` internally with
      per-instance `polar-s3-<N>` buckets. Run backend tests on the host with the
      standard setup in `server/AGENTS.md` (`uv run task test`).
      
      **Database operations:**
      ```bash
      dev docker exec api uv run alembic upgrade head
      dev docker exec api uv run alembic downgrade -1
      dev docker exec db psql -U polar -d polar_dev_<N>
      ```
      
      Find `<N>` (and the real ports) for the current worktree with `dev docker ports`.
      
    • start-environment.md 3 KB
      ---
      title: Starting the Local Environment
      category: Operations
      tags: docker, start, development
      ---
      
      # Starting the Local Environment
      
      `dev docker up` starts shared infra (if it isn't already running) plus this
      worktree's app stack. It **defaults to detached** (`-d` is on by default), so
      plain `dev docker up` returns once containers are created.
      
      ## Basic commands
      
      **Start in background (the default):**
      
      ```bash
      dev docker up -d
      ```
      
      **Start and block until app services are healthy:**
      
      ```bash
      dev docker up -d --wait
      ```
      
      `--wait` returns only once api answers `/healthz` and web responds. Prefer it in
      scripts and tooling so "up finished" means "actually serving" rather than "the
      container was created" — on first boot the app keeps compiling and migrating for
      a while after the container starts.
      
      **Start in the foreground and stream logs:**
      
      ```bash
      dev docker up --no-detach
      ```
      
      Shared infra still starts detached; only the app stack is attached, so Ctrl+C
      stops the app stack.
      
      **Start specific services:**
      
      ```bash
      dev docker up -d api            # api (+ shared infra)
      dev docker up -d web            # web (+ shared infra)
      dev docker up -d api worker     # api and worker
      ```
      
      ## Options
      
      | Flag              | Description                                          |
      | ----------------- | ---------------------------------------------------- |
      | `-d` / `--detach` | Detached / background (default)                      |
      | `--no-detach`     | Foreground; stream app logs until you stop it        |
      | `--wait`          | Block until app services are healthy (detached only) |
      | `-b` / `--build`  | Rebuild images before starting                       |
      | `--pull`          | Refresh base images before building (see below)      |
      | `--monitoring`    | Include Prometheus and Grafana in shared infra       |
      | `-i N`            | Target instance N explicitly (usually auto-detected) |
      
      ## Rebuilding with fresh bases
      
      ```bash
      dev docker up -b --pull -d
      ```
      
      `-b` rebuilds the images, and `--pull` refreshes the base images first. Use
      `--pull` after a `.python-version` bump or when a rebuild alone still boots the
      old interpreter — a cached `FROM` layer can otherwise pin you to a stale base.
      See troubleshooting for the "No interpreter found for Python X" symptom.
      
      ## First-time startup
      
      First run is slow because it does real work inside the containers:
      
      1. Build the api/web images
      2. `uv sync` (api/worker) and `pnpm install` (web) — several minutes
      3. Bootstrap the per-instance DB and MinIO buckets
      4. Run migrations and load seed data
      5. Services become healthy
      
      `dev docker up -d --wait` is the cleanest way to wait this out.
      
      ## Finding the URLs
      
      Ports are per-instance, so don't assume 3000/8000. Ask:
      
      ```bash
      dev docker ports          # human-readable
      dev docker ports --json   # for tooling
      ```
      
      For instance 0 the app is at http://localhost:3000 (web) and
      http://localhost:8000 (api, docs at `/docs`). MinIO exposes ports 9000 (API)
      and 9001 (console) on localhost; db and redis have no host port — reach them
      with `dev docker exec <service> ...`.
      
    • stop-environment.md 1.6 KB
      ---
      title: Stopping the Local Environment
      category: Operations
      tags: docker, stop, cleanup
      ---
      
      # Stopping the Local Environment
      
      Commands act on **this instance's app stack** by default; the shared infra
      (postgres/redis/minio/tinybird) keeps running so other worktrees aren't
      disrupted. Reach for the shared stack explicitly only when you mean to.
      
      ## Stop services
      
      **Stop this instance's app stack (keeps data + shared infra):**
      ```bash
      dev docker down
      ```
      
      **Stop the app stack and the shared infra too:**
      ```bash
      dev docker down --all
      ```
      
      **Stop a specific instance:**
      ```bash
      dev docker down -i 1
      ```
      
      ## Cleanup (destructive)
      
      **Reset this instance** — removes its api/worker/web containers and their
      build/cache volumes. Shared infra and its data are left intact:
      ```bash
      dev docker cleanup -f
      ```
      
      **Wipe everything shared** — this destroys postgres data, MinIO objects,
      Tinybird events, and prometheus/grafana state for **every** instance on the
      machine:
      ```bash
      dev docker cleanup --all -f
      ```
      
      Use per-instance cleanup for a fresh app stack; use `--all` only when you truly
      want to reset the machine-wide data. To drop just one instance's DB/buckets
      without touching others, delete its worktree and run `dev docker prune`.
      
      ## Restart vs stop/start
      
      **Restart (keeps containers, fastest):**
      ```bash
      dev docker restart          # all app services
      dev docker restart api      # one service
      ```
      
      **Stop/start (recreates containers):**
      ```bash
      dev docker down
      dev docker up -d
      ```
      
      Prefer `restart` for quick changes. Recreate when you've changed Docker config,
      environment variables, or a container is wedged.
      
    • troubleshooting.md 6 KB
      ---
      title: Troubleshooting Common Issues
      category: Debugging
      tags: troubleshooting, errors, fixes
      ---
      
      # Troubleshooting Common Issues
      
      ## Service Won't Start
      
      **Check if ports are in use:**
      ```bash
      lsof -i :8000    # API port
      lsof -i :3000    # Web port
      lsof -i :5432    # Database port
      ```
      
      **Verify Docker is running:**
      ```bash
      docker info
      ```
      
      **Check container logs:**
      ```bash
      dev docker logs api
      dev docker logs web
      ```
      
      **Try stop and restart:**
      ```bash
      dev docker down
      dev docker up -d
      ```
      
      ## Docker Daemon Not Running
      
      `dev docker up` preflights the daemon and prints a friendly message if it's
      down. If you see it, start Docker (OrbStack or Docker Desktop) and retry. A raw
      `docker.sock: no such file or directory` from another command means the same
      thing.
      
      ## `No interpreter found for Python 3.14.x` (stale base image)
      
      The api container exits during the JWKS/`uv sync` step with something like:
      
      ```
      error: No interpreter found for Python 3.14.6 in managed installations or search path
      ```
      
      This is a **stale base image**: the cached api image ships an older Python patch
      than `server/.python-version` requires, and a plain rebuild reuses the cached
      `FROM` layer. Refresh the bases and rebuild:
      
      ```bash
      dev docker up -b --pull -d
      ```
      
      `--pull` re-fetches `python:3.14.x-slim` and `uv` so the rebuilt image satisfies
      the pin. (The Dockerfile pins the patch to keep this rare, but a `.python-version`
      bump can still outrun a cached image.)
      
      ## First boot fails cloning a git dependency
      
      `uv sync` clones a git dependency (the dramatiq fork) over the network, and
      Docker's embedded DNS can flap transiently:
      
      ```
      failed to fetch commit ... Could not resolve host: github.com
      ```
      
      Startup now retries `uv sync` a few times, so this usually self-heals. If the
      container still exited, just restart it — the retry runs again with a fresh
      resolver:
      
      ```bash
      dev docker up -d api worker
      ```
      
      ## Database Connection Failed
      
      **Wait for health check (up to 40 seconds on first start)**
      
      **Check db container:**
      ```bash
      dev docker ps
      dev docker logs db
      ```
      
      **Verify database is healthy:**
      ```bash
      # Each instance has its own database: polar_dev_<instance-number>
      dev docker exec db psql -U polar -d polar_dev_<N> -c "SELECT 1"
      ```
      
      Shared infra (db/redis/minio) is on the `polar-shared` Docker network with no
      host port — reach it through `dev docker exec` or `docker exec polar-shared-<svc>-1`.
      
      ## Hot-Reload Not Working
      
      **Check file mounting:**
      ```bash
      dev docker shell api
      ls -la /app/server/polar/
      ```
      
      **Restart the service:**
      ```bash
      dev docker restart api
      ```
      
      **If still broken, rebuild:**
      ```bash
      dev docker build api
      dev docker restart api
      ```
      
      ## Out of Memory
      
      **Check Docker memory settings** (should be 8GB+)
      
      **Stop unused instances:**
      ```bash
      dev docker down -i 1
      dev docker down -i 2
      ```
      
      **Clean up Docker:**
      ```bash
      docker system prune
      ```
      
      ### `ERR_PNPM_ENOMEM` on first `dev docker up`
      
      On a fresh worktree the api container (building the email renderer) and the
      web container (installing frontend deps) can both run `pnpm install` at the
      same time and OOM. Symptom in `docker logs polar-app-<N>-api-1`:
      
      ```
      ERR_PNPM_ENOMEM  ENOMEM: not enough memory, copyfile ...
      ```
      
      `docker ps` then shows api/web with `Exited (1)` while worker is still `Up`.
      
      **Fix — restart the failed containers, pnpm resumes from its cache:**
      
      ```bash
      docker start polar-app-<N>-api-1 polar-app-<N>-web-1
      ```
      
      Wait for `/healthz` on the API port (printed by `dev docker up`) to come up
      before continuing. Bumping Docker Desktop's memory above 8 GB or starting
      services one at a time (`dev docker up -d api`, then `web`) also avoids the
      clash.
      
      ## MinIO/S3 Issues
      
      Shared MinIO exposes ports 9000 (API) and 9001 (console) on localhost. Access
      the console at http://localhost:9001 with credentials `polar-development` /
      `polar123456789`.
      
      **Check minio-setup logs:**
      ```bash
      dev docker logs minio-setup
      ```
      
      **List this instance's buckets:**
      ```bash
      dev docker exec minio mc alias set local http://localhost:9000 \
        polar-development polar123456789
      dev docker exec minio mc ls local
      ```
      Buckets are per-instance: `polar-s3-<N>` and `polar-s3-public-<N>`.
      
      ## Frontend Build Errors
      
      **Clear Next.js cache:**
      ```bash
      dev docker shell web
      rm -rf .next
      exit
      dev docker restart web
      ```
      
      **Reinstall dependencies:**
      ```bash
      dev docker shell web
      pnpm install
      exit
      dev docker restart web
      ```
      
      ## Migration Issues
      
      **Check current migration state:**
      ```bash
      dev docker shell api
      uv run alembic current
      ```
      
      **Run pending migrations:**
      ```bash
      uv run alembic upgrade head
      ```
      
      **Rollback if needed:**
      ```bash
      uv run alembic downgrade -1
      ```
      
      ## Stale Connections After Shared DB Recycle
      
      If `polar-shared-db-1` was recreated (e.g. you ran `dev docker down` on the
      shared stack, or it was replaced by an unrelated `docker compose` run), the
      running api/worker still hold connections to the old container and surface
      errors like:
      
      ```
      asyncpg.exceptions._base.InterfaceError: connection is closed
      sqlalchemy.dialects.postgresql.asyncpg.InterfaceError
      ```
      
      **Fix — restart api and worker so the pool reconnects:**
      
      ```bash
      docker restart polar-app-<N>-api-1 polar-app-<N>-worker-1
      ```
      
      ## Don't Mix `dev docker` and Bare `docker compose`
      
      `dev docker` runs the shared infra under the project name `polar-shared` on
      the `polar-shared` network. Running `cd server && docker compose up` from the
      same checkout creates a parallel stack on `server_default` with `server-`
      prefixed containers. They don't conflict by name, but: they double the
      memory footprint, `docker ps` shows two of everything, and a later
      `docker compose down` on one stack will leave the other half running with
      broken cross-network references.
      
      Pick one. For everything in this skill, prefer `dev docker`.
      
      ## Complete Reset
      
      **When all else fails:**
      ```bash
      dev docker cleanup -f
      dev docker up -b -d
      ```
      
      This removes all data and rebuilds from scratch.
      
      ## Getting Help
      
      1. Check logs: `dev docker logs`
      2. Check status: `dev docker ps`
      3. Check Docker: `docker info`
      4. Try restart: `dev docker restart`
      5. Try cleanup: `dev docker cleanup`
      
    • view-logs.md 1.9 KB
      ---
      title: Viewing Service Logs
      category: Debugging
      tags: logs, debugging, troubleshooting
      ---
      
      # Viewing Service Logs
      
      `dev docker logs` **follows by default** (`-f` is on). It auto-routes by service
      name: app services (api/worker/web) come from this instance's `polar-app-<N>`
      project, and infra services (db/redis/minio/...) from `polar-shared`.
      
      ## Commands
      
      **Follow all app logs (the default):**
      ```bash
      dev docker logs
      ```
      
      **Print current logs and exit (don't follow):**
      ```bash
      dev docker logs --no-follow
      dev docker logs --no-follow api
      ```
      Use `--no-follow` in scripts or when you just want a snapshot — otherwise the
      command blocks streaming. (This maps to a foreground `docker compose logs`.)
      
      **Specific service logs:**
      ```bash
      dev docker logs api
      dev docker logs worker
      dev docker logs web
      dev docker logs db       # auto-routed to polar-shared
      dev docker logs redis
      dev docker logs minio
      ```
      
      **Follow a specific service:**
      ```bash
      dev docker logs -f api
      ```
      
      **Instance-specific logs:**
      ```bash
      dev docker logs -i 1 api
      ```
      
      ## Log Interpretation
      
      ### API Logs
      
      **Successful request:**
      ```
      INFO: 127.0.0.1:54321 - "GET /api/v1/users HTTP/1.1" 200
      ```
      
      **Error:**
      ```
      ERROR: Exception in endpoint
      Traceback (most recent call last):
        ...
      ```
      
      ### Worker Logs
      
      **Task started:**
      ```
      [dramatiq.MainProcess] Task started: polar.tasks.example:process
      ```
      
      **Task completed:**
      ```
      [dramatiq.MainProcess] Task completed in 0.123s
      ```
      
      ### Web Logs
      
      **Page compiled:**
      ```
      ✓ Compiled /dashboard in 234ms
      ```
      
      **Error:**
      ```
      Error: Cannot find module 'xxx'
      ```
      
      ## Debugging Tips
      
      1. **Follow API logs during development:**
         ```bash
         dev docker logs -f api
         ```
      
      2. **Check worker for background job issues:**
         ```bash
         dev docker logs -f worker
         ```
      
      3. **Database issues - check db:**
         ```bash
         dev docker logs db
         ```
      
      4. **Startup issues - check all:**
         ```bash
         dev docker logs
         ```
      
  • SKILL.md 5.4 KB
    ---
    name: polar-local-environment
    description: "This skill should be used when setting up or managing Polar local development environment with Docker."
    license: MIT
    metadata:
      author: polar
      version: "1.1.0"
    ---
    
    # Local Environment Skill
    
    Helps manage the Polar local development environment through the `dev docker`
    CLI. Use it to start, stop, debug, or reason about the local stack.
    
    ## The two-part model
    
    `dev docker` deliberately splits the stack so many worktrees can share one set
    of heavy infra:
    
    - **Shared infra** — one copy per machine, Docker project `polar-shared`:
      postgres, redis, minio, tinybird, and optional prometheus/grafana. Postgres
      and redis publish no host ports; MinIO exposes 9000/9001 for browser
      uploads. Use `dev docker exec <service> ...` for services without host ports.
    - **Per-instance app stack** — one per worktree, project `polar-app-<N>`: api,
      worker, web. Only **api and web** publish host ports, offset per instance so
      worktrees don't collide.
    
    Knowing this prevents the most common confusion: there is no `localhost:5432`
    for the database (it lives in the shared stack and is reached through
    `dev docker exec db ...`). MinIO does expose ports 9000/9001 for browser
    uploads and console access.
    
    ## Instance auto-detection
    
    `dev docker` auto-detects the instance for the current worktree, so `-i` is
    rarely needed. Priority:
    
    1. `POLAR_DOCKER_INSTANCE` pinned in `dev/docker/.env.docker` (`dev docker set-instance N`)
    2. `CONDUCTOR_PORT` env var → `(port - 55000) / 10 + 1`
    3. The cross-worktree registry (`~/.config/polar/docker-instances.json`)
    4. Otherwise the lowest free number, then registered
    
    Run `dev docker ports` to see the resolved instance and its URLs (add `--json`
    for tooling). To wire this worktree into Claude Code's preview, run
    `dev docker launch-json`, which writes a per-instance `.claude/launch.json` with
    the correct ports (it's gitignored, so regenerate after `set-instance`).
    
    ## When to use
    
    - Start / stop / restart the local environment
    - View logs or debug a service that won't come up
    - Run several isolated worktree instances in parallel
    - Understand the service architecture or find a service's real port
    - Diagnose container or first-boot errors
    
    ## Quick reference
    
    | Task | Command |
    |------|---------|
    | Start full stack (background) | `dev docker up -d` |
    | Start and block until healthy | `dev docker up -d --wait` |
    | Start in foreground (stream logs) | `dev docker up --no-detach` |
    | Rebuild with fresh base images | `dev docker up -b --pull -d` |
    | Show this instance's ports/URLs | `dev docker ports` (`--json` for tooling) |
    | Write Claude Code preview config | `dev docker launch-json` |
    | Stop app stack | `dev docker down` |
    | Stop app **and** shared infra | `dev docker down --all` |
    | Follow logs | `dev docker logs -f [service]` |
    | Print logs and exit | `dev docker logs --no-follow [service]` |
    | Status | `dev docker ps` |
    | Restart a service | `dev docker restart <service>` |
    | Shell into a service | `dev docker shell <service>` |
    | One-off command in a service | `dev docker exec <service> <cmd>` |
    | Reset this instance | `dev docker cleanup -f` |
    | Wipe ALL shared data | `dev docker cleanup --all -f` |
    | List every instance | `dev docker list` |
    | With monitoring | `dev docker up --monitoring -d` |
    
    ## Services
    
    | Service | Project | Host port (instance 0 / N) | Notes |
    |---------|---------|----------------------------|-------|
    | api | `polar-app-<N>` | 8000 / 8100+N | FastAPI; `/healthz` healthcheck |
    | web | `polar-app-<N>` | 3000 / 3100+N | Next.js; healthchecked |
    | worker | `polar-app-<N>` | none | Background jobs |
    | db | `polar-shared` | none (`exec`) | PostgreSQL; DB `polar_dev_<N>` |
    | redis | `polar-shared` | none (`exec`) | Redis DB index = N |
    | minio | `polar-shared` | 9000, 9001 | S3; buckets `polar-s3-<N>`; console at 9001 |
    | tinybird | `polar-shared` | none (`exec`) | Analytics |
    | prometheus / grafana | `polar-shared` | none (`exec`) | `--monitoring` only |
    
    Discover the exact host ports for the current worktree with `dev docker ports`.
    
    ## Instance port mapping
    
    Only api and web get host ports: `Port = Base + Instance` (Base 8100 for api,
    3100 for web) for instances 1–99. Instance 0 uses the legacy `8000` / `3000`.
    
    | Instance | API | Web |
    |----------|-----|-----|
    | 0 | 8000 | 3000 |
    | 1 | 8101 | 3101 |
    | 2 | 8102 | 3102 |
    | 5 | 8105 | 3105 |
    
    Everything else is per-instance but not on a host port: database `polar_dev_<N>`,
    redis DB index `<N>`, buckets `polar-s3-<N>` / `polar-s3-public-<N>`. Reach them
    via `dev docker exec <service>` or `docker exec polar-shared-<service>-1`.
    
    ## Rules index
    
    | Rule | Category | Description |
    |------|----------|-------------|
    | [service-architecture](rules/service-architecture.md) | Reference | Service details, ports, healthchecks |
    | [start-environment](rules/start-environment.md) | Operations | Starting the stack (flags, `--wait`, `--pull`) |
    | [stop-environment](rules/stop-environment.md) | Operations | Stopping and cleanup (app vs shared) |
    | [manage-instances](rules/manage-instances.md) | Operations | Parallel worktree instances |
    | [view-logs](rules/view-logs.md) | Debugging | Viewing service logs |
    | [shell-and-workflows](rules/shell-and-workflows.md) | Operations | Shell access and common dev workflows |
    | [troubleshooting](rules/troubleshooting.md) | Debugging | Common errors and fixes |
    | [payment-testing](rules/payment-testing.md) | Operations | Login codes, Stripe webhooks, backoffice |
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related