gangway

gangway

Docker app from charlesabarnes' Repository

Overview

gangway gives decks, dashboards and small apps their own HTTPS URL on your domain, from an agent (MCP), a pull request, or a folder dropped in the browser. Previews run as containers on this server's Docker. To keep them in a VM instead, install gangway-inabox. The defaults put gangway behind a reverse proxy on this server (Nginx Proxy Manager, SWAG): proxy *.your-domain and your-domain to https://172.17.0.1:8443 with websockets on, and use a wildcard certificate. Without a domain, set Base domain to [server IP with dashes].sslip.io (e.g. 192-168-1-10.sslip.io), Listen address to ::, Public port to 8443 and Share to true, then open https://app.192-168-1-10.sslip.io:8443. The one-time link that creates the first admin is in this container's log.

gangway: full-stack artifacts on your domain: decks, dashboards and small tools, from your agent, a pull request, or a folder dropped in the browser.

Docs · Quickstart · Connect an agent · Pull-request previews · Apache-2.0


gangway gives any containerized app a public HTTPS URL on your own domain.

Hosted platforms will put your app on a URL, but on their servers, for the frameworks they support, at their prices. I had a server with room to spare and kept needing a link for something: a visual review for a frontend pull request, a build for a client, a deck an agent had just made. gangway is the one place, on hardware you already own, that turns any of those into a URL in seconds.

You get a URL in three ways, and all three produce the same kind of preview:

  1. A pull request opens or updates. The preview is linked from a sticky comment and torn down when the PR closes.
  2. An agent calls the MCP deploy tool. It works with Claude Code, Codex, Cursor, VS Code or any MCP client. The call blocks until the URL actually answers, then returns it.
  3. A person deploys from the web UI or the REST API: drop in files, paste a Compose stack, or point at a git ref.

It runs as a single process on a plain Docker host, with no Kubernetes. You self-host it on your own domain.

I used AI extensively to build this application. I am a professional software engineer, so the architecture and general code direction has been determined and curated by me. This app is live in production for myself, but until the 1.0 release I can not guarantee there won't be rough edges. Please reach out if you have thoughts, comments, or concerns to share about this.

https://github.com/user-attachments/assets/f53178ca-89fc-4ecf-b6b7-bf7490b57ac2

What you get

  • A URL per preview under one wildcard certificate, such as shop-pr-142.preview.example.com. There is no per-preview certificate, so Let's Encrypt rate limits never bite.
  • Full Docker Compose stacks, not just single containers. Several services, healthchecks and seed hooks all work, and each exposed service gets its own hostname.
  • No Dockerfile needed. Static, Node, Bun, Deno, workerd, Python and PHP apps are detected from their files, and a gangway.yml can say more.
  • Throwaway databases. Postgres, MySQL or Redis run beside the app, their URLs are handed to it as environment variables, and they are gone when the preview is. The preview page has a data browser.
  • Previews expire and sleep. Each preview gets a time to live, idle ones go to sleep, and the first request wakes them.
  • Who can see a preview is up to you. A preview can be public, unlisted (an unguessable hostname), password-protected, or visible only to people signed in to gangway.
  • Static sites and artifacts are served by gangway itself. They need no container and deploy in milliseconds. One artifact.md renders as a document, a slide deck, a dashboard or a prototype.
  • Accounts and permissions. Roles are made of per-feature permissions that you can remap. There are API tokens and OAuth for MCP clients, and an audit log.

How it works

app.<domain>   ─┐
api.<domain>   ─┼─▶  UI · REST API · OAuth ─┐
mcp.<domain>   ─┤    MCP                    │
hooks.<domain> ─┘    GitHub webhooks        ├─▶ builder · scheduler · SQLite
                                            │          │
*.<domain>     ───▶  proxy: gate, wake ─────┘          ▼ Docker API
                          │                     Docker host(s)
                          └──────────────────▶  your preview's containers

gangway dispatches every request on its Host header. The reserved labels (app, api, mcp, hooks) reach gangway itself, and every other label is a preview. It runs your stack with docker compose, under a policy that refuses anything that would reach the host: privileged mode, bind mounts, host networking, the Docker socket and the like. It also drops Linux capabilities and applies memory and process limits. SQLite is the source of truth, and every container carries labels that describe it, so a restart reconciles the two.

Quickstart

Status: early. gangway is v0.x and runs in production for its author. Expect rough edges, and read Security before you expose it.

On a host with Docker, as root or a user in the docker group:

curl -fsSL gangway.sh/install | sh

The installer asks where gangway answers: on your domain behind a reverse proxy (the default), on your domain with gangway holding port 443 and its own Let's Encrypt certificate, or with no domain (--lan to try it from your network, --local for this machine only). It starts gangway and prints a one-time link that creates the first admin account; there are no default credentials. Run it again to upgrade; a new version that does not come up healthy is rolled back.

  • Install: flags, platforms, and running it by hand with compose.yaml.
  • Unraid: gangway-inabox runs gangway in a VM it creates, like Home Assistant in a Box; the plain gangway template runs it on Unraid's Docker. Both are in Community Applications: search Apps for gangway.
  • In a VM: vm/cloud-init.yaml turns a stock Debian or Ubuntu cloud image into a gangway VM, in any hypervisor.
  • Reverse proxy: Nginx Proxy Manager, SWAG, Caddy and Traefik.

Deploy something

From the UI, go to New preview and drop in a folder. You can also start from a runtime's example, or add a throwaway database.

