NGROK: Multiple Tunneling & Exposing Local VM into Public

January 31, 2025 by Ramadhan4 minutes

NGROK: Multiple Tunneling & Exposing Local VM into Public

Introduction

In the world of development and application testing, we often need a way to expose local services to the internet without configuring complex network settings. Ngrok provides a convenient solution that allows us to create secure tunnels from a local machine to the internet quickly and efficiently. We will discusses how to implement multiple tunneling using Ngrok within a Docker container to expose local services, such as Nginx, SSH, and file-sharing, to the public.

This project leverages Docker Compose to automate deployment, enabling multiple services to run with a single command. By utilizing Ngrok, we can easily:

  • Publicly access a Nginx web server.
  • Enable SSH access to a local virtual machine via the internet.
  • Share static files from a local directory.

Configurations

Project Structure

tree
$ tree ngrok-multitunnel/
├── README.md
├── conf.sh
├── docker-compose.yml
├── img
│   └── dummy.jpg
├── ngrok.yaml
├── pm2.sh
├── setup.sh
└── shutdown.sh

1 directory, 8 files

Tech Stacks


○ DOCKER

docker-compose.yml
version: '3.8'

networks:
  ngrok_network:
    driver: bridge

services:
  nginx_repo:
    image: nginx:alpine
    container_name: nginx
    networks:
      - ngrok_network
    restart: always

  ngrok_repo:
    image: ngrok/ngrok:alpine
    container_name: ngrok
    networks:
      - ngrok_network
    volumes:
      - ./ngrok.yaml:/etc/ngrok.yaml:ro
     - ./img:/img:ro
    environment:
      NGROK_CONFIG: /etc/ngrok.yaml
    command: start --all
    restart: always

○ YAML

ngrok.yaml
version: 3

agent:
  authtoken: <your-ngrok-authtoken>

tunnels:
  nginx:
    proto: http
    addr: nginx:80
  ssh-access:
    proto: tcp
    addr: <nginx-network-ip-gateway>:22
 file:
   proto: http
   addr: file:///img

○ BASH Config File

pm2.sh
# LIST
#pm2 list

# START
#  Using `conf.sh` (docker run) file
# pm2 start --name ngrok-multitunnel "./conf.sh"
# Using `setup.sh` (docker compose) file
pm2 start --name ngrok-multitunnel "./setup.sh"

# STOP
# pm2 stop ngrok-multitunnel

# DELETE
# pm2 delete <id>
setup.sh
#!/bin/bash
docker compose up -d --build
shutdown.sh
#!/bin/bash

declare NGINX="nginx"
declare NGROK="ngrok/ngrok"

# DOCKER RUN
#docker stop $NGINX $NGROK
#docker rm $NGINX $NGROK
#docker image rm $NGINX $NGROK

# DOCKER COMPOSE
docker compose stop $NGINX $NGROK
docker compose rm $NGINX $NGROK
docker rmi $(docker images -q $NGINX) $(docker images -q $NGROK)

# DOCKER DELETE ALL IMAGES (caution)
#docker image rm $(docker images -aq)
conf.sh (optional)
docker run --rm --detach \
        --network ngrok \
        --name nginx \
        nginx:alpine

docker run --rm --detach \
        --name ngrok \
        --network ngrok \
        -v $(pwd)/ngrok.yaml:/etc/ngrok.yaml \
        -v $(pwd)/img:/img \
        -e NGROK_CONFIG=/etc/ngrok.yaml \
        ngrok/ngrok:alpine \
        start --all

Build & Run containers in PM2 state

up & running container
$ pm2 start --name ngrok-multitunnel "./setup.sh"

[PM2] Spawning PM2 daemon with pm2_home=/home/nopedawn/.pm2
[PM2] PM2 Successfully daemonized
[PM2] Starting /home/nopedawn/ngrok-multitunnel/setup.sh in fork_mode (1 instance)
[PM2] Done.
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
0  │ ngrok-multitunnel  │ fork     │ 0    │ online    │ 0%       │ 3.3mb    │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

View PM2 & containers running

view container running
$ docker ps
CONTAINER ID   IMAGE                COMMAND                  CREATED         STATUS         PORTS      NAMES
b461f4815405   nginx:alpine         "/docker-entrypoint.…"   2 minutes ago   Up 2 minutes   80/tcp     nginx
632dd2efb6c7   ngrok/ngrok:alpine   "/nix/store/1qpvcjc0…"   2 minutes ago   Up 2 minutes   4040/tcp   ngrok 

Ngrok Agents Page

ngrok-agents-page

ngrok-agents-sidebar


Output

→ http 80

http-80

→ http file:///img

http-file-img

→ tcp 22

tcp-22


Stopping PM2 & containers

Check the pm2 list first and grab the id

pm2 lists
$ pm2 list
┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
0  │ ngrok-multitunnel  │ fork     │ 8464 │ online    │ 0%       │ 3.1mb    │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

pm2 stop <id> && pm2 delete <id>

in this case, the pm2 that are running is in id 0

stop & delete pm2 instance
$ pm2 stop 0 && pm2 delete id 0
[PM2] Applying action stopProcessId on app [0](ids: [ '0' ])
[PM2] [ngrok-multitunnel](0)┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
├────┼────────────────────┼──────────┼──────┼───────────┼──────────┼──────────┤
0  │ ngrok-multitunnel  │ fork     │ 8696 │ stopped   │ 0%       │ 0b       │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
[PM2] Applying action deleteProcessId on app [0](ids: [ '0' ])
[PM2] [ngrok-multitunnel](0)┌────┬────────────────────┬──────────┬──────┬───────────┬──────────┬──────────┐
│ id │ name               │ mode     │ ↺    │ status    │ cpu      │ memory   │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘

then, shutdown the container and delete the images

shutdown container
$ ./shutdown.sh
WARN[0000] /home/nopedawn/ngrok-multitunnel/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Stopping 2/2
 ✔ Container ngrok  Stopped                                                                              0.4s
 ✔ Container nginx  Stopped                                                                              0.6s
WARN[0000] /home/nopedawn/ngrok-multitunnel/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
? Going to remove nginx, ngrok Yes
[+] Removing 2/2
 ✔ Container ngrok  Removed                                                                              0.0s
 ✔ Container nginx  Removed                                                                              0.0s
Untagged: nginx:alpine
Untagged: nginx@sha256:814a8e88df978ade80e584cc5b333144b9372a8e3c98872d07137dbf3b44d0e4
Deleted: sha256:93f9c72967dbcfaffe724ae5ba471e9568c9bbe67271f53266c84f3c83a409e3
...
Untagged: ngrok/ngrok:alpine
Untagged: ngrok/ngrok@sha256:752c581ffd40e5a2d1238cb3a4f8344763e3d3dedabc3bf2341da2c73de356c0
Deleted: sha256:a90830383b392ae8fb387bc02baf00e8d0bc28105a85c89f24a3493adb073ddf
...