Aperio exposes a service running on your machine (or inside a private network) to the public internet through a single outbound WebSocket connection. Nothing on your side accepts inbound traffic, the client dials out to the server and requests flow back through that tunnel.
You need two pieces:
aperio-serveron a machine with a public address (usually behind a TLS-terminating proxy such as Traefik, Caddy, or nginx).aperio-clientnext to the service you want to expose.
# on the public box
docker run -d --name aperio-server \
-p 8080:8080 \
-e APERIO_SERVER_TOKEN="change-me-to-a-long-random-string" \
-v ./data:/app/data \
ghcr.io/co3moz/aperio-server:latestThe token is the master credential: it authenticates tunnel clients and doubles as the dashboard admin password. The ./data volume persists dynamic tokens, statistics, the audit log, and webhooks across restarts, don't skip it.
With Docker:
# on the machine next to the service you are exposing
docker run -d --name aperio-client \
--network host \
-e APERIO_SERVER_TOKEN="change-me-to-a-long-random-string" \
-e APERIO_SERVER_URL="http://your-server-ip:8080" \
-e APERIO_TARGET="http://localhost:3000" \
ghcr.io/co3moz/aperio-client:latestOr with the CLI (installed via curl -sSf https://raw.githubusercontent.com/co3moz/aperio/master/install.sh | sh):
# on the machine next to the service you are exposing
aperio-client 3000 --server-url https://tunnel.example.com --server-token apr_xxxxxxxxOpen http://your-server-ip:8080, requests are proxied to your local port 3000. The admin dashboard lives at /aperio (user aperio, password: your token).
If something doesn't work, run aperio-client check: it verifies the server's health endpoint, compares client/server versions, performs a real token handshake, and probes your local target, exit code 0 means every hop is green.
A single client process can expose several targets, put a services: list in aperio.yaml (each entry with its own target, hostname/path, and health probe) and the client opens one tunnel per entry. See Multiple services in the configuration reference.
- Put the server behind TLS and set
APERIO_TRUST_PROXY=1(yamltrust_proxy), see Tokens & Authentication for why the master token should never travel in plaintext. - Give each client its own hostname, see Routing & Load Balancing.
- Mint scoped tokens instead of sharing the master token, see Tokens & Authentication.
- Browse every setting on both sides, see the Configuration Reference.
Copy-and-adapt config pairs for this topic:
simple: minimal one-target pair