Skip to main content

Prerequisites

Kernel 6.1+ is recommended for multishot accept and SQPOLL stability. Verify your kernel:

Install the binary

This script detects your architecture, downloads the latest GitHub Release binary, verifies SHA256, and installs arc-gateway to /usr/local/bin/arc-gateway. Current release assets are arc-gateway-linux-x86_64 and arc-gateway-linux-arm64.

Systemd (bare metal or VM)

Install

Install the service unit and default config files:
The service unit expects:
  • Binary: /usr/local/bin/arc-gateway
  • Config file: /etc/arc/arc.json
Edit your config, then start:

Service unit settings

The service unit is installed at /etc/systemd/system/arc-gateway.service. 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:
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


Kubernetes

All Kubernetes manifests are under deploy/kubernetes/. Apply them in order:

Resources created

Before you apply: set the auth token

The Secret must be created with an actual token before applying the Deployment:
Keep control_plane.auth_token in the ConfigMap consistent with this value.

ConfigMap key fields to override

Deployment spec

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:
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.