Skip to main content

Prerequisites

RequirementMinimumRecommended
Linux kernel5.106.1+
Rust toolchain1.75latest stable
Redis (optional)6.07.x
Kernel 6.1+ is recommended for multishot accept and SQPOLL stability. Verify your kernel:
uname -r

Building

Build the arc-gateway binary in release mode:
git clone https://github.com/shuakami/Arc
cd arc
cargo build -p arc-gateway --release
Output binary: ./target/release/arc-gateway

Systemd (bare metal or VM)

Install

Run the install script as root:
sudo bash deploy/install-systemd.sh
This script does the following:
  1. Copies ./target/release/arc-gateway/usr/local/bin/arc-gateway
  2. Creates /etc/arc/
  3. Copies arc.example.json/etc/arc/arc.json
  4. Installs deploy/arc-gateway.service/etc/systemd/system/
  5. Runs systemctl daemon-reload && systemctl enable arc-gateway
Edit your config, then start:
# Edit the configuration
sudo vim /etc/arc/arc.json

# Start Arc
sudo systemctl start arc-gateway

# Check status
sudo systemctl status arc-gateway

Service unit settings

The service unit is installed at /etc/systemd/system/arc-gateway.service.
DirectiveValuePurpose
ExecStart/usr/local/bin/arc-gateway --config /etc/arc/arc.jsonLaunch binary
ExecReload/bin/kill -HUP $MAINPIDTrigger hot reload
Restarton-failureAuto-restart on crash
RestartSec5sBackoff between restarts
TimeoutStopSec35sDrain window before SIGKILL
WatchdogSec60ssystemd watchdog interval
LimitNOFILE1048576Max open file descriptors
LimitNPROC65536Max processes
Security hardening enabled by default:
  • NoNewPrivileges=yes
  • PrivateTmp=yes
  • ProtectSystem=strict
  • ProtectHome=yes
  • ProtectKernelTunables=yes
  • ProtectControlGroups=yes
  • RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX

Hot reload

After changing /etc/arc/arc.json:
# via systemd
sudo systemctl reload arc-gateway

# directly
sudo kill -HUP $(pidof arc-gateway)
In-flight connections are not dropped. If the new config contains changes to listener addresses, worker count, io_uring ring sizing, or control plane binding, the reload is rejected and you must restart the process instead.

Useful commands

# Follow logs
sudo journalctl -u arc-gateway -f

# Check metrics
curl http://localhost:9090/metrics

# Health check
curl http://localhost:9090/healthz

Kubernetes

All Kubernetes manifests are under deploy/kubernetes/. Apply them in order:
kubectl apply -f deploy/kubernetes/namespace.yaml
kubectl apply -f deploy/kubernetes/secret.yaml
kubectl apply -f deploy/kubernetes/configmap.yaml
kubectl apply -f deploy/kubernetes/deployment.yaml
kubectl apply -f deploy/kubernetes/service.yaml
kubectl apply -f deploy/kubernetes/hpa.yaml

Resources created

ResourceDetails
Namespacearc-gateway
ConfigMaparc-gateway-config — contains arc.toml
Secretarc-gateway-secret — contains auth-token
Deploymentreplicas=2, rolling update
Service (ClusterIP)Ports 8080, 8443, 9090
Service (NodePort)Node ports 30080 (HTTP), 30443 (HTTPS)
HPACPU-based autoscaling

Before you apply: set the auth token

The Secret must be created with an actual token before applying the Deployment:
# Edit deploy/kubernetes/secret.yaml and replace __ARC_TOKEN__
# Then apply
kubectl apply -f deploy/kubernetes/secret.yaml
Keep control_plane.auth_token in the ConfigMap consistent with this value.

ConfigMap key fields to override

KeyDefaultNotes
workers0 (auto-detect)Set to your pod’s CPU limit for predictable performance
control_plane.auth_token__ARC_TOKEN__Must replace
io_uring.sqpollfalseEnable for bare-metal nodes
upstreams[0].addr127.0.0.1:18080Point to your backend

Deployment spec

FieldValue
replicas2
strategyRollingUpdate, maxSurge=1, maxUnavailable=0
terminationGracePeriodSeconds35
HTTP port8080
HTTPS port8443
Admin port9090
CPU request/limit500m / 2000m
Memory request/limit256Mi / 1Gi
Liveness probe: GET /healthz on port 9090, initialDelaySeconds=5, periodSeconds=10 Readiness probe: GET /healthz on port 9090, initialDelaySeconds=3, periodSeconds=5

Graceful shutdown

A preStop hook runs sleep 5 before the container receives SIGTERM, giving the load balancer time to drain. terminationGracePeriodSeconds=35 matches the drain window.

XDP/eBPF in Kubernetes

XDP requires NET_ADMIN and SYS_ADMIN capabilities plus privileged: true. This is disabled by default in the manifests (commented out YAML). It is not compatible with most managed Kubernetes platforms.

Startup flow (arc-daemon)

Arc’s startup sequence when you run arc-gateway --config arc.yaml:
main()

  ├── load config from file
  ├── compile config → SharedConfig
  ├── (if control_plane.enabled) spawn control plane thread
  │       └── axum router: /v1/status, /v1/config, /v1/cluster/*, /v1/xdp/*

  ├── configure Pingora Server (worker count, keepalive pool)
  ├── register HTTP proxy service (ArcProxy)
  ├── configure listeners (HTTP/HTTPS/TCP/UDP)
  ├── (if metrics_enabled) register Prometheus service
  └── server.run_forever()
Worker count is derived from node.workers. If set to 0, it uses std::thread::available_parallelism().

Production checklist

Before going live:
  • Set node.workers to 0 (auto-detect) or pin to the number of CPU cores available to the process
  • Set max_connections to match your expected concurrent connection count
  • Configure LimitNOFILE in the systemd unit (default: 1048576)
  • Enable SO_REUSEPORT on listeners — Arc uses it to distribute connections across worker threads without kernel lock contention
  • Configure upstream pool.max_idle based on your peak concurrent request rate
  • Set control_plane.auth_token to a strong random value if the control plane port is reachable from outside loopback
  • Bind the metrics server to loopback only (observability.metrics_bind: "127.0.0.1:9090") or protect port 9090 with a firewall rule — the endpoint has no built-in authentication
  • Configure log rotation (logging.output.rotation.max_size and max_files)
  • Point Prometheus to <host>:9090/metrics
  • Verify hot reload works: kill -HUP <pid>, then check arc_config_reload_total increments in /metrics

Troubleshooting

Check journalctl -xe -u arc-gateway for the error. The most common causes are: the binary path in ExecStart does not exist, or the config file path is wrong. Verify with arc-gateway --config /etc/arc/arc.json --check (if --check is supported) or inspect startup logs.
The kernel may not support the requested io_uring operations. Try disabling SQPOLL (io_uring.sqpoll: false) and reducing uring_entries. Required minimum: Linux 5.10.
Run kubectl logs -n arc-system <pod>. Common causes are: arc-gateway-secret does not exist or auth-token key is missing; ConfigMap arc.toml references an upstream address that is not reachable at pod start; resource limits are too low and you need to increase resources.limits.memory.
Another process is using the port. Check with ss -tlnp | grep 8080. If this is a pod restart, the previous pod may not have fully terminated yet — increase terminationGracePeriodSeconds.
Fields such as node.workers, listener bind addresses, and io_uring.uring_entries require a full process restart. All others apply live. Check the arc_config_reload_total metric to confirm a reload fired.