Headless vs Headed
PinchTab instances can run Chrome in two modes: Headless (no visible UI) and Headed (visible window). Understanding the tradeoffs helps you choose the right mode for your workflow.
Note: You run a single orchestrator (pinchtab), then create instances with different modes via the API.
Headless Mode (Default)
Chrome runs without a visible window. All interactions happen via the API.
# Start orchestrator (once)pinchtab# Terminal 2: Create headless instance (default)pinchtab instance launch# Or via curlcurl -X POST http://localhost:9867/instances/launch \ -H "Content-Type: application/json" \ -d '{"mode": "headless"}'# Response{ "id": "inst_0a89a5bb", "port": "9868", "headless": true, "status": "starting"}
Characteristics
- ✅ No UI overhead — No window rendering, faster operations
- ✅ Scriptable — Perfect for automation, CI/CD, unattended workflows
- ✅ Lightweight — Lower CPU/memory than headed mode
- ✅ Remote-friendly — Works over SSH, Docker, cloud servers
- ❌ Can’t see what’s happening — Debugging requires screenshots or logs
Use Cases
- AI agents — Automated tasks, form filling, data extraction
- CI/CD pipelines — Testing, scraping, report generation
- Cloud servers — VPS, Lambda, container orchestration
- Production workflows — Long-running tasks, batch processing
Headed Mode
Chrome runs with a visible window that you can see and interact with.
# Start orchestrator (once)pinchtab# Terminal 2: Create headed instancepinchtab instance launch --mode headed# Or via curlcurl -X POST http://localhost:9867/instances/launch \ -H "Content-Type: application/json" \ -d '{"mode": "headed"}'# Response{ "id": "inst_1b9a5dcc", "port": "9869", "headless": false, "status": "starting"}
Characteristics
- ✅ Visual feedback — See exactly what’s happening in real-time
- ✅ Debuggable — Watch the browser, inspect elements, debug flows
- ✅ Interactive — You can click, type, scroll in the window manually
- ✅ Development-friendly — Great for testing, debugging, prototyping
- ❌ Slower — Window rendering adds latency
- ❌ Requires a display — Needs X11/Wayland on Linux, native desktop on macOS/Windows
- ❌ Resource-heavy — More CPU/memory for rendering
Use Cases
- Development & debugging — Build and test automation scripts
- Local testing — Verify workflows before production
- Live demonstrations — Show what your automation is doing
- Interactive debugging — Watch and modify behavior in real-time
- Manual collaboration — A human watches and guides the automation
Viewing Headed Instances in the Dashboard
The Pinchtab dashboard lets you monitor both headless and headed instances:

