How to Debug a Node.js App on AWS ECS Fargate Using Port Forwarding (Step-by-Step Guide)

At some point in their lives, every software engineer eventually faces the task of debugging an app live on a remote server. If that app happens to be running on AWS ECS Fargate, getting into that container safely is possible, but not immediately obvious.
Full disclosure: I haven’t done this exact thing before, but I have used the same port-forwarding trick to peek into our RDS instances via an ECS task acting as a jumpbox. So yes, I tested these instructions, and yes, they actually work.
The Setup
Here’s what we have:
- A Node.js app deployed to AWS ECS Fargate
- Inside a private VPC subnet
- Without SSH access (Fargate doesn’t do that)
- VS Code (or some other IDE with Node.js inspector support)
Step 1: Enable Node.js Inspector in Your Fargate Task
To attach a debugger, your Node.js process must start with the inspector enabled. This can be achieved by passing a flag to the node command.
node --inspect=127.0.0.1:9229 server.js
Or by adding the inspect flag to the NODE_OPTIONS environmental variable.
NODE_OPTIONS="--inspect=127.0.0.1:9229"
Since we’re using ECS, adding flag to the node command would require modifying Dockerfile or overriding command in the task definition, so I’d suggest the environmental variable approach.
{ "containerDefinitions": [ { "name": "...", "image": "...", "essential": true, "environment": [ { "name": "NODE_OPTIONS", "value": "--inspect=127.0.0.1:9229" } ] } ] }
Step 2: Grant SSM and ECS Exec Permissions
To enable secure shell-like access to your ECS Fargate container, configure ECS Exec and grant SSM permissions. Attach this AWS IAM policy to your ECS task execution role:
arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
Enable ECS Exec on your service:
aws ecs update-service \ --cluster my-cluster \ --service my-service \ --enable-execute-command
If you’re changing it through the console, it’s hidden under Troubleshooting → Enable Execute Command.
Step 3: Identify the Running Task ID
Before establishing a session, identify the running ECS task ARN and container runtime ID.
List your tasks:
aws ecs list-tasks --cluster my-cluster
You’ll get something like:
{ "taskArns": [ "arn:aws:ecs:us-east-1:123456789012:task/my-cluster/abc123def456ghi789" ] }
The last part of the ARN (abc123def456ghi789) is your Task ID.
Step 4: Create an SSM Port Forwarding Session
Use AWS Systems Manager Session Manager to create a secure port forwarding tunnel from your local machine to the Node.js process inside the Fargate task.
Here’s where the real black magic starts. AWS won’t just give you a SSM target ID for the Fargate task, you have to construct it yourself using this template:
ecs:<cluster_name>_<task_id>_<container_runtime_id>
Get the container runtime ID:
aws ecs describe-tasks \ --cluster my-cluster \ --task <task_arn> \ --query "tasks[].containers"
You’ll get a massive JSON blob, and somewhere in that mess hides the runtimeId field you actually care about. There might be a few of them actually, one for each container in the task. And yes, the runtimeId contains task id in it.
Then start the port forwarding:
aws ssm start-session \ --target "ecs:my-cluster_cb39a47ef2ef45f4b947236bf00aeadd_cb39a47ef2ef45f4b947236bf00aeadd-3935363592" \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters '{"host":["127.0.0.1"],"portNumber":["9229"],"localPortNumber":["9229"]}'
This opens a local port (9229) and forwards traffic securely to the Node.js process in your container.
Pro tip: We’re forwarding to
127.0.0.1here, but you can forward to any IP or hostname accessible from the ECS task.
Step 5: Attach VS Code Debugger
Once the tunnel is active, open VS Code and attach your debugger to localhost:9229. You’re now live-debugging inside Fargate.
In .vscode/launch.json:
{ "type": "node", "request": "attach", "name": "Attach to Fargate", "address": "localhost", "port": 9229, "localRoot": "${workspaceFolder}", "remoteRoot": "/usr/src/app" }
And that’s all.
If you don’t know the remoteRoot , then you can get into the container and look around to check it.
aws ecs execute-command --cluster my-cluster \ --task <task_arn> \ --interactive \ --command "/bin/sh" \ --container <container name>
Alternative: Chrome debugger (chrome://inspect). Usually works, but tends to have issues with source maps.
TL;DR Cheat Sheet
# 1. Make sure your Node.js app runs with --inspect flag NODE_OPTIONS="--inspect=127.0.0.1:9229" # 2. Give ECS task SSM permissions # 3. Enable ECS Exec aws ecs update-service —service my-service —cluster my-cluster —enable-execute-command # 4. Get you task arn aws ecs list-tasks --cluster my-cluster # 5. Get your container runtime id aws ecs describe-tasks \ --cluster <cluster> \ --region us-east-2 \ --task <task_arn> \ --query "tasks[].containers" # 6. Start port forwarding aws ssm start-session \ --target ecs:<cluster>_<task>_<runtime_id> \ --document-name AWS-StartPortForwardingSessionToRemoteHost \ --parameters '{"host":["127.0.0.1"],"portNumber":["9229"],"localPortNumber":["9229"]}'
Then attach your VS Code debugger to localhost:9229 and voila.
Common Issues When Debugging on Fargate
- Missing
AmazonSSMManagedInstanceCorepolicy on the ECS task role. - Incorrect
runtimeIdor malformedecs:<cluster>_<task>_<runtimeId>string. - Task not running with
--inspectflag. - Conflicting local port already in use.
Pro tip: Always verify that your
NODE_OPTIONSenvironment variable is actually applied at container startup.
FAQ Section
Q1: Can I debug ECS Fargate containers without enabling ECS Exec?
No. ECS Fargate doesn’t support SSH. You need to enable ECS Exec and grant SSM permissions to establish a secure session.
Q2: Is it safe to expose the Node.js inspector port?
Yes, as long as you forward it locally using SSM (which encrypts traffic). Avoid exposing port 9229 publicly.
Q3: Does this method work for other languages or frameworks?
Yes. The same SSM port forwarding technique works for any process exposing a debug port (Python, Go, .NET, etc.).
Q4: Can I automate this setup?
You can automate ECS Exec and port forwarding setup via scripts or AWS CloudFormation hooks for recurring debugging sessions.






