AWS API Gateway
Rate limiting works, but you adopt its request/response model, its quotas, and its bill — and bolting it onto a service that already runs happily in a container is not a ten-minute job.
$1.00–$3.50 per million requests, forever
VGOV LLC · Rate limiting for containerized APIs
Charon is a drop-in reverse proxy that puts rate limiting, abuse detection, and bot mitigation in front of any containerized API — with no code changes to your service, no config language to learn, and no traffic leaving your VPC.
The situation
You have a container serving an API. It needs rate limiting before someone brute-forces your login route or scrapes you into an outage. Today you get three choices, and each one asks for something you'd rather not give.
Rate limiting works, but you adopt its request/response model, its quotas, and its bill — and bolting it onto a service that already runs happily in a container is not a ten-minute job.
$1.00–$3.50 per million requests, forever
Enormously capable, and priced accordingly — in money and in people. You inherit a config language, a plugin ecosystem, and the standing assumption that someone on staff owns it.
~$105/mo per service, plus $200 per extra million
Excellent at the edge — but every request now transits a third party. For teams with data-residency rules, latency budgets, or a security review to pass, that is where the conversation ends.
Your traffic leaves your VPC
One container, one YAML file, one environment variable pointing at your app. It speaks HTTP/1.1 and h2c, meters every caller, and turns the over-limit ones back before they ever touch your service. It runs inside your VPC as a sidecar or a standalone gateway, and it is Apache-2.0 at the core.
Flat rate per container. No per-request tax.
“Charon is not trying to out-feature Kong. It is trying to be the thing a two-person team deploys on a Tuesday afternoon and never thinks about again.”
— the design brief, verbatim
How it works
Every request takes the same four steps. Nothing reaches your service without passing through all of them, and the whole path is a handful of atomic operations and a sharded map lookup — not a plugin chain.
Over-limit callers are turned back at the ferry, not at your backend.
Deployment
Run it as a sidecar next to your app container, or as a standalone gateway in front of the service. Your load balancer points at Charon's port instead of your app's.
One environment variable — CHARON_UPSTREAM. Your application code does not change. Not one line, not one dependency.
Edit the YAML and send SIGHUP. Limits, abuse rules, and path overrides reload live; a config with a typo in it is rejected and the running one kept.
# Where your app lives. The only required field.
upstream: "http://app:3000"
key:
from: ip # ip | header | jwt
trusted_proxy: true # behind an ALB/NLB
limits:
algorithm: token_bucket
rate: 100
window: "1s"
burst: 200
overrides:
# the login route gets a much shorter leash
- path_prefix: "/auth/"
rate: 10
window: "1s"
abuse:
burst:
threshold: 50
action: block # or: log
docker run -p 8080:8080 -p 9110:9110 \
-e CHARON_UPSTREAM=http://your-api:3000 \
charon:marketplace
$ curl localhost:9110/metrics | grep charon_
charon_requests_total{decision="allow"} 14208
charon_requests_total{decision="deny_ratelimit"} 311
charon_requests_total{decision="deny_abuse"} 27
charon_active_keys 642
Metrics and health live on a separate admin port, so your scrapes and health checks are never rate limited and never compete with real traffic.
What ships today
No asterisks, no "available soon" hidden in a footnote. What isn't built yet is on the roadmap further down, named plainly.
Token bucket for burst-tolerant traffic, sliding window for strict ceilings. Key on IP, API key, JWT subject, or a custom header — and give individual path prefixes their own tighter budget.
Per-caller burst detection catches credential stuffing and scraping runs. Header-consistency checks catch clients that don't look like the browsers or SDKs they claim to be. Start in log mode, promote to block when you trust it.
Bodies stream through rather than buffering, so large uploads and downloads pass untouched. Your service sees the original method, path, query, and headers, plus a correct X-Forwarded-For chain.
Everything Charon does is described in about forty lines of YAML, with environment variable overrides for the deployment-shaped fields. Reload with SIGHUP; an invalid edit is refused and the running config survives.
A Prometheus endpoint and a health check on a dedicated admin port, structured JSON logs to stdout for CloudWatch or whatever you already run, and a Grafana dashboard in the repo to start from.
A 44 MB distroless image with no shell, running as an unprivileged user. It never phones home and never proxies your traffic through anyone else's network.
The arithmetic
Everyone else meters you by the request, so the better your product does, the more your rate limiter costs. Charon is priced per running container. Traffic doubles; the bill doesn't.
| At 100 million requests / month | Pricing model | Monthly |
|---|---|---|
| AWS API Gateway — HTTP API | $1.00 per million | $100 |
| AWS API Gateway — REST API | $3.50 per million | $350 |
| Kong Konnect Plus | ~$105 per service + $200 per extra million † | $105 + overage |
| Cloudflare / Fastly | Plan-based | Traffic leaves your VPC |
| Charon Pro — two containers | Flat hourly, per container | $116 |
† Kong Konnect Plus includes one million requests per gateway service and caps at ten million per month, so a hundred-million-request workload sits on a custom Enterprise contract rather than the list price shown. Competitor figures are their published US-region list prices as of August 2026 and are given for comparison only — check them yourself before making a decision. Charon's own figures are a draft proposal, as flagged below.
Pricing
Draft pricing — proposed figures, not yet final. Confirm before launch.
The whole rate-limiting core, in production, for free. Not a trial and not crippled — the same binary the paid tiers are built on.
About $58 per container per month, billed through your existing AWS account. No per-request charges at any volume.
For platforms reselling capacity to their own customers, and for teams who have to hand something to an auditor.
Pro and Enterprise capabilities are on the roadmap below and are not in the shipping container yet. We would rather tell you that here than have you find out after signing.
Evidence, not testimonials
Charon is new. Rather than borrow credibility we haven't earned, here is what is actually measured and verified in the repository — every number below comes from a test you can run yourself.
The test suite counts upstream hits, not just status codes — proving that a rate-limited or abuse-blocked request never reaches your service at all.
Reload is tested with deliberately broken YAML: Charon refuses it, logs why, and keeps serving on the last configuration that worked.
The roadmap below states plainly what is not built yet — single-instance limits, JWT decoded but not verified — because you will find out in production otherwise.
Roadmap
Today, limits are enforced per instance and held in memory, so running several replicas multiplies the effective limit until shared limiting ships. JWT subjects are decoded to identify a caller but signatures are not verified — safe for keying, not a substitute for authentication.
Questions
Charon sits in the request path, so treat it like any other proxy: run it as a sidecar so its blast radius is one pod or task, and let your orchestrator restart it. It is a single static binary with no database and no external dependencies, so a restart takes well under a second. Health and metrics are on a separate port from the data plane, so a saturated data plane doesn't blind your monitoring.
Not today. Charon speaks plaintext HTTP/1.1 and h2c, which is the normal shape behind an ALB, an NLB doing TLS termination, or a service mesh. Put it behind your existing terminator and it slots in without changing your certificate story.
Not yet, and this is the most important caveat to understand. Limits are currently held in memory per instance, so three replicas means roughly three times the effective limit. If you need a hard global ceiling right now, run Charon as a single standalone gateway rather than as a sidecar per pod. Shared limiting backed by Redis or ElastiCache is the next thing we're building.
The decision path is a handful of atomic counter increments and one sharded map lookup — no allocation, no lock held across the request, no plugin chain. In our own measurements the p99 for the full round trip over loopback, including the test upstream's own processing, was under half a millisecond. Measure it in your own environment: the latency histogram is on the metrics endpoint from the first request.
No. That is the entire point. Charon is a reverse proxy in front of your service, so your code, your framework, and your dependencies stay exactly as they are. If you want the real client IP in your logs, read the X-Forwarded-For header Charon maintains for you.
If you already run Kong or Envoy well, you probably shouldn't — they do far more than Charon does and you've already paid the operational cost of learning them. Charon is for the team that needs rate limiting this week, doesn't have a platform engineer, and doesn't want to take on a config language and a plugin ecosystem to get it.
No. The rate limiting core is Apache-2.0 and production-ready, with no request caps, container caps, or time limits. The commercial layer is a separate directory in the repository covering the operational and multi-tenant features — adaptive thresholds, the dashboard, SIEM export — that larger organizations pay for. You can read that code too; you just need a subscription to run it in production.
None. Charon runs inside your VPC, makes no outbound calls, and has no telemetry or licence phone-home. Metrics stay on your metrics endpoint and logs stay on your stdout.
Get started
Run the container against your own service and watch the metrics endpoint. If it isn't useful in ten minutes, it isn't the product we set out to build.