When you launch a headed instance:
- Mode field shows “headed” (vs “headless”)
- Port indicates which port the instance is listening on
- Status shows “Running” with green badge
- STOP button allows graceful shutdown
The dashboard automatically detects the mode and displays the appropriate controls. You can see the real Chrome window alongside the dashboard for visual verification.
Side-by-Side Comparison
| Aspect | Headless | Headed |
|---|---|---|
| Visibility | ❌ Invisible | ✅ Visible window |
| Speed | ✅ Fast | ❌ Slower (2-3x) |
| Resource usage | ✅ Light | ❌ Heavy |
| Debugging | ❌ Hard | ✅ Easy |
| Display required | ❌ No | ✅ Yes (X11/Wayland/native) |
| Automation | ✅ Perfect | ⚠️ Can interact manually |
| CI/CD | ✅ Ideal | ❌ Not practical |
| Development | ⚠️ Possible | ✅ Recommended |
When to Use Headless
Use headless for:
- Production automation (scripts, agents, workflows)
- CI/CD pipelines (GitHub Actions, GitLab CI, Jenkins)
- Unattended execution (servers, containers, cloud functions)
- High-throughput tasks (scraping 1000s of pages)
- Cost-sensitive environments (minimize CPU/memory)
- Long-running processes (24/7 automation)
# Production: Create headless instancepinchtab instance launch# Or multiple headless instances for scalefor i in 1 2 3; do pinchtab instance launch --mode headlessdone# List all instancescurl http://localhost:9867/instances | jq .
When to Use Headed
Use headed for:
- Local development (debugging scripts)
- Testing automation behavior
- Demonstrating workflows to humans
- Prototyping and experimentation
- Interactive debugging (pause and inspect)
- Manual verification before production
# Development: Create headed instance with profilepinchtab profile create dev# Get profile IDDEV_ID=$(pinchtab profiles | jq -r '.[] | select(.name=="dev") | .id')# Start headed instance with profilecurl -X POST http://localhost:9867/instances/start \ -H "Content-Type: application/json" \ -d '{"profileId":"'$DEV_ID'","mode":"headed"}'# Or simpler: start without persistent profilepinchtab instance launch --mode headed
Display Requirements
Display requirements apply to headed instances, not the orchestrator. The orchestrator can run anywhere and create instances in any mode.
On macOS
- Native window system — headed instances work out of the box
-
bash terminal
pinchtab # Orchestrator# Terminal 2:pinchtab instance launch --mode headed
On Linux
- Headless instances: Work anywhere (no display needed)
- Headed instances: Require X11 or Wayland display server
- In a Docker container: Forward
DISPLAYenvironment variable - In a headless server: Use headless mode only
-
bash terminal
# Orchestrator on remote server (SSH)ssh user@server 'pinchtab &'# Create headed instance via X11 forwardingssh -X user@server 'pinchtab instance launch --mode headed'
On Windows
- Native window system — headed instances work out of the box
-
bash terminal
pinchtab # Orchestrator# Terminal 2:pinchtab instance launch --mode headed
In Docker (Headless - Recommended)
# Headless works everywhere, no display needed
FROM pinchtab/pinchtab:latest
CMD ["pinchtab"]
Run with:
docker run -d -p 9867:9867 pinchtab/pinchtab# Create headless instances in the containercurl -X POST http://localhost:9867/instances/launch \ -d '{"mode":"headless"}'
In Docker (Headed - Advanced)
# Headed requires display forwarding
FROM pinchtab/pinchtab:latest
ENV DISPLAY=:0
CMD ["pinchtab"]
Run with X11 forwarding:
docker run \ -e DISPLAY=$DISPLAY \ -v /tmp/.X11-unix:/tmp/.X11-unix:rw \ -p 9867:9867 \ pinchtab/pinchtab# Create headed instances (display forwarded to host)curl -X POST http://localhost:9867/instances/launch \ -d '{"mode":"headed"}'
Best Practices
Development Workflow
# Terminal 1: Start orchestrator (once)pinchtab# Terminal 2: Create headed instance for debuggingDEV=$(pinchtab instance launch --mode headed | jq -r '.id')# Terminal 3: Build and test your automationcurl -X POST http://localhost:9867/instances/$DEV/navigate \ -d '{"url":"https://example.com"}'# Verify behavior in the visible window while you develop# ... iterate on your script ...# When stable, test in headless for productionPROD=$(pinchtab instance launch --mode headless)# Run full test suite against headless instance# ... verify all tests pass ...# Clean uppinchtab instance stop $DEVpinchtab instance stop $PROD
CI/CD Pipeline
# Always headless in CI
test:
script:
# Start orchestrator
- pinchtab &
- sleep 1 # Wait for orchestrator to be ready
# Create headless instance
- INST=$(curl -X POST http://localhost:9867/instances/launch | jq -r '.id')
- sleep 2 # Wait for instance to initialize
# Run tests against headless instance
- npm test
# Cleanup
- curl -X POST http://localhost:9867/instances/$INST/stop
- pkill pinchtab
Multi-Instance Setup (Scale)
# Terminal 1: Start orchestrator (once)pinchtab# Terminal 2: Create multiple headless instances for scalefor i in 1 2 3; do INST=$(pinchtab instance launch --mode headless | jq -r '.id') echo "Created headless instance: $INST"done# List all instancescurl http://localhost:9867/instances | jq .# Response: 3 independent headless instances# [# {"id": "inst_xxx", "port": "9868", "headless": true, "status": "running"},# {"id": "inst_yyy", "port": "9869", "headless": true, "status": "running"},# {"id": "inst_zzz", "port": "9870", "headless": true, "status": "running"}# ]# Terminal 3: Route requests to instances via orchestrator# All operations go through: http://localhost:9867/instances/{id}/...
Multi-Instance with Mixed Modes
# Terminal 1: Start orchestratorpinchtab# Create persistent profilespinchtab profile create alicepinchtab profile create bobpinchtab profile create dev# Get profile IDsALICE_ID=$(pinchtab profiles | jq -r '.[] | select(.name=="alice") | .id')BOB_ID=$(pinchtab profiles | jq -r '.[] | select(.name=="bob") | .id')DEV_ID=$(pinchtab profiles | jq -r '.[] | select(.name=="dev") | .id')# Production: Multiple headless instancesALICE_INST=$(curl -X POST http://localhost:9867/instances/start \ -d '{"profileId":"'$ALICE_ID'","mode":"headless"}' | jq -r '.id')BOB_INST=$(curl -X POST http://localhost:9867/instances/start \ -d '{"profileId":"'$BOB_ID'","mode":"headless"}' | jq -r '.id')# Development: One headed instance for debuggingDEV_INST=$(curl -X POST http://localhost:9867/instances/start \ -d '{"profileId":"'$DEV_ID'","mode":"headed"}' | jq -r '.id')# Each instance is isolated with its own profile/cookies
Dashboard View of Mixed Instances
In the Profiles tab, you’ll see all your instances with their live Chrome windows:

The dashboard displays:
- Left panel — Profile metadata (name, size, account, status)
- Right panel — Live Chrome window when in headed mode
- Status badge — Running instance on specific port
- Port number — Where this instance is accessible via API
Each instance is completely isolated — different profiles, different cookies, different Chrome processes, different ports.
Performance Tips
For Headless Instances (Already Optimized)
- Headless is the default and already optimized
- Most operations are fast
- For high-throughput: Create multiple headless instances and load-balance
For Headed Instances (Optimize for Dev)
- Close unused tabs to reduce rendering load
- Use snapshots instead of screenshots when possible (faster)
- Take screenshots sparingly (they’re rendered + encoded, slower)
- Minimize window size (less to render)
- For faster debugging: Use headless for bulk work, headed only for visual debugging
For Scale
- Use headless instances (no display overhead)
- Create multiple instances (one per worker/agent)
- Load-balance via
/instanceslist and round-robin routing - Monitor instance health via
GET /instances/{id}
Monitoring Instance Activity
The Pinchtab dashboard includes an Agents tab that shows all activity happening across your instances in real-time:

The activity feed displays:
- Timestamp — When the operation occurred
- Agent/User — Which client made the request
- Method — GET, POST, etc.
- Endpoint — Which API endpoint was called
- Timing — How long the operation took
- Status — HTTP response code (200, 404, 500, etc.)
Use the activity feed to:
- Monitor what your agents are doing in real-time
- Debug failed operations (check status codes)
- Understand performance (see timing metrics)
- Track all API calls across all instances
Filter by operation type (Navigate, Snapshot, Actions, All) to focus on specific actions.
Troubleshooting
Headed Instance Not Opening a Window
Root cause: Display server not available
On Linux:
# Check if DISPLAY is setecho $DISPLAY# If empty or :0 unavailable, you need X11 or Wayland# Fallback to headless: Use --mode headless insteadpinchtab instance launch --mode headless
On macOS/Windows:
- Verify Chrome/Chromium is installed
- Verify OS has native display server
Headed Instance Too Slow
Solution: Use headless for production, headed only for development
# Switch from headed to headlessINST=$(pinchtab instance launch --mode headed)# ... do some debugging ...pinchtab instance stop $INST# Create headless for production testingINST=$(pinchtab instance launch --mode headless)
Headless Instance But Need to Debug
Solution: Use API operations to see what’s happening
# Get page structure (DOM)curl http://localhost:9867/instances/$INST/snapshot | jq .# Extract all textcurl http://localhost:9867/instances/$INST/text# Take screenshotTAB_ID=$(curl -s http://localhost:9867/instances/$INST/tabs | jq -r '.[0].id')curl "http://localhost:9867/tabs/$TAB_ID/screenshot" > page.png# Get page title and URLcurl http://localhost:9867/instances/$INST/tabs | jq '.[] | {title, url}'
Instance Initialization Slow
Symptom: Instance stuck in “starting” state
Solution: Wait for instance to reach “running” state before making requests
# Check instance statuscurl http://localhost:9867/instances/$INST | jq '.status'# Poll until running (first request initializes Chrome: 5-20 seconds)while [ "$(curl -s http://localhost:9867/instances/$INST | jq -r '.status')" != "running" ]; do sleep 0.5done# Now safe to usecurl -X POST http://localhost:9867/instances/$INST/navigate \ -d '{"url":"https://example.com"}'
Can’t Connect to Instance
Symptom: 503 error when trying to navigate/snapshot
Causes:
- Instance still initializing (check status)
- Chrome crashed (check logs)
- Port already in use (specify different port)
Debug:
# Get instance status and error detailscurl http://localhost:9867/instances/$INST | jq .# Get instance logscurl http://localhost:9867/instances/$INST/logs# Check if port is availablelsof -i :9868 # Shows what's using the port
Summary
- Headless instances = Fast, scriptable, production-ready (no display overhead)
- Headed instances = Visible, debuggable, development-friendly (requires display)
Key Points
- Start orchestrator once:
pinchtab - Create instances with mode via API:
instance launch --mode headless|headed - Profiles are optional but preserve state across mode switches
- You can have both headless and headed instances running simultaneously
- Switch modes by stopping one instance and creating another
- If using profiles, login state and cookies persist across mode switches
Quick Reference
# Orchestrator (runs once)pinchtab# Create headless instance (production)pinchtab instance launch --mode headless# Create headed instance (development)pinchtab instance launch --mode headed# Create with persistent profile (state preserved on mode switch)pinchtab profile create workcurl -X POST http://localhost:9867/instances/start \ -d '{"profileId":"work-id","mode":"headed"}'
Next: See Instance API Reference for complete instance management details.