Arion deploys as a single static binary (arion) plus one YAML configuration file — no sidecars, no state on disk. This page shows the three common ways to run it: a systemd service, the Docker image, and Kubernetes.
Run under systemd
Standalone
Install the binary and configuration, then let systemd supervise it:
[Unit]
Description=Arion gateway
After=network-online.target
Wants=network-online.target
[Service]
ExecStart=/usr/local/bin/arion --config /etc/arion/arion.yaml
Restart=on-failure
User=arion
LimitNOFILE=1048576
[Install]
WantedBy=multi-user.targetsystemctl daemon-reload && systemctl enable --now arion
Arion logs to stdout, so journalctl -u arion shows the application log. Raise LimitNOFILE deliberately: every proxied connection consumes file descriptors on both sides. Configuration changes require a restart unless you use an xDS control plane.
Run in Docker
The repository ships a docker/Dockerfile that builds the binary and bakes in the sample configuration at /etc/arion/arion-runtime.yaml, listening on 8000 (HTTP) and 8001 (TCP):
git submodule update --init --recursive
docker build -t arion-proxy -f docker/Dockerfile .
docker run -d -p 8000:8000 --name arion-proxy arion-proxy
Mount your own file over the baked-in one to use a real configuration:
docker run -d -p 8080:8080 \
-v $(pwd)/arion.yaml:/etc/arion/arion-runtime.yaml \
-e ARION_CPU_LIMIT=2 \
--name arion-proxy arion-proxy
The entrypoint substitutes the literal string CONTROL_PLANE_IP in the config with the CONTROL_PLANE_IP environment variable, which is a convenient hook for pointing at an xDS server (port 50051 in the samples). Two things to know about the stock image: there is no published registry image yet, so build and push your own; and the Dockerfile builds without optional Cargo features, so add --features metrics,prometheus,access-log,... to its cargo build line for the observability endpoints you want.
Run on Kubernetes
Kubernetes
For Gateway API management, install the Arion control plane using its Helm chart. The controller translates Gateway and route resources into proxy configuration and can provision the proxy workloads, or use proxies you manage yourself.
For a proxy managed directly, use a ConfigMap and Deployment. Ship the config as a ConfigMap, mount it, and probe readiness through the admin interface:
apiVersion: apps/v1
kind: Deployment
metadata:
name: arion
spec:
replicas: 2
selector: { matchLabels: { app: arion } }
template:
metadata: { labels: { app: arion } }
spec:
containers:
- name: arion
image: registry.example.com/arion-proxy:latest
args: ["--config", "/etc/arion/arion.yaml"]
ports:
- containerPort: 8080
- containerPort: 9901
resources:
requests: { cpu: "1", memory: 256Mi }
limits: { cpu: "2", memory: 512Mi }
readinessProbe:
httpGet: { path: /ready, port: 9901 }
volumeMounts:
- { name: conf, mountPath: /etc/arion }
volumes:
- name: conf
configMap: { name: arion-config }
Create the ConfigMap with kubectl create configmap arion-config --from-file=arion.yaml and apply the Deployment with kubectl apply -f arion-deploy.yaml. The readiness probe requires the admin block in your bootstrap; keep port 9901 off any Service you expose.
CPU limits and runtime sizing
Arion sizes its worker threads from the CPUs it believes it has. On Linux it reads the cgroup CPU quota first — v2 cpu.max, then v1 cfs_quota_us/cfs_period_us, rounding fractional quotas up — and only falls back to the host's CPU count when no quota is set. A container limited to cpu: 2 therefore gets a two-thread proxy runtime, not one sized to the node.
When the cgroup files are not readable (restricted runtimes, unusual sandboxes), set the thread count explicitly with the ARION_CPU_LIMIT environment variable or runtime.num_cpus in the configuration, matching your CPU limit. The environment variable wins over the file. The remaining runtime knobs are covered in performance tuning.
Verify
curl -i http://<host>:9901/ready # 200 LIVE once the runtimes are up
curl -i http://<host>:8080/ # traffic through your listener
A 503 from /ready means the runtimes are still coming up. Connection refused means the process exited (a configuration it cannot honor stops it with an error) or the admin block is missing — either way, the log has the answer.