A self-contained, Dockerized Xray VPN node. You provision the whole host with one command, and it ships its metrics back to your manager's Grafana.
env_file.Full requirements are on the website.
# 1. Get the code
git clone https://github.com/compassvpn/agent.git
cd agent
# 2. Configure it
cp env_file.example env_file
nano env_file # paste the values from your manager setup
# 3. Bring it up
./agent.sh start
The first run installs Docker and builds the images, so give it a few minutes. Metrics should appear in your Grafana within 5 to 10 minutes.
Everything goes through ./agent.sh (run ./agent.sh help to list them):
| Command | What it does |
|---|---|
./agent.sh start |
Set up, update, or restart everything. Run this first and any time after; it's safe to re-run. |
./agent.sh stop |
Stop and remove the containers, networks, volumes and images. |
./agent.sh update |
Pull the latest code and reconverge. |
./agent.sh configs |
Print the VPN config links. |
./agent.sh logs [service] |
Tail the container logs. |
./agent.sh help |
Show the command list. |
Two files drive everything:
agent.sh is the launcher you run. It makes sure uv
is installed (Ansible's control side needs Python 3.12+, newer than these releases ship,
and uv fetches one on its own, so there's nothing system-wide to manage), then runs the
playbook from a pinned toolchain (pyproject.toml plus uv.lock) so every server gets
the exact same Ansible and dependency versions.agent.yml is a single Ansible playbook that provisions the host and deploys the
stack, in stages../agent.sh start runs the playbook top to bottom:
env_file), used as a
metrics label.docker compose up -d --build to (re)start the stack.It's idempotent, so re-running ./agent.sh start only changes what has drifted
(usually just a container rebuild). Each stage is also a tag, so you can run one on its
own, for example uv run --frozen ansible-playbook agent.yml --tags deploy.
The Ansible version is pinned in pyproject.toml and locked in uv.lock. To move to a
newer Ansible, bump it there and run uv lock; to refresh the pinned dependencies without
changing Ansible, run uv lock --upgrade. Commit both files either way.
Six containers, wired up in docker-compose.yml:
| Service | Role |
|---|---|
xray-config |
Builds the Xray config, fetches and renews TLS certs (acme.sh and Cloudflare), runs the config self-tests. Internal only. |
xray |
The proxy that handles all the public VPN traffic. Brings up a WireGuard tunnel for egress in warp mode. |
nginx |
Public TLS front plus a decoy site for anything that isn't real proxy traffic. |
xray-exporter |
Turns Xray's access log and stats API into Prometheus metrics on :9550, with GeoIP enrichment. |
node-exporter |
Host metrics (CPU, memory, disk, network). Internal only. |
metric-forwarder |
Runs Grafana Alloy, which scrapes the metrics above and ships them to your manager's Grafana. |
$ claude mcp add agent \
-- python -m otcore.mcp_server <graph>