From b5948e5a7da28be74fca5c7493af47265d6beb4c Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 18 Sep 2026 09:51:15 +0000 Subject: [PATCH 01/10] 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 02/10] 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 03/10] 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 04/10] 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 05/10] 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 From cb3d42e2c16050b94319226823ed46ba9ab3b6ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Proch=C3=A1zka?= Date: Tue, 29 Sep 2026 11:42:50 +0200 Subject: [PATCH 06/10] Apply batched suggestions from code review Co-authored-by: Vlada Dusek Co-authored-by: Edyta <142720610+szaganek@users.noreply.github.com> --- .../platform/actors/development/debugging.md | 59 +++++++++---------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index 8233b481f3..38b36f75e2 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -9,24 +9,23 @@ 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 independent ways to attach a debugger to an Actor run on the platform. Pick one: +You can reproduce most bugs by running your Actor locally and using your IDE's debugger. However, some bugs are related to the platform's proxies, memory limits, environment variables, or data that exists only in a real run. To debug such issues, attach a debugger to an Actor run on the platform: - **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. ## 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. +An Actor run is a Docker container on a shared worker machine. You can't open a TCP connection to the container, so there's 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`. 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. +Debuggers speak raw TCP. The Node.js inspector listens on port `9229`, debugpy on `5678`. Each option in this guide handles the mismatch 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: +These constraints shape 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`. +- Without an access check, 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. @@ -35,24 +34,24 @@ This shapes what debugging on the platform looks like: | | Actor debugger | wstunnel | | --- | --- | --- | -| Setup | Change the Dockerfile `CMD` | Add a binary, start it next to the debugger | +| Setup | Install a package, 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 | ## 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. +The experimental [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: +Install the package after your other dependencies, so the project's `npm install` doesn't prune it, and swap the entrypoint in your Dockerfile: ```dockerfile RUN npm install actor-debugger -# Replaces the normal entrypoint, for example CMD ["npm", "start"] +# Replaces the normal entrypoint, for example CMD ["node", "dist/main.js"] CMD ["npx", "actor-debugger", "--brk"] ``` @@ -74,11 +73,11 @@ 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"] +# Replaces the normal entrypoint, for example CMD ["python", "-m", "my_actor"] 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 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", "my_actor"]` or `CMD ["python3", "-m", "actor_debugger", "main.py"]`. The run log prints a URL in this form: @@ -95,9 +94,9 @@ That page is a debugger UI for [debugpy](https://github.com/microsoft/debugpy). ## 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. +[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. The tunnel works for any language with a TCP debug protocol. -### Step 1: Add wstunnel to the image +### 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. @@ -110,7 +109,8 @@ 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 + | tar -xz -C /usr/local/bin wstunnel \ + && chmod +x /usr/local/bin/wstunnel ``` @@ -122,7 +122,8 @@ 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 + | tar -xz -C /usr/local/bin wstunnel \ + && chmod +x /usr/local/bin/wstunnel ``` Add `debugpy` to your `requirements.txt`. @@ -130,7 +131,7 @@ Add `debugpy` to your `requirements.txt`. -### Step 2: Start the tunnel and the debugger +### 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. @@ -140,28 +141,22 @@ Then replace the `CMD` so the container starts the tunnel server and the Actor u ```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. +CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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"] ```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. +CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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 my_actor"] -`--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. +`: "${DEBUG_SECRET:?}"` fails the run before anything starts when the variable is unset or empty, because an empty prefix lets in any client. `--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. +You can also start `wstunnel server` from your Actor code and gate it on an input field. That approach avoids a separate debug build at the cost of shipping the binary in every build. -### Step 3: Connect from your machine +### 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: @@ -169,9 +164,9 @@ Install wstunnel locally with `brew install wstunnel` or a [release binary](http 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. +Use `5678` in place of `9229` for Python. Leave the command running. The port on `localhost` now leads to the debugger inside the run. -### Step 4: Attach your IDE +### 4. Attach your IDE Attach to `localhost` and map your project root to `/usr/src/app`, the working directory in the Apify base images. @@ -221,10 +216,10 @@ Set a breakpoint and start the configuration. The run resumes under your debugge ## Keep debugging out of production :::caution Unauthenticated code execution -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. +Either option exposes a code-execution endpoint on the container URL. The Actor debugger has no authentication. Anyone who learns the wstunnel secret gets the same access. ::: - 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. +- Abort the run when you finish. From 1106e18f712bc9432adb7eac482b718592b3999b Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 10:00:38 +0000 Subject: [PATCH 07/10] fix: close the Dockerfile code fences in the debugging guide The batched review suggestions dropped the closing fences of the two wstunnel CMD blocks, so the MDX parser swallowed the following JSX and the build failed with an unclosed TabItem. Close both fences and restore the explanatory sentences the suggestions carried. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- sources/platform/actors/development/debugging.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index 38b36f75e2..2cc56f9884 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -142,12 +142,18 @@ Then replace the `CMD` so the container starts the tunnel server and the Actor u ```dockerfile CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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"] +``` + +Replace `dist/main.js` with your Actor's entrypoint. `--inspect-brk` pauses on the first line until a debugger attaches. Use `--inspect` to attach mid-run. ```dockerfile CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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 my_actor"] +``` + +Replace `my_actor` with your Actor's package. `--wait-for-client` pauses until a debugger attaches. Drop it to attach mid-run. From 9974a8e62fe664fe0d02a6dd3c78ee398b0767e2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 11:56:46 +0000 Subject: [PATCH 08/10] docs: restructure the debugging guide from review feedback Merge the option comparison into one "Attach a debugger" section, where an outer tab group holds the two options and the language tabs sit inside each one. Turn both options into numbered procedures, reword the description around the job to be done, keep the introduction free of the options, and give the production section a lead sentence before its admonition and list. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- .../platform/actors/development/debugging.md | 260 +++++++++--------- 1 file changed, 132 insertions(+), 128 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index 2cc56f9884..ebbeeafd75 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -1,7 +1,7 @@ --- 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. +description: Learn how to attach a debugger to an Actor run on the Apify platform, so you can set breakpoints and inspect variables in a run as it executes. sidebar_position: 8 slug: /actors/development/debugging --- @@ -9,11 +9,7 @@ slug: /actors/development/debugging import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - -You can reproduce most bugs by running your Actor locally and using your IDE's debugger. However, some bugs are related to the platform's proxies, memory limits, environment variables, or data that exists only in a real run. To debug such issues, attach a debugger to an Actor run on the platform: - -- **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 can reproduce most bugs by running your Actor locally and using your IDE's debugger. However, some bugs are related to the platform's proxies, memory limits, environment variables, or data that exists only in a real run. This guide shows how to attach a debugger to such a run on the Apify platform. ## Infrastructure constraints @@ -21,7 +17,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`. Each option in this guide handles the mismatch the same way: a small server inside the container 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`. Bridging that mismatch takes a small server inside the container, which carries the debug port over WebSocket on the web server port. These constraints shape what debugging on the platform looks like: @@ -30,202 +26,210 @@ These constraints shape what debugging on the platform looks like: - 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 +## Attach a debugger + +You have two independent options: | | Actor debugger | wstunnel | | --- | --- | --- | -| Setup | Install a package, change the Dockerfile `CMD` | Add a binary, start it next to the debugger | -| Client | Any browser | wstunnel client and your IDE | +| Where you debug | In any browser, with no local tooling | In your local IDE | +| What you add to the image | The `actor-debugger` package | The `wstunnel` binary | | Languages | Node.js/TypeScript, Python | Anything with a TCP debug protocol | -| Best for | Quick look at a run, no local setup | Full IDE experience | +| Best for | A quick look at a run | A full IDE experience | -## Option 1: Debug in the browser with Actor debugger +Select the option you want, then the language your Actor uses: + + + The experimental [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 after your other dependencies, so the project's `npm install` doesn't prune it, and swap the entrypoint in your Dockerfile: +1. Install the package in your Dockerfile, after your other dependencies, so the project's `npm install` doesn't prune it: + + ```dockerfile + RUN npm install actor-debugger + ``` -```dockerfile -RUN npm install actor-debugger +1. Replace the Dockerfile entrypoint, for example `CMD ["node", "dist/main.js"]`, with the debug launcher: -# Replaces the normal entrypoint, for example CMD ["node", "dist/main.js"] -CMD ["npx", "actor-debugger", "--brk"] -``` + ```dockerfile + 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 launcher finds your entrypoint from `scripts.start` or `main` in `package.json`, or from conventional paths like `dist/main.js`. To name the entrypoint yourself, pass its path: `CMD ["npx", "actor-debugger", "dist/main.js"]`. -The run log prints a URL in this form: +1. Build the Actor, start a run, and open the URL from the run log in your browser: -```text -https://.runs.apify.net/devtools/js_app.html?wss=.runs.apify.net/ -``` + ```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. + 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: +1. Install the package in your Dockerfile: -```dockerfile -RUN pip install actor-debugger + ```dockerfile + RUN pip install actor-debugger + ``` -# Replaces the normal entrypoint, for example CMD ["python", "-m", "my_actor"] -CMD ["python3", "-m", "actor_debugger", "--brk"] -``` +1. Replace the Dockerfile entrypoint, for example `CMD ["python", "-m", "my_actor"]`, with the debug launcher: -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", "my_actor"]` or `CMD ["python3", "-m", "actor_debugger", "main.py"]`. + ```dockerfile + CMD ["python3", "-m", "actor_debugger", "--brk"] + ``` -The run log prints a URL in this form: + The launcher finds the runnable package in the working directory, which covers the Apify Python templates. To name the entrypoint yourself, pass the module or file: `CMD ["python3", "-m", "actor_debugger", "-m", "my_actor"]` or `CMD ["python3", "-m", "actor_debugger", "main.py"]`. -```text -https://.runs.apify.net/ui/ -``` +1. Build the Actor, start a run, and open the URL from the run log in your browser: -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. + ```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. +The `--brk` flag pauses the Actor on its first line and holds the run there until you open the debugger URL. Without the flag, the Actor starts working immediately and you can open the URL at any point during the run. To stop debugging, restore the original `CMD` and rebuild the Actor. -## 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. The tunnel works for any language with a TCP debug protocol. -### 1. Add wstunnel to the image +1. Download the static wstunnel release binary in your Dockerfile: -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 -```dockerfile -FROM apify/actor-node:24 + 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 \ + && chmod +x /usr/local/bin/wstunnel + ``` -# 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 \ - && chmod +x /usr/local/bin/wstunnel -``` + + - - + ```dockerfile + FROM apify/actor-python:3.13 -```dockerfile -FROM apify/actor-python:3.13 + 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 \ + && chmod +x /usr/local/bin/wstunnel + ``` -# 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 \ - && chmod +x /usr/local/bin/wstunnel -``` + Add `debugpy` to your `requirements.txt`. -Add `debugpy` to your `requirements.txt`. + + - - +1. 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. -### 2. Start the tunnel and the debugger +1. Replace the `CMD` in your Dockerfile, so the container starts the tunnel server and the Actor under its 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", ": \"${DEBUG_SECRET:?}\"; 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"] + ``` - - + Replace `dist/main.js` with your Actor's entrypoint. The `--inspect-brk` flag pauses the Actor on its first line until a debugger attaches. Use `--inspect` instead to let the Actor start working immediately. -```dockerfile -CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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"] -``` + + -Replace `dist/main.js` with your Actor's entrypoint. `--inspect-brk` pauses on the first line until a debugger attaches. Use `--inspect` to attach mid-run. + ```dockerfile + CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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 my_actor"] + ``` - - + Replace `my_actor` with your Actor's package. The `--wait-for-client` flag pauses the Actor on its first line until a debugger attaches. Omit the flag to let the Actor start working immediately. -```dockerfile -CMD ["sh", "-c", ": \"${DEBUG_SECRET:?}\"; 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 my_actor"] -``` + + -Replace `my_actor` with your Actor's package. `--wait-for-client` pauses until a debugger attaches. Drop it to attach mid-run. + The `: "${DEBUG_SECRET:?}"` guard fails the run before anything starts when the variable is unset or empty, because an empty prefix lets in any client. The `--restrict-to` option limits the tunnel to the debug port, so nothing else in the container becomes reachable. The `exec` command keeps the Actor as the main process, so it still receives the platform's shutdown signals. - - - -`: "${DEBUG_SECRET:?}"` fails the run before anything starts when the variable is unset or empty, because an empty prefix lets in any client. `--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 approach avoids a separate debug build at the cost of shipping the binary in every build. - -### 3. Connect from your machine +1. Build the Actor, start a run, and copy the container URL from the run detail page in Apify Console. -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: +1. Install wstunnel on your machine with `brew install wstunnel` or download a [release binary](https://github.com/erebe/wstunnel/releases). Then open the tunnel, and leave the command running: -```bash -wstunnel client --http-upgrade-path-prefix -L tcp://9229:127.0.0.1:9229 wss://.runs.apify.net -``` + ```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. The port on `localhost` now leads to the debugger inside the run. + Use `5678` in place of both occurrences of `9229` for Python. The port on `localhost` now leads to the debugger inside the run. -### 4. Attach your IDE +1. Attach your IDE to `localhost` and map your project root to `/usr/src/app`, the working directory in the Apify base images: -Attach to `localhost` and map your project root to `/usr/src/app`, the working directory in the Apify base images. + + - - + ```json + { + "type": "node", + "request": "attach", + "name": "Attach to Apify run", + "address": "localhost", + "port": 9229, + "localRoot": "${workspaceFolder}", + "remoteRoot": "/usr/src/app" + } + ``` -VS Code `launch.json` configuration: + In JetBrains IDEs, create an **Attach to Node.js/Chrome** run configuration for `localhost:9229` instead, and map the project root to `/usr/src/app` under **Remote URLs of local files**. In Chrome, open `chrome://inspect` and add `localhost:9229` as a target. -```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. + ```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` instead, with the same path mapping. -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" } - ] -} -``` +1. Set a breakpoint in your source files and start the configuration. The run resumes under your debugger. -In PyCharm 2026.1 or later, create an **Attach to DAP** run configuration for `localhost:5678` with the same path mapping. +You can also start `wstunnel server` from your Actor code and gate it on an input field. That approach avoids a separate debug build at the cost of shipping the binary in every build. -Set a breakpoint and start the configuration. The run resumes under your debugger. - ## Keep debugging out of production -:::caution Unauthenticated code execution -Either option exposes a code-execution endpoint on the container URL. The Actor debugger has no authentication. Anyone who learns the wstunnel secret gets the same access. +Both options turn the run's container URL into a code-execution endpoint. The Actor debugger has no authentication, and with wstunnel, anyone who learns the secret gets the same access. + +:::caution Never publish a debug build + +A build with a debug entrypoint gives its users access to your Actor's environment, including `APIFY_TOKEN`. Keep such builds out of Apify Store. + ::: +Limit the exposure while you debug: + - 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. From cc8e9cfee436c213b0e166f2194d5bb46984f11c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Proch=C3=A1zka?= Date: Tue, 29 Sep 2026 16:52:03 +0200 Subject: [PATCH 09/10] Update sources/platform/actors/development/debugging.md Co-authored-by: Vlada Dusek --- 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 ebbeeafd75..523d54c46f 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -81,10 +81,10 @@ The experimental [actor-debugger](https://github.com/apify/actor-debugger) packa 1. Replace the Dockerfile entrypoint, for example `CMD ["python", "-m", "my_actor"]`, with the debug launcher: ```dockerfile - CMD ["python3", "-m", "actor_debugger", "--brk"] + CMD ["python", "-m", "actor_debugger", "--brk"] ``` - The launcher finds the runnable package in the working directory, which covers the Apify Python templates. To name the entrypoint yourself, pass the module or file: `CMD ["python3", "-m", "actor_debugger", "-m", "my_actor"]` or `CMD ["python3", "-m", "actor_debugger", "main.py"]`. + The launcher finds the runnable package in the working directory, which covers the Apify Python templates. To name the entrypoint yourself, pass the module or file: `CMD ["python", "-m", "actor_debugger", "-m", "my_actor"]` or `CMD ["python", "-m", "actor_debugger", "main.py"]`. 1. Build the Actor, start a run, and open the URL from the run log in your browser: From 489785ecca96d6df8b8f3df5dc035f264dd257a6 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 30 Sep 2026 14:50:02 +0000 Subject: [PATCH 10/10] docs: turn the two debugging options into headings Replace the outer tab group with "Actor debugger" and "wstunnel" subheadings, so neither option is hidden behind a click and both appear in the table of contents. The language tabs inside each option stay as they were, and no wording changes. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01A89bV3vDmnjJQt2MSepEZ6 --- sources/platform/actors/development/debugging.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/sources/platform/actors/development/debugging.md b/sources/platform/actors/development/debugging.md index 523d54c46f..b9f605dc61 100644 --- a/sources/platform/actors/development/debugging.md +++ b/sources/platform/actors/development/debugging.md @@ -37,10 +37,7 @@ You have two independent options: | Languages | Node.js/TypeScript, Python | Anything with a TCP debug protocol | | Best for | A quick look at a run | A full IDE experience | -Select the option you want, then the language your Actor uses: - - - +### Actor debugger The experimental [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. @@ -99,8 +96,7 @@ The experimental [actor-debugger](https://github.com/apify/actor-debugger) packa The `--brk` flag pauses the Actor on its first line and holds the run there until you open the debugger URL. Without the flag, the Actor starts working immediately and you can open the URL at any point during the run. To stop debugging, restore the original `CMD` and rebuild the Actor. - - +### 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. The tunnel works for any language with a TCP debug protocol. @@ -215,9 +211,6 @@ The `--brk` flag pauses the Actor on its first line and holds the run there unti You can also start `wstunnel server` from your Actor code and gate it on an input field. That approach avoids a separate debug build at the cost of shipping the binary in every build. - - - ## Keep debugging out of production Both options turn the run's container URL into a code-execution endpoint. The Actor debugger has no authentication, and with wstunnel, anyone who learns the secret gets the same access.