Last updated Mar 1, 2024


Tunneling runs your app code locally on your machine via the Forge CLI and ngrok.


Tunnelling requires Docker installed and running on your machine. Get Docker and start it before using the tunnel command.

Providing credentials for ngrok

Forge tunnel uses ngrok to tunnel invocations. As ngrok does not support anonymous users, you need to set up your credentials before using tunnel command. If you don't have an ngrok account, sign up for one.

Once you have an ngrok account:

  1. Get your ngrok authtoken.
  2. Create an ngrok config file. Here's an example YAML file:
    version: 2
    log_level: debug
    region: us
    authtoken: <your-ngrok-authtoken>
  3. Provide the path to the config file to Forge using the command forge settings set ngrok-config-path <file>.


You can start a tunnel for any app that has been deployed and installed on to a site. To start a tunnel for your app, run the following command in your CLI:

forge tunnel

You'll see output similar to this:

Tunnel redirects requests you make to your local machine. This occurs for any
Atlassian site where your app is installed in the specific development
environment. You will not see requests from other users.
Press Ctrl+C to cancel.

Checking Docker image... 100%
Your Docker image is up to date.

Reloading code...

=== Running forge lint...
No issues found.

=== Bundling code...
App code bundled.

=== Snapshotting functions...
No log output.

App code reloaded.

Listening for requests...

Messages logged to the console will look similar to this:

INFO  17:34:04.955 Count of objects in test array: 0

Tunneling also helps you debug your app in two ways:

  • Real-time logging for your local app: By inserting console.log() statements in your code, you can see the output in the Forge CLI as the code executes.
  • Fast turnaround for changes: The tunnel watches for code changes and rebuilds your app. You don’t need to deploy your app after every change, which lets you test fixes faster.

The new native Node.js runtime no longer requires Docker to operate.

Tunneling with UI kit

When running forge tunnel with a UI kit app, any changes to your source code triggers a rebundle from the Forge CLI. Once the rebundling is completed successfully, you can see your changes by refreshing the page that your app is on.

Tunneling with custom UI

When running forge tunnel with a custom UI app, the Forge CLI serves the content from the path directories specified in each resource that's defined in your manifest. This means that if your static assets require rebundling, you need to bundle them before refreshing the page that your app is on. See here for more details on resource declarations for custom UI apps.

Tunneling with custom UI apps is only supported on Chrome and Firefox browsers.

Connecting the tunnel to your own dev server

While forge tunnel lets you avoid redeploying your custom UI app to test changes, you have to manually bundle it for each change you make. To solve having to do this, popular tools, such as create-react-app can automatically reload your app each time you change the code. These tools usually host their own server on a specific port.

For example, running npm start with create-react-app starts a server at http://localhost:3000 by default. The Forge CLI allows you to proxy tunnel requests to these servers, enabling features, such as hot-reloading while developing your custom UI apps.

To connect a server to forge tunnel, first identify the port that the server is hosted on. In the above example, the port would be 3000. Then, add the following to your manifest.yml file, under the resource your server is hosting:

  port: <YOUR_PORT_HERE>

For example, a resources definition might look like this:

  - key: main
    path: static/hello-world/build
      port: 3000

Once a port has been added to a resource definition, running forge tunnel causes the Forge CLI to bypass the path directory, and instead proxy the request to http://localhost:<port>.

You can then run forge tunnel, and start your server by running npm start. You can then see the assets served by your local server by refreshing the page that your app is on. If your server supports hot-reloading, you no longer need to refresh the page to see updates when you make changes to the code.

Tunneling with non-UI functions

When running forge tunnel with non-UI functions, such as custom UI resolvers and web triggers, any changes to your source code triggers a rebundle from the Forge CLI. Once that rebundling is completed successfully, you can see your changes reflected in the next invocation of the function.

Interactive debugging

The new native Node.js runtime no longer supports interactive debugging.

The forge tunnel command accepts a --debug flag that will allow you to perform remote debugging in Chrome's DevTools and will pause execution when a debugger; statement is found. The debugger; is a special statement that invokes any available debugging functionality, such as setting a breakpoint. The debugger; statement will be ignored if you're not tunnelling in debug mode.

When you run tunnel with the --debug flag, you'll be prompted with a message similar to the following:

Chrome inspector URL (add "debugger;" statement to your app to pause): devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=

Opening the provided inspector URL, your app will now pause on any debugger; breakpoints you add in your code.

This is a great way to interactively debug your application within the context of the breakpoint.

Known limitations

  • Tunneling doesn't work with Safari browsers as it doesn't allow iframes connecting to localhost
  • Any logging that happens while you're tunneling won’t show in forge logs. This is because your app code runs locally while tunneling. forge logs only shows information for your deployed app, not locally running code.
  • If you make changes to the manifest.yml file, you must deploy the app with the latest manifest. This is needed for the tunnel to pick up the changes.
  • Environment variables must be set locally, as the tunnel can't access the values set in other environments.
  • Tunneling only displays output from your usage of the app, that is, your requests.
  • Tunnelled app resolvers won't time out, unlike deployed apps. Instead, they will continue to run until completed. This is because developer environments have network and processing speeds that differ from Forge's runtime environment, preventing us from accurately replicating app timeouts during tunnelling. Learn more about deployed app invocation limits.
  • Forge invocations have a time limit. This may cause issues in two instances:
    • If your local machine has less compute power than the remote Lambda function server, you could see local timeouts that wouldn't happen once deployed.
    • Conversely, if your local machine has more power than the remote, you could see remote timeouts that didn't happen locally.

Troubleshooting Docker issues

The current runtime's version of the Forge tunnel uses Docker, which may cause issues.

Docker doesn't follow symlinks when creating a container to avoid potential inconsistencies. Therefore, you can't use symlinks in your app repository.

To work around this, install Yalc locally and add dependencies via yalc add <dependency> before running forge tunnel or forge deploy.

Bundling issues

The forge tunnel command will get stuck in the Bundling Code step if you:

  • are using a Mac computer with an Apple Silicon chip (for example, M1)
  • have updated your Docker Desktop app recently (version 4.25+)

To work around this, disable the "Rosetta" setting on your Docker Desktop and restart your Docker daemon.

Rate this page: