January 31, 2025 by Ramadhan4 minutes
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:
$ 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
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
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
# 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>
#!/bin/bash
docker compose up -d --build
#!/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)
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
$ 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 │
└────┴────────────────────┴──────────┴──────┴───────────┴──────────┴──────────┘
$ 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
Check the pm2 list first and grab the id
$ 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
$ 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.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
...