
pingap v0.13.10
A reverse proxy like nginx, built on pingora, simple and efficient.
Pingap
A reverse proxy and application gateway built on Cloudflare Pingora. It is configured through TOML files or a web admin that is part of the binary, takes changes without a restart, and comes with more than thirty plugins.
中文说明 | Documentation · 中文文档 | Examples | Plugins | Crates | Upgrading
What sets it apart
- A web admin in the binary. Every setting has a form. A change is checked the way
pingap -tchecks it before it is stored, earlier versions can be kept and restored, and there are read-only accounts and API tokens. - Changes without downtime. Upstreams, locations, plugins and certificates are replaced in the running process (
--autoreload). What needs new listeners is applied by a restart that hands the sockets over (--autorestart, for a daemon on Linux), and the running process keeps serving if its replacement does not come up. - Over thirty plugins, per location and in the order you list them: authentication (JWT, OIDC, key, basic, HMAC, forward auth), access rules (IP, referer, user agent, blocked paths), traffic (rate and bandwidth limits, cache, mirroring, traffic splitting), content (compression, body substitution, redirects, static files, image optimisation) and operations (maintenance mode, health endpoint, error pages). All plugins
- Certificates without a cron job. Let's Encrypt or any other ACME CA, by HTTP-01 or DNS-01 (Alibaba Cloud, Cloudflare, Tencent Cloud, Huawei Cloud), RSA and ECDSA side by side, OCSP stapling, and mutual TLS towards clients and towards upstreams.
- Configuration that is checked before it runs.
-tbuilds everything a start would,--strictrefuses keys it does not know,--diffshows what a change would do. TOML, HCL or KDL, in files or in etcd, with secrets taken from the environment or from files. - Made to be watched. Prometheus metrics (pull and push), OpenTelemetry traces, access logs as text or JSON with more than thirty fields, and JA4 TLS fingerprints of the clients.
The rest of what a reverse proxy does is there too: HTTP/1.1, HTTP/2, WebSocket and gRPC-web; routing by host, path and regular expression; round robin, least connections, consistent hashing and sticky sessions, with health checks, retries and a circuit breaker; backends from DNS (A/AAAA, SRV) or Docker labels; the PROXY protocol on listeners and towards upstreams; OpenSSL or rustls.
Metrics, tracing and image optimisation are in the -full builds.
🚀 Getting Started
Docker Compose
# docker-compose.yml
services:
pingap:
image: vicanso/pingap:latest # for production, pin a release: vicanso/pingap:<version>-full
container_name: pingap-instance
restart: always
ports:
- "80:80"
- "443:443"
volumes:
# everything Pingap keeps: configuration, certificates, cache
- ./pingap_data:/opt/pingap
environment:
- PINGAP_CONF=/opt/pingap/conf
- PINGAP_ADMIN_ADDR=0.0.0.0:80/pingap
- PINGAP_ADMIN_USER=pingap
- PINGAP_ADMIN_PASSWORD=<YourSecurePassword> # change this
command:
- pingap
- --autoreload
mkdir pingap_data
docker compose up -d
The admin is at http://localhost/pingap. Images are tagged latest, full and rustls-full, and <version>, <version>-full, <version>-rustls-full for a release. Each also has a -distroless twin without a shell, where command has to start with pingap as it does above.
Binary
curl -sSL https://raw.githubusercontent.com/vicanso/pingap/main/install.sh | sh
installs the latest release to /usr/local/bin/pingap on Linux and macOS (x86_64 and arm64). Variables in front of sh choose what is installed:
| Variable | Effect |
|---|---|
PINGAP_FULL=1 | The -full build, with every optional feature |
PINGAP_TLS=rustls | Linux: the -rustls-full build, without OpenSSL (see TLS backend) |
PINGAP_LIBC=gnu | Linux: the glibc build in place of the static musl one |
PINGAP_SERVICE=1 | Linux with systemd: also the pingap service and a starter configuration in /etc/pingap/conf; it is not enabled or started |
PINGAP_VERSION=0.15.0 | That release in place of the latest |
All assets are on the releases page.
One command, no configuration file
# the certificate comes from Let's Encrypt
pingap --domain=pingap.io --upstream=192.168.1.1:3000
# or bring your own
pingap --domain=pingap.io --upstream=192.168.1.1:3000 --cert=/etc/ssl/pingap.io
- Without
--certthe certificate is ordered by HTTP-01: the domain has to resolve to this host, with port 80 reachable. It is kept in~/.pingap/acme/<domains>.tomland used again at the next start. Do not delete it, issuing is rate limited. --certis the certificate or the directory it is in (fullchain.pem/privkey.pem,cert.pem/key.pemandtls.crt/tls.keyare found;--keynames anything else).--upstreamand--domaintake lists separated by commas,--addrthe address to listen on.- The configuration is made anew at every start, so the admin cannot edit it. For more than one server use
--conf, which these flags cannot be combined with.
📝 Configuration
[upstreams.api]
addrs = ["api.github.com:443"]
discovery = "dns"
sni = "api.github.com"
[plugins.staticServe]
category = "directory"
path = "~/Downloads"
[locations.github-api]
upstream = "api"
path = "/api"
proxy_set_headers = ["Host:api.github.com"]
rewrite = "^/api/(?<path>.+)$ /$1"
[locations.static]
plugins = ["staticServe"]
[servers.test]
addr = "127.0.0.1:6118"
locations = ["github-api", "static"]
The same can be written in HCL or KDL. Every key is described in the configuration reference.
--autoreload | A change is applied without a restart: within ten seconds from files, at once from etcd. What takes a restart is reported, and waits for one. The mode for containers |
-a, --autorestart | The same, and a restart that drops no request for what a reload cannot change, a listen address for one. For a daemon (-d) on Linux; anywhere else it reloads, and reports what waits |
-t | Builds every upstream, location, plugin, certificate and server as a start does, and binds nothing |
--strict | A key Pingap does not know, or one this build has no feature for, is an error, where it is otherwise a warning. For -t in CI |
--diff <other> | Prints what would change if <other> took the place of the configuration, credentials shown as checksums |
$ENV:NAME, $FILE:/path | As a whole value: replaced by the variable or the file when the configuration is loaded |