Run a self-hosted agent
Pull the agent image, get its TLS certificate, and run it on your own Kubernetes cluster while Relay Cloud keeps SSO, teams, masking and audit.
A self-hosted agent runs inside your network, next to your databases. Relay Cloud stays the control plane: it keeps sign-in, teams, masking, policies and query audit.
How it connects
- The agent opens one outbound connection to
api.userelay.sh:443. Relay never connects into your network. relay connectgets a short-lived tunnel token from Relay, then dials the agent's address inside your network. Query results flow only between your users, the agent and the database.
So the agent needs egress to api.userelay.sh:443, and your users need a route
to the agent.
Before you start
Contact support@userelay.sh with:
- the AWS account ID or IAM role ARN that will pull the image (for example your EKS node role). Without an AWS account, ask for a pull-only identity;
- the DNS name your users will use to reach the agent, for example
relay-agent.example.internal.
Relay grants pull access to your private registry repository and shares its address. Each Relay release is copied there by digest, with its signatures. Mirror the image into your own registry for production, so the agent never depends on Relay's registry at runtime.
1. Get the TLS certificate
Users connect to the agent over TLS with a certificate that Relay issues. You keep the private key. Create the key and a certificate signing request for the agent's DNS name:
cat > agent.cnf <<'EOF'
[req]
distinguished_name = dn
req_extensions = ext
prompt = no
[dn]
CN = relay-agent.example.internal
[ext]
subjectAltName = DNS:relay-agent.example.internal
EOF
openssl ecparam -name prime256v1 -genkey -noout -out tls.key
openssl req -new -key tls.key -config agent.cnf -out agent.csrSend agent.csr to support. Relay returns tls.crt and the Relay CA
certificate, hybrid-agent-ca.crt. The certificate is valid for 397 days; send a
new request about 30 days before it expires.
2. Create the agent and its enrollment token
relay agents create onprem
relay enrollment-tokens create --name onprem-agents --agent onprem3. Deploy
kubectl create namespace relay
kubectl -n relay create secret tls relay-agent-tls --cert=tls.crt --key=tls.key
kubectl -n relay create secret generic relay-agent \
--from-literal=RELAY_ENROLLMENT_TOKEN='<enrollment token>' \
--from-literal=RELAY_CONTROL_PLANE_TOKEN="$(openssl rand -hex 32)"apiVersion: apps/v1
kind: Deployment
metadata: {name: relay-agent, namespace: relay}
spec:
replicas: 2
selector: {matchLabels: {app: relay-agent}}
template:
metadata: {labels: {app: relay-agent}}
spec:
securityContext: {runAsNonRoot: true, runAsUser: 65532, seccompProfile: {type: RuntimeDefault}}
containers:
- name: agent
image: <your registry>/agent-saas@sha256:<digest>
env:
- {name: RELAY_SERVER_ADDR, value: "api.userelay.sh:443"}
- {name: RELAY_SERVER_TLS, value: "true"}
- {name: RELAY_AGENT_ADVERTISE_ADDR, value: "relay-agent.example.internal:8080"}
- {name: RELAY_TUNNEL_REQUIRE_SERVER_SPEC, value: "true"}
- {name: RELAY_CONTROL_PLANE_TLS_CERT, value: /tls/tls.crt}
- {name: RELAY_CONTROL_PLANE_TLS_KEY, value: /tls/tls.key}
envFrom: [{secretRef: {name: relay-agent}}]
ports:
- {name: grpc, containerPort: 8080}
- {name: metrics, containerPort: 9090}
readinessProbe: {httpGet: {path: /readyz, port: metrics}}
livenessProbe: {httpGet: {path: /healthz, port: metrics}}
securityContext: {allowPrivilegeEscalation: false, readOnlyRootFilesystem: true, capabilities: {drop: [ALL]}}
volumeMounts: [{name: tls, mountPath: /tls, readOnly: true}, {name: tmp, mountPath: /tmp}]
volumes: [{name: tls, secret: {secretName: relay-agent-tls}}, {name: tmp, emptyDir: {}}]
---
apiVersion: v1
kind: Service
metadata: {name: relay-agent, namespace: relay}
spec:
type: LoadBalancer
selector: {app: relay-agent}
ports: [{name: grpc, port: 8080, targetPort: grpc}]Add your platform's internal load balancer annotation to the Service, and point
relay-agent.example.internal at it. Every replica advertises the same name.
Keep RELAY_CONTROL_PLANE_TOKEN set. Without it, anyone who reaches port 8080
can reconfigure the agent. Tunnels don't need it: each user is authenticated by
their Relay session.
RELAY_TUNNEL_REQUIRE_SERVER_SPEC=true makes the agent use only the connection
details Relay sends, never ones a client supplies.
4. Point users at the Relay CA
Each user trusts the Relay CA once, with any of:
export RELAY_AGENT_CA=/path/to/hybrid-agent-ca.crt
relay connect <connection> --agent-ca /path/to/hybrid-agent-ca.crtThen set the connection's agent:
relay connections agents set <connection> onprem:1relay agents get onprem shows the replicas once they enroll.
Renewing the certificate
Replace the Secret with the new certificate. The agent reloads it without a restart:
kubectl -n relay create secret tls relay-agent-tls --cert=tls.crt --key=tls.key \
--dry-run=client -o yaml | kubectl apply -f -Updating the agent
Change the image digest in the Deployment to the new release in your registry. Replicas drain open tunnels on shutdown (see Shutdown).
For every setting, see Settings for self-hosted agents.