CLI tool
outray-tunnel/outray avatar
outray-tunnel/outray

OutRay: a self-hosted ngrok alternative with a real server behind it

Open-source ngrok alternative.

1,153 stars122 forksTypeScriptAGPL-3.0

At a glance

What is it?
OutRay is an AGPL-3.0 TypeScript project that exposes a local server to the internet over HTTP, TCP or UDP tunnels, with custom domains, TLS, a dashboard and team roles. The judgement is that it is a complete server rather than a thin client, which is what makes it a different proposition from the ngrok alternative it bills itself as.
Who is it for?
OutRay fits a team that wants to self-host its tunnel service, needs TCP or UDP alongside HTTP, and is prepared to run and secure a server component with its own database and cache. It does not fit a user who wants a zero-setup tunnel for a demo, and it does not fit a team that cannot take on the operational side of a public ingress.
Can I use it commercially?
Yes, with strict conditions. AGPL-3.0 is a network copyleft licence: if people use a modified version over a network, for example as a hosted service, you must offer them its source code under the same licence.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three tunnel protocols, not just HTTP

OutRay is described as an open-source ngrok alternative, and the headline promise is exposing your localhost to the internet so you can share local servers with anyone, anywhere.

The feature list is what sets the scope. HTTP tunnels expose web servers with custom subdomains. TCP tunnels tunnel any TCP service, with the example given being databases and game servers. UDP tunnels tunnel UDP traffic, with DNS, VoIP and TFTP named. Custom domains bring your own domain with automatic TLS. A dashboard lets you monitor traffic, view analytics and manage tunnels. And team support lets you collaborate with organizations and role-based access.

The TCP and UDP entries are the reason this is not simply a reverse proxy. A service that tunnels a PostgreSQL port or a DNS server over UDP is doing something a plain HTTP reverse proxy cannot, and it is the feature that separates a general-purpose tunnel from an ngrok-shaped clone.

The three protocols are created with the same command shape:

bash
# HTTP tunnel
outray http 3000

# TCP tunnel (e.g., for PostgreSQL)
outray tcp 5432

# UDP tunnel
outray udp 53

The CLI is a single global npm package:

bash
npm install -g outray

So the client side is a small install and a short command, and the complexity is all on the server side, which is where the interesting engineering in this project lives.

The repository is a server, not a client

The project structure in the README is the clearest statement of what OutRay is, because it is a full multi-application system rather than a single binary.

code
outray/
β”œβ”€β”€ apps/
β”‚   β”œβ”€β”€ cli/             # CLI client
β”‚   β”œβ”€β”€ cron/            # Background jobs
β”‚   β”œβ”€β”€ internal-check/  # Domain verification for Caddy
β”‚   β”œβ”€β”€ landing/         # Marketing website
β”‚   β”œβ”€β”€ tunnel/          # Tunnel server
β”‚   └── web/             # Dashboard & API
β”œβ”€β”€ shared/              # Shared utilities
└── deploy/              # Deployment configs

Six applications. The `tunnel` app is the tunnel server itself, the one that terminates connections and relays traffic. The `web` app is the dashboard and the API. The `cli` is the client you install globally. The `cron` app runs background jobs, which is what a tunnel service needs for expiring idle tunnels and for the analytics rollup. The `internal-check` app exists for one specific purpose, domain verification for Caddy, which tells you the reverse proxy in front of the tunnel is Caddy and that TLS for a custom domain is proven by a request to a service of your own. The `landing` app is the marketing website.

That is a substantial piece of infrastructure, and it is the main thing to weigh against the ngrok comparison. An ngrok alternative that is a single client talking to someone else's cloud is a much smaller commitment than running your own tunnel server with a control plane, a web console and a job runner.

The root `package.json` confirms the workspace arrangement, with npm workspaces for `apps/tunnel`, `apps/cli` and `apps/web`, and separate `dev:tunnel` and `dev:web` scripts for running the two halves locally. The `build` script builds all three in sequence, and `install:all` walks the workspaces in turn to install.

The `shared/` directory holds the utilities the applications have in common, and `deploy/` holds the deployment configuration, with a `deploy.sh` at the root.

PostgreSQL, Redis and TimescaleDB are the runtime requirements

The requirements are listed and they are not a light set.

Node.js 20+, PostgreSQL, Redis, and Tiger Data (TimescaleDB).

The three data stores do three different jobs, and the `.env.example` shows how. `DATABASE_URL` is a PostgreSQL connection, `REDIS_URL` is a Redis connection, and the presence of Tiger Data means the time-series workload, the tunnel traffic records that the dashboard's analytics feature reports on, is stored in a PostgreSQL extension built for time series rather than in plain tables.

That is a coherent design for a service whose central data is a stream of events over time. Traffic analytics across many tunnels is exactly the workload TimescaleDB is built for, and the cost is a third data store to operate rather than two.

The `.env.example` also shows the rest of the system's shape. There is a `BETTER_AUTH_SECRET` and a `BETTER_AUTH_URL`, so authentication goes through Better Auth, and there are OAuth credentials for GitHub and Google. `INTERNAL_API_SECRET` is described as a secret shared between web and tunnel server, which is how the two applications authenticate to each other, and it is a static shared secret rather than a token exchange, so it is worth treating as a real credential rather than a development placeholder.

Billing runs through Polar, with an access token, a webhook secret, a server setting that is `sandbox` for testing and `production` for live, and product identifiers for a set of plans. The monthly products are `RAY`, `BEAM` and `PULSE`, with matching yearly variants, and the same identifiers are exposed to the frontend through `VITE_` prefixed variables, which is a standard Vite pattern for making server-side configuration values available in the client build.

