GitHub - Oranda-IO/Conduit: Automatic Port Forwarding for Docker Containers

3 min read Original article ↗

GitHub Stars  License  Version 

Conduit

Automatic Port Forwarding for Docker Containers
Use a single exposed port to quickly access services running on your container. Automatic port detection, port forwarding, and app aliasing. CLI, Browser UI, and HTTP API.


conduit watches local listening TCP ports and forwards HTTP requests by path:

  • Incoming: http://<host>:<public_port>/<internal_port>/<path...>
  • Forwarded to: http://127.0.0.1:<internal_port>/<path...>

Example:

  • http://myserver.com:9000/3000/api/users -> http://127.0.0.1:3000/api/users

Why Conduit

  • Auto Discovery: Polls for local listening ports to find running services.

  • Single-Port: Expose one port (e.g. 9000) and reach all your apps through it.

  • Consistent: Deployment platforms vary in their port forwarding strategy. Conduit keeps it consistent.

  • App Aliases: Map ports to friendly names so URLs are easy to remember across restarts.

  • Simple UI: Use CLI or a browser UI to manage your apps.

  • Built-in API: Use the HTTP API for automation / workflows.

Route Modes

Port route

  • Incoming: http://<host>:<public_port>/<internal_port>/<path...>
  • Forwarded to: http://127.0.0.1:<internal_port>/<path...>

Example:

  • http://myserver.com:9000/3000/api/users -> http://127.0.0.1:3000/api/users

Named route

Define a mapping in settings:

{
  "apps": {
    "myapp1": 3000,
    "myapp2": 5173
  }
}

Then:

  • http://myserver.com:9000/myapp1/api/users -> http://127.0.0.1:3000/api/users
  • http://myserver.com:9000/myapp2/ -> http://127.0.0.1:5173/

Conduit only proxies if the target port is currently listening.

Install

  1. Install Go (1.22+).
  2. Build:

Run

./conduit -public-host 0.0.0.0 -public-port 9000

With one open/public port, examples look like:

  • http://<host>:9000/3000/ for direct port routing
  • http://<host>:9000/api/ for alias routing

Useful flags:

  • -public-host bind host (default 0.0.0.0)
  • -public-port bind port (default 9000)
  • -target-host upstream host (default 127.0.0.1)
  • -poll-interval rescan interval (default 2s)
  • -settings-file settings path (default ~/.conduit/settings.json)
  • -no-http run without starting the HTTP server
  • -no-ui alias for -no-http
  • -json JSON output for CLI commands

Terminal Interface

Conduit supports CLI commands against the same state used by HTTP endpoints:

conduit ports
conduit apps list
conduit apps set api 3000
conduit apps delete api
conduit health

Use -json for machine-readable output:

conduit -json apps list
conduit -json ports

Settings File

Default location:

  • ~/.conduit/settings.json

Format:

{
  "apps": {
    "api": 3000,
    "web": 5173
  }
}

Rules:

  • Names are normalized to lowercase.
  • Allowed characters: a-z, 0-9, ., _, -
  • Max name length: 63 chars.
  • Port must be 1-65535.

Dashboard UI

Open:

  • http://<host>:<public_port>/ui

The dashboard shows:

  • Current name-to-port mappings
  • Which mapped ports are running
  • Running ports that do not yet have names
  • Quick links for named route and numeric route
  • Add/update/remove mapping form

API

GET /health

Health check.

Response:

GET /ports

Returns currently discovered listening local ports.

Response example:

{
  "ports": [22, 3000, 5432],
  "count": 3,
  "updated_at": "2026-02-27T03:45:00Z"
}

GET /apps

Returns configured app mappings plus running state and quick route paths.

Response example:

{
  "apps": [
    {
      "name": "api",
      "port": 3000,
      "running": true,
      "named_path": "/api/",
      "port_path": "/3000/",
      "named_url": "http://localhost:9000/api/",
      "port_url": "http://localhost:9000/3000/"
    }
  ],
  "unmapped_running_ports": [5432],
  "settings_file": "/home/node/.conduit/settings.json",
  "updated_at": "2026-02-27T03:45:00Z"
}

POST /apps

Create/update/delete app mapping.

JSON body examples:

Set mapping:

{"action":"set","name":"api","port":3000}

Delete mapping:

{"action":"delete","name":"api"}

Proxy route /<internal_port>/<path...>

Conduit validates internal_port is listening, then proxies the request.

Proxy route /<app_name>/<path...>

Conduit resolves app_name from settings, validates mapped port is listening, then proxies the request.

HTTP Interface

When conduit runs normally, it starts one HTTP server that provides:

  • Proxy routes (/<port>/..., /<app>/...)
  • API endpoints (/health, /ports, /apps)
  • Dashboard UI (/ui)

Run with -no-http (or -no-ui) to disable the HTTP server and use CLI-only workflows.

Testing

Notes

  • HTTP reverse proxy behavior preserves query params, request body, and standard headers/cookies (except normal hop-by-hop header stripping).
  • Linux-oriented port discovery via /proc/net/tcp and /proc/net/tcp6.
  • In containers, publish/forward Conduit’s single public port (for example 9000:9000).