# Introducing portway

> A small gateway for routing and rate-limiting internal service traffic. Built because the alternatives were either too much or too little.

Published: 2026-06-24
Category: Open Source
Author: Relayweave Labs
Tags: open-source, go, kubernetes, platforms
Canonical: https://relayweavelabs.com/notes/introducing-portway

Every platform we have worked on eventually grows a layer between services. Somebody needs per-client rate limits. Somebody needs to route a percentage of traffic to a new version. Somebody needs a single place to attach authentication. The first time, it is a few lines in each service. The fifth time, it is a full service mesh with a control plane, a sidecar per pod, and a learning curve nobody budgeted for.

portway is our attempt at the middle. A single Go binary that sits in front of internal services and does a small number of things well.

## What it does

- **Routing** by host, path prefix, and header, with weighted upstreams for gradual rollouts.
- **Rate limiting** per route and per client key, using a token bucket that is local by default and can be backed by a shared store when limits need to be global.
- **Retries and timeouts** with per-route budgets, so a slow upstream cannot consume the gateway.
- **Authentication hooks** that call out to a policy endpoint and cache the decision.
- **Health and readiness** that reflect upstream state, so orchestrators do the right thing.

Configuration is a single file, reloaded on change without dropping connections. There is no control plane, no CRDs, and nothing to run other than the binary.

## What it deliberately does not do

It is not a service mesh. There is no sidecar model and no mutual TLS between every pair of services. If you need that, you need a mesh, and portway will not pretend otherwise.

It is not an edge gateway. It has no WAF, no bot management, and no interest in being exposed to the internet. Put something built for that in front of it.

It does not do transformations. Request and response bodies pass through untouched. Rewriting payloads in a gateway is a maintenance burden that belongs in the services.

## Why Go

The gateway is on the hot path for everything, so predictability matters more than raw throughput. Go's runtime is boring in the ways that count: small memory footprint, no warm-up, and a standard library HTTP stack that has been hardened by a decade of production use. A single binary also makes distribution trivial, which matters for a tool meant to be dropped into an existing cluster in an afternoon.

## Running it on Kubernetes

The reference deployment is a Deployment with a few replicas behind a Service, with the config mounted from a ConfigMap. A small controller watches the ConfigMap and signals a reload. Horizontal scaling is a replica count. There is a Helm chart, but the raw manifests are short enough to read.

```yaml
routes:
  - match: { host: api.internal, prefix: /orders }
    upstreams:
      - { url: http://orders-v1:8080, weight: 90 }
      - { url: http://orders-v2:8080, weight: 10 }
    rate_limit: { per_client: 200/m }
    timeout: 5s
```

## Status

portway runs in production for two of our clients and in our own lab. The configuration format is stable. The policy hook interface is likely to change once more before we call it settled. It is open source under the MIT licence, and the repository is linked from [the lab](/#lab).

If you have a use case it almost fits, we would like to hear about it. Small tools stay small by saying no to most things, but the things they say yes to should be the ones people actually need.