Email goes through Zepto Mail with a `ZEPTO_API_KEY`, and the repository root has a `test-server/` directory, which suggests a local target for exercising the tunnel path without pointing at a real service.

The CLI is one command, the client surface is a package

The client side is deliberately minimal, and the documentation is honest about where the complexity lives.

A single global install gives you the `outray` command, and the three subcommands create HTTP, TCP and UDP tunnels against a local port. The full documentation is at outray.dev/docs, and the README is a summary rather than a manual, which is the right shape for a project this size.

The npm package name is `outray`, and the root manifest is marked `private: true` with version `1.0.0`, which tells you the published artefact is the CLI inside `apps/cli` rather than the root project.

The root dependencies are a small sign of the client side's nature. `axios` and `zustand` are the only runtime dependencies, and `typescript` at `^5.7.2` is the sole dev dependency. Zustand is a state management library, and its presence at the root is a hint that the CLI or the web client shares a state layer rather than each application carrying its own.

For a user, the workflow is: install the CLI, authenticate against the service, run a tunnel command, and get a public URL for HTTP or a public endpoint for TCP and UDP. The dashboard is where you would then see the traffic and manage what is running, and team support with role-based access is how you would let colleagues open tunnels without sharing credentials.

The one thing the README does not tell you, and it is the thing an adopter will want, is how the client authenticates to the tunnel server and how the tunnel handshake works at the protocol level. The `apps/internal-check` entry hints that the tunnel server sits behind Caddy and that custom domain verification is a service of its own, so the ingress path is Caddy plus the tunnel application, which is a reasonable arrangement but one you would want to read the docs for before designing around it.

Self-hosting is the point, and the AGPL is the price

The licence is AGPL-3.0-only, and for a network service that is a decision with real consequences, so it deserves a plain statement rather than a summary.

The AGPL extends copyleft to network use. If you run a modified version of OutRay and let users interact with it over a network, you owe those users the corresponding source of your modified version. For a project whose entire purpose is to be a public ingress, that is a much more likely situation than for a library, and a team that wants to operate a modified OutRay without publishing the changes cannot use the AGPL version.

That is the same consideration that applies to any self-hosted network service under copyleft, and it is the price of the openness the project is selling. The upstream project is AGPL-3.0-only, and a fork inherits that choice unless the copyright holders grant a different licence, which for an individual or small project is a conversation rather than a given.

The alternative is not really a question of licence, though. The alternative is a hosted tunnel service, where the operator bears the operational cost and the user bears the dependency. OutRay's pitch is that you can be that operator, and the costs of being the operator are the ones the requirements section lists: three data stores, a Node.js 20+ runtime, and the deployment configuration in `deploy/`.

The project is also part of the Vercel OSS Program, indicated by the badge at the top of the README, which is a hosting-provider programme rather than a functional feature, but it does signal that the project has some institutional support.

The release history is short. The listed versions are v0.1.6 on 2026-01-24, v0.1.7 on 2026-02-10, and v0.1.14 on 2026-10-01, so the project is at patch level 14 of its first minor version. The last push was on 2026-10-15 and the repository is not archived, so the code is being worked on.

Editorial conclusion

OutRay fits a team that wants to self-host its tunnel service, needs TCP or UDP alongside HTTP, and is prepared to run and secure a server component with its own database and cache. It does not fit a user who wants a zero-setup tunnel for a demo, and it does not fit a team that cannot take on the operational side of a public ingress. Verify first that the tunnel server can reach your service over the protocol you need, since TCP and UDP tunnels are the feature that distinguishes it from a hosted reverse proxy, that your ingress terminates TLS and handles the custom domain verification the way the `internal-check` app expects, and that the AGPL-3.0 obligations fit your deployment model before you expose it publicly.

Frequently asked questions

How do I install and start an OutRay tunnel?

Install the CLI globally with `npm install -g outray`, then run `outray http 3000` for an HTTP tunnel, `outray tcp 5432` for a TCP service such as PostgreSQL, or `outray udp 53` for UDP traffic.

What does OutRay need to run on the server side?

The stated requirements are Node.js 20+, PostgreSQL, Redis and Tiger Data (TimescaleDB). The `.env.example` shows a PostgreSQL `DATABASE_URL`, a `REDIS_URL` and configuration for Better Auth, GitHub and Google OAuth, Zepto Mail and Polar billing.

What is the difference between OutRay and ngrok?

OutRay is a self-hostable tunnel server rather than a client for a hosted service, and it supports TCP and UDP tunnels alongside HTTP. The repository contains a tunnel server, a web dashboard and API, a background job runner, a domain verification service and a marketing site, so operating it means running that stack with PostgreSQL, Redis and TimescaleDB.

Can OutRay use a custom domain with TLS?

Yes, custom domains are a listed feature with automatic TLS. The `apps/internal-check` application exists for domain verification for Caddy, which indicates the tunnel server sits behind a Caddy proxy that handles the certificate and the domain proof.

Under what licence is OutRay released?

AGPL-3.0-only, stated in the README and with the `LICENSE` file at the repository root. For a network service this means that running a modified version and exposing it over a network carries an obligation to offer the corresponding source of those modifications.

Official sources

  1. License: AGPL-3.0
  2. outray-tunnel/outray on GitHub
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/outray-tunnel-outray.svg)](https://hysenlabs.com/projects/outray-tunnel-outray)

Was this useful?