From b5948e5a7da28be74fca5c7493af47265d6beb4c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 09:51:15 +0000 Subject: [PATCH 1/5] docs: add guide on debugging Actors on the Apify platform Covers the platform's networking constraints, the browser-based Actor debugger for Node.js and Python, and tunneling a debug port to a local IDE with wstunnel. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- .../platform/actors/development/debugging.md | 227 ++++++++++++++++++ sources/platform/actors/development/index.md | 5 + 2 files changed, 232 insertions(+) create mode 100644 sources/platform/actors/development/debugging.md diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md new file mode 100644 index 0000000000..cac7467cc4 --- /dev/null +++ b/sources/platform/actors/development/debugging.md @@ -0,0 +1,227 @@ +--- +title: Debug Actors on the Apify platform +sidebar_label: Debugging +description: Attach a debugger to an Actor run on the Apify platform. Use the browser-based Actor debugger, or tunnel the debug port to your local IDE with wstunnel. +sidebar_position: 8 +slug: /actors/development/debugging +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +--- + +Most bugs reproduce locally with `apify run` and your IDE's debugger. Some don't. They depend on the platform's proxies, memory limits, environment variables, or data that exists only in a real run. This guide shows two ways to attach a debugger to an Actor run on the platform. + +## Infrastructure constraints + +An Actor run is a Docker container on a shared worker machine. You can't open a TCP connection to the container, so there is no SSH and no port forwarding. + +The one inbound channel is the [container web server](./programming_interface/container_web_server.md). Whatever listens on `ACTOR_WEB_SERVER_PORT` (default `4321`) inside the container is reachable at the run's container URL, `https://.runs.apify.net`. The platform forwards HTTP and WebSocket traffic to that port. It doesn't forward raw TCP. + +Debuggers speak raw TCP. The Node.js inspector listens on port `9229`, debugpy on `5678`. Both options in this guide run a small server inside the container that bridges the debug port over WebSocket on the web server port. + +This shapes what debugging on the platform looks like: + +- Anyone who can reach the container URL can reach the debugger. A debugger is a code-execution channel into the run, with access to its environment, including `APIFY_TOKEN`. +- A run paused on a breakpoint keeps consuming compute and counts toward the run timeout. Set a generous timeout and abort the run when you finish. +- A [migration](./builds_and_runs/state_persistence.md) restarts the container and drops the debug session. +- Breakpoints bind to the deployed code. Keep your local checkout at the same commit as the build you debug. + +## Choose an option + +| | Actor debugger | wstunnel | +| --- | --- | --- | +| Setup | Change the Dockerfile `CMD` | Add a binary, start it next to the debugger | +| Client | Any browser | wstunnel client and your IDE | +| Languages | Node.js/TypeScript, Python | Anything with a TCP debug protocol | +| Best for | Quick look at a run, no local setup | Full IDE experience | + +## Actor debugger + +The [actor-debugger](https://github.com/apify/actor-debugger) package launches your Actor under its native debugger and serves a debugger UI on the web server port. You open one URL from the run log in your browser. Nothing runs on your machine. + + + + +Install the package and swap the entrypoint in your Dockerfile: + +```dockerfile +RUN npm install actor-debugger + +# Replaces the normal entrypoint, for example CMD ["npm", "start"] +CMD ["npx", "actor-debugger", "--brk"] +``` + +The launcher finds your entrypoint from `scripts.start` or `main` in `package.json`, or from conventional paths like `dist/main.js`. Pass a path to override it: `CMD ["npx", "actor-debugger", "dist/main.js"]`. + +The run log prints a URL in this form: + +```text +https://.runs.apify.net/devtools/js_app.html?wss=.runs.apify.net/ +``` + +That page is Chrome DevTools connected to your Actor. TypeScript sources show up through source maps. The launcher inlines external `.js.map` files at startup, so any `tsc` build with `sourceMap` or `inlineSourceMap` enabled works. + + + + +Install the package and swap the entrypoint in your Dockerfile: + +```dockerfile +RUN pip install actor-debugger + +# Replaces the normal entrypoint, for example CMD ["python3", "-m", "src"] +CMD ["python3", "-m", "actor_debugger", "--brk"] +``` + +The launcher finds the runnable package in the working directory, which covers the Apify Python templates. Pass the entrypoint to override it: `CMD ["python3", "-m", "actor_debugger", "-m", "src"]` or `CMD ["python3", "-m", "actor_debugger", "main.py"]`. + +The run log prints a URL in this form: + +```text +https://.runs.apify.net/ui/ +``` + +That page is a debugger UI for [debugpy](https://github.com/microsoft/debugpy). Select a line number to set a breakpoint, then step, inspect variables, and evaluate expressions in the paused frame. + + + + +`--brk` pauses the Actor on its first line until you attach. Drop it to let the Actor run and attach mid-flight. To turn debugging off, restore the original `CMD` and rebuild. + +## wstunnel + +[wstunnel](https://github.com/erebe/wstunnel) tunnels TCP over WebSocket. The server runs inside the container on the web server port. The client runs on your machine and exposes the remote debug port on `localhost`. Your IDE attaches to `localhost` as if the Actor ran there. This works for any language with a TCP debug protocol. + +### Step 1: Add wstunnel to the image + +Download the static release binary in your Dockerfile. The Apify base images differ in what download tool they ship. + + + + +```dockerfile +FROM apify/actor-node:24 + +# Alpine base image: use wget +ARG WSTUNNEL_VERSION=10.7.1 +RUN wget -qO- "https://github.com/erebe/wstunnel/releases/download/v${WSTUNNEL_VERSION}/wstunnel_${WSTUNNEL_VERSION}_linux_amd64.tar.gz" \ + | tar -xz -C /usr/local/bin wstunnel +``` + + + + +```dockerfile +FROM apify/actor-python:3.13 + +# Debian base image: use curl +ARG WSTUNNEL_VERSION=10.7.1 +RUN curl -fsSL "https://github.com/erebe/wstunnel/releases/download/v${WSTUNNEL_VERSION}/wstunnel_${WSTUNNEL_VERSION}_linux_amd64.tar.gz" \ + | tar -xz -C /usr/local/bin wstunnel +``` + +Add `debugpy` to your `requirements.txt`. + + + + +### Step 2: Start the tunnel and the debugger + +Define a `DEBUG_SECRET` [environment variable](./programming_interface/environment_variables.md) in the Actor version and mark it as secret. wstunnel accepts only WebSocket upgrades whose path starts with this value, which keeps random visitors of the container URL out. + +Then replace the `CMD` so the container starts the tunnel server and the Actor under its debugger: + + + + +```dockerfile +CMD ["sh", "-c", "wstunnel server --restrict-to 127.0.0.1:9229 --restrict-http-upgrade-path-prefix \"$DEBUG_SECRET\" \"ws://0.0.0.0:$ACTOR_WEB_SERVER_PORT\" & exec node --inspect-brk=127.0.0.1:9229 dist/main.js"] +``` + +`--inspect-brk` pauses on the first line until a debugger attaches. Use `--inspect` to attach mid-run. + + + + +```dockerfile +CMD ["sh", "-c", "wstunnel server --restrict-to 127.0.0.1:5678 --restrict-http-upgrade-path-prefix \"$DEBUG_SECRET\" \"ws://0.0.0.0:$ACTOR_WEB_SERVER_PORT\" & exec python -m debugpy --listen 127.0.0.1:5678 --wait-for-client -m src"] +``` + +`--wait-for-client` pauses until a debugger attaches. Drop it to attach mid-run. + + + + +`--restrict-to` limits the tunnel to the debug port, so nothing else in the container becomes reachable. `exec` keeps the Actor as the main process, so it still receives the platform's shutdown signals. + +You can also start `wstunnel server` from your Actor code and gate it on an input field. That avoids a separate debug build at the cost of shipping the binary in every build. + +### Step 3: Connect from your machine + +Install wstunnel locally with `brew install wstunnel` or a [release binary](https://github.com/erebe/wstunnel/releases). Start the run, copy the container URL from the run detail page, and open the tunnel: + +```bash +wstunnel client --http-upgrade-path-prefix -L tcp://9229:127.0.0.1:9229 wss://.runs.apify.net +``` + +Use `5678` in place of `9229` for Python. Leave the command running. Port `9229` on `localhost` now leads to the inspector inside the run. + +### Step 4: Attach your IDE + +Attach to `localhost` and map your project root to `/usr/src/app`, the working directory in the Apify base images. + + + + +VS Code `launch.json` configuration: + +```json +{ + "type": "node", + "request": "attach", + "name": "Attach to Apify run", + "address": "localhost", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "/usr/src/app" +} +``` + +In JetBrains IDEs, create an **Attach to Node.js/Chrome** run configuration for `localhost:9229` and map the project root to `/usr/src/app` under **Remote URLs of local files**. Chrome users can open `chrome://inspect` and add `localhost:9229` as a target. + + + + +VS Code `launch.json` configuration: + +```json +{ + "type": "debugpy", + "request": "attach", + "name": "Attach to Apify run", + "connect": { "host": "localhost", "port": 5678 }, + "pathMappings": [ + { "localRoot": "${workspaceFolder}", "remoteRoot": "/usr/src/app" } + ] +} +``` + +In PyCharm 2026.1 or later, create an **Attach to DAP** run configuration for `localhost:5678` with the same path mapping. + + + + +Set a breakpoint and start the configuration. The run resumes under your debugger. + +## Keep debugging out of production + +:::caution Unauthenticated code execution +Both options expose a code-execution endpoint on the container URL. The Actor debugger has no authentication. The wstunnel secret is only as protected as the run that prints or stores it. +::: + +- Keep the debug `CMD` in a dedicated Actor version with its own build tag. Production builds keep their normal entrypoint. +- Never publish a build with a debug entrypoint to Apify Store. +- Run debug builds with [limited permissions](./permissions/index.md) where the Actor allows it. +- Abort the run when you finish. A paused run bills like a running one. diff --git a/sources/platform/actors/development/index.md b/sources/platform/actors/development/index.md index 253a9028c1..2ea91e45a2 100644 --- a/sources/platform/actors/development/index.md +++ b/sources/platform/actors/development/index.md @@ -47,6 +47,11 @@ import CardGrid from "@site/src/components/CardGrid"; to="/actors/development/builds-and-runs" desc="Learn about Actor builds and runs, their lifecycle, versioning, and other properties." /> + Date: Fri, 18 Sep 2026 11:06:55 +0000 Subject: [PATCH 2/5] docs: make clear the two Actor debugging options are alternatives Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- .../platform/actors/development/debugging.md | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index cac7467cc4..9bf7a2fd4f 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -11,7 +11,12 @@ import TabItem from '@theme/TabItem'; --- -Most bugs reproduce locally with `apify run` and your IDE's debugger. Some don't. They depend on the platform's proxies, memory limits, environment variables, or data that exists only in a real run. This guide shows two ways to attach a debugger to an Actor run on the platform. +Most bugs reproduce locally with `apify run` and your IDE's debugger. Some don't. They depend on the platform's proxies, memory limits, environment variables, or data that exists only in a real run. This guide shows two independent ways to attach a debugger to an Actor run on the platform. Pick one: + +- **Actor debugger** - a package you install in the image. You debug from your browser, with no local tooling. +- **wstunnel** - a generic TCP tunnel you add to the image. You debug from your local IDE. + +You never need both. ## Infrastructure constraints @@ -19,7 +24,7 @@ An Actor run is a Docker container on a shared worker machine. You can't open a The one inbound channel is the [container web server](./programming_interface/container_web_server.md). Whatever listens on `ACTOR_WEB_SERVER_PORT` (default `4321`) inside the container is reachable at the run's container URL, `https://.runs.apify.net`. The platform forwards HTTP and WebSocket traffic to that port. It doesn't forward raw TCP. -Debuggers speak raw TCP. The Node.js inspector listens on port `9229`, debugpy on `5678`. Both options in this guide run a small server inside the container that bridges the debug port over WebSocket on the web server port. +Debuggers speak raw TCP. The Node.js inspector listens on port `9229`, debugpy on `5678`. Each option in this guide solves this the same way: a small server inside the container bridges the debug port over WebSocket on the web server port. This shapes what debugging on the platform looks like: @@ -30,6 +35,8 @@ This shapes what debugging on the platform looks like: ## Choose an option +The two options are alternatives, not steps. Compare them and follow only the section for the one you pick. + | | Actor debugger | wstunnel | | --- | --- | --- | | Setup | Change the Dockerfile `CMD` | Add a binary, start it next to the debugger | @@ -37,9 +44,9 @@ This shapes what debugging on the platform looks like: | Languages | Node.js/TypeScript, Python | Anything with a TCP debug protocol | | Best for | Quick look at a run, no local setup | Full IDE experience | -## Actor debugger +## Option 1: Debug in the browser with Actor debugger -The [actor-debugger](https://github.com/apify/actor-debugger) package launches your Actor under its native debugger and serves a debugger UI on the web server port. You open one URL from the run log in your browser. Nothing runs on your machine. +This option is complete on its own and needs no tunnel. The [actor-debugger](https://github.com/apify/actor-debugger) package launches your Actor under its native debugger and serves a debugger UI on the web server port. You open one URL from the run log in your browser. Nothing runs on your machine. @@ -90,9 +97,9 @@ That page is a debugger UI for [debugpy](https://github.com/microsoft/debugpy). `--brk` pauses the Actor on its first line until you attach. Drop it to let the Actor run and attach mid-flight. To turn debugging off, restore the original `CMD` and rebuild. -## wstunnel +## Option 2: Debug from your IDE with wstunnel -[wstunnel](https://github.com/erebe/wstunnel) tunnels TCP over WebSocket. The server runs inside the container on the web server port. The client runs on your machine and exposes the remote debug port on `localhost`. Your IDE attaches to `localhost` as if the Actor ran there. This works for any language with a TCP debug protocol. +This option is complete on its own and doesn't use the actor-debugger package. [wstunnel](https://github.com/erebe/wstunnel) tunnels TCP over WebSocket. The server runs inside the container on the web server port. The client runs on your machine and exposes the remote debug port on `localhost`. Your IDE attaches to `localhost` as if the Actor ran there. This works for any language with a TCP debug protocol. ### Step 1: Add wstunnel to the image @@ -218,7 +225,7 @@ Set a breakpoint and start the configuration. The run resumes under your debugge ## Keep debugging out of production :::caution Unauthenticated code execution -Both options expose a code-execution endpoint on the container URL. The Actor debugger has no authentication. The wstunnel secret is only as protected as the run that prints or stores it. +Either option exposes a code-execution endpoint on the container URL. The Actor debugger has no authentication. The wstunnel secret is only as protected as the run that prints or stores it. ::: - Keep the debug `CMD` in a dedicated Actor version with its own build tag. Production builds keep their normal entrypoint. From e5e8c2de4354468e702362fd1dd3cb248aaec567 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 11:09:36 +0000 Subject: [PATCH 3/5] docs: drop redundant sentence from debugging guide intro Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- sources/platform/actors/development/debugging.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index 9bf7a2fd4f..e1ef46ac90 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -16,8 +16,6 @@ Most bugs reproduce locally with `apify run` and your IDE's debugger. Some don't - **Actor debugger** - a package you install in the image. You debug from your browser, with no local tooling. - **wstunnel** - a generic TCP tunnel you add to the image. You debug from your local IDE. -You never need both. - ## Infrastructure constraints An Actor run is a Docker container on a shared worker machine. You can't open a TCP connection to the container, so there is no SSH and no port forwarding. From df858466eae217e35636b1deb2acf8888c50c52c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 11:10:22 +0000 Subject: [PATCH 4/5] docs: drop redundant lead from the options comparison Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- sources/platform/actors/development/debugging.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index e1ef46ac90..f5d9694f57 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -33,8 +33,6 @@ This shapes what debugging on the platform looks like: ## Choose an option -The two options are alternatives, not steps. Compare them and follow only the section for the one you pick. - | | Actor debugger | wstunnel | | --- | --- | --- | | Setup | Change the Dockerfile `CMD` | Add a binary, start it next to the debugger | From a6c099fa27557c81265c8ff5d09303ce70e41802 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 11:11:46 +0000 Subject: [PATCH 5/5] docs: open both debugging options with the option itself Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- sources/platform/actors/development/debugging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index f5d9694f57..8233b481f3 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -42,7 +42,7 @@ This shapes what debugging on the platform looks like: ## Option 1: Debug in the browser with Actor debugger -This option is complete on its own and needs no tunnel. The [actor-debugger](https://github.com/apify/actor-debugger) package launches your Actor under its native debugger and serves a debugger UI on the web server port. You open one URL from the run log in your browser. Nothing runs on your machine. +The [actor-debugger](https://github.com/apify/actor-debugger) package launches your Actor under its native debugger and serves a debugger UI on the web server port. You open one URL from the run log in your browser. Nothing runs on your machine. @@ -95,7 +95,7 @@ That page is a debugger UI for [debugpy](https://github.com/microsoft/debugpy). ## Option 2: Debug from your IDE with wstunnel -This option is complete on its own and doesn't use the actor-debugger package. [wstunnel](https://github.com/erebe/wstunnel) tunnels TCP over WebSocket. The server runs inside the container on the web server port. The client runs on your machine and exposes the remote debug port on `localhost`. Your IDE attaches to `localhost` as if the Actor ran there. This works for any language with a TCP debug protocol. +[wstunnel](https://github.com/erebe/wstunnel) tunnels TCP over WebSocket. The server runs inside the container on the web server port. The client runs on your machine and exposes the remote debug port on `localhost`. Your IDE attaches to `localhost` as if the Actor ran there. This works for any language with a TCP debug protocol. ### Step 1: Add wstunnel to the image