The New preview page: runtime starters, a drop zone for a folder, files or a .zip, and Postgres, MySQL and Redis add-ons.

From a terminal:

tar -czf - . | curl --fail -X POST \
  -H "Authorization: Bearer $GANGWAY_TOKEN" -H "Content-Type: application/gzip" \
  --data-binary @- "https://api.preview.example.com/v1/previews?name=hello&runtime=auto&wait=true"

The answer includes the URL once the preview serves it. Create tokens under Account. Deploying covers how gangway reads an app, gangway.yml, databases, expiry and visibility.

Connect an agent

Turn on MCP under Admin → Server → Surfaces, then copy your client's setup from Account → Connect an agent. For Claude Code:

claude plugin marketplace add charlesabarnes/gangway && \
  claude plugin install gangway@gangway --config mcp_url=https://mcp.preview.example.com/

Run /mcp, sign in to gangway, and choose what the agent may do. Any MCP client works with the MCP URL; Connect an agent has the scopes, the tools, and settings that make Claude Code pick gangway over its own artifacts.

Pull-request previews

  1. Under Admin → GitHub, create a GitHub App in one click through GitHub's manifest flow, so no secret is copied by hand. Install it on your repositories.
  2. Under Repositories, connect a repository. By default gangway gives you a workflow file to commit. It builds on GitHub Actions and hands gangway the image, authenticated by the run's OIDC token. The alternative is to have gangway build from webhooks.
  3. Open a PR. The preview's URL arrives in a sticky comment and a GitHub Deployment. Pull requests from forks wait until a maintainer comments /preview deploy.

Documentation

Everything else is at gangway.sh/docs: configuration, domains, sharing, upgrades and backups, security, known limits and troubleshooting. The docs' source is in guide/.

gangway talks to the Docker socket, which is root on its host; read Security before you give anyone an account. Report vulnerabilities privately through GitHub's Security → Report a vulnerability, or by email to security@gangway.sh, not in an issue.

Development

bun install
bun run test         # server, shared and render; no Docker needed
bun run typecheck && bun run lint
cp scripts/dev.example.json scripts/dev.json   # then point it at your Docker host
GANGWAY_CONFIG=scripts/dev.json bun run dev
cd web && npm install && npm start   # the Angular UI

The server is TypeScript on Bun, Hono and bun:sqlite. The UI is Angular with Tailwind. Every dependency is free of native addons.

License

Apache-2.0

Media gallery

1 / 2

Install Gangway on Unraid in a few clicks.

Find Gangway in Community Apps on your Unraid server, review the template, and click Install. Unraid handles the Docker app or plugin setup from the published template.

Open the Apps tab on your Unraid server Search Community Apps for Gangway Review the template variables and paths Click Install

Requirements

A domain with a wildcard DNS record and a reverse proxy on this server, or sslip.io to try it without one. gangway holds the Docker socket and runs the code you deploy; to keep previews off Unraid's Docker, install gangway-inabox instead.

Related apps

Details

Repository
ghcr.io/charlesabarnes/gangway:latest
Last Updated2026-09-30
First Seen2026-09-30

Runtime arguments

Network
host
Shell
sh
Privileged
false
Extra Params
--stop-timeout 15

Template configuration

Base domainVariable

e.g. preview.example.com; the dashboard is app.preview.example.com and previews get their own names under it.

Target
GANGWAY_BASE_DOMAIN
StatePathrw

Database, logs, uploads and backups.

Target
/state
Default
/mnt/user/appdata/gangway
Value
/mnt/user/appdata/gangway
Listen addressVariable

172.17.0.1 (docker0) is where a proxy container in bridge mode reaches gangway. Use :: to answer on every address.

Target
GANGWAY_LISTEN_ADDRESS
Default
172.17.0.1
Value
172.17.0.1
Listen portVariable
Target
GANGWAY_LISTEN_PORT
Default
8443
Value
8443
HTTP redirect portVariable

A plain-HTTP listener that redirects to HTTPS. Empty behind a proxy, which redirects itself.

Target
GANGWAY_LISTEN_HTTP_PORT
Public portVariable

The port browsers use: 443 behind a proxy, 8443 with no proxy.

Target
GANGWAY_PUBLIC_PORT
Default
443
Value
443
Trusted proxiesVariable

Where the proxy connects from; its forwarded headers are believed. Empty with no proxy.

Target
GANGWAY_TRUSTED_PROXIES
Default
172.17.0.0/16
Value
172.17.0.0/16
Share linksVariable

true turns on public Share links (Cloudflare quick tunnels); empty leaves it to Admin > Server.

Target
GANGWAY_SHARE
Admin tokenVariable

Optional API token with every permission. Empty: create an account from the first-admin link in the log, and tokens under Account.

Target
GANGWAY_ADMIN_TOKEN
TLS modeVariable

selfsigned: the proxy holds the real certificate. acme needs the Cloudflare settings in compose.yaml's notes.

Target
GANGWAY_TLS_MODE
Default
selfsigned
Value
selfsigned
InstanceVariable

Prefix of the containers gangway makes; unique per gangway on a host.

Target
GANGWAY_INSTANCE
Default
main
Value
main
OrphansVariable

report only logs containers that look like gangway's but that it does not know; stop stops them.

Target
GANGWAY_RECONCILE_ORPHANS
Default
report
Value
report
Docker socketPathrw

gangway runs previews as containers on this server.

Target
/var/run/docker.sock
Default
/var/run/docker.sock
Value
/var/run/docker.sock