Skip to content
Trailer.devDocumentation

Remote Docker Daemons

The Docker agent does not have to run on the same machine as the Docker daemon it manages. The agent talks to Docker through the standard Docker client configuration, so it honors the same DOCKER_HOST environment variable that the docker CLI does. Point it at a remote daemon and every workspace, image build, network, and volume it manages is created on that remote machine.

This page covers the supported connection methods, which of them work from the containerized agent, and what changes when the daemon is remote.

On startup the agent resolves its Docker endpoint exactly like the docker CLI with the default context:

  1. If DOCKER_HOST is set, that address is used. DOCKER_TLS_VERIFY and DOCKER_CERT_PATH are honored for TLS connections.
  2. Otherwise it falls back to the local socket, /var/run/docker.sock on Linux.

These variables are documented in Docker’s CLI environment variables reference.

If the agent runs as a native binary, SSH is usually the most convenient transport. It needs no daemon-side configuration beyond a user that can reach the Docker socket:

Terminal window
DOCKER_HOST=ssh://build-box ./trailer.dev -S https://server.example.com

Requirements:

  • An ssh client installed on the machine running the agent. The Docker client does not implement SSH itself. It shells out to the ssh binary.
  • Non-interactive authentication to the remote machine (an SSH key loaded in an agent or referenced from ~/.ssh/config). The agent cannot answer password prompts.
  • The remote user must be allowed to use Docker, typically by membership in the docker group.

Host aliases, usernames, ports, and identity files from ~/.ssh/config all apply, so ssh://build-box can carry as much connection detail as you like. You can also spell it out inline: ssh://deploy@build-box.internal:2222.

Why SSH does not work from the agent container

Section titled “Why SSH does not work from the agent container”

The following looks reasonable but fails:

Terminal window
# This does NOT work.
docker run -d \
-e DOCKER_HOST=ssh://build-box \
-v ~/.ssh:/root/.ssh \
ghcr.io/trailer-dev/agent:latest \
-S https://server.example.com

The agent container image is intentionally minimal and does not ship an ssh client. Since the Docker client delegates ssh:// connections to the ssh binary, the connection fails inside the container no matter what keys you mount.

If you want a containerized agent to reach a daemon over SSH, keep the SSH tunnel outside the container and hand the container a plain socket. Forward the remote Docker socket to a local one:

Terminal window
ssh -nNT -L /tmp/build-box-docker.sock:/var/run/docker.sock build-box &

Then mount the forwarded socket where the agent expects it:

Terminal window
docker run -d \
-v /tmp/build-box-docker.sock:/var/run/docker.sock \
ghcr.io/trailer-dev/agent:latest \
-S https://server.example.com

From the agent’s point of view this is a local daemon. Alternatively, use a TCP transport as described below. TCP needs no extra binaries in the container.

For a daemon that exposes a TLS-protected TCP endpoint (conventionally port 2376), set the three standard variables:

Terminal window
DOCKER_HOST=tcp://build-box.internal:2376 \
DOCKER_TLS_VERIFY=1 \
DOCKER_CERT_PATH=~/.docker/build-box-certs \
./trailer.dev -S https://server.example.com

DOCKER_CERT_PATH must contain the usual ca.pem, cert.pem, and key.pem. Setting up the daemon side (server certificate, client certificate authority) is standard Docker daemon TLS configuration and is covered by Docker’s own documentation.

This transport works from the agent container as well. Mount the certificates and pass the variables:

Terminal window
docker run -d \
-e DOCKER_HOST=tcp://build-box.internal:2376 \
-e DOCKER_TLS_VERIFY=1 \
-e DOCKER_CERT_PATH=/certs \
-v ~/.docker/build-box-certs:/certs:ro \
ghcr.io/trailer-dev/agent:latest \
-S https://server.example.com

A daemon can also listen on an unencrypted, unauthenticated TCP port (conventionally 2375):

Terminal window
DOCKER_HOST=tcp://10.0.0.5:2375 ./trailer.dev -S https://server.example.com
  • unix:///some/path.sock: any Unix socket path, not just the default one. Useful for sockets forwarded over SSH (see above) or provided by socket proxies.
  • npipe:////./pipe/docker_engine: Windows named pipe, for an agent binary running on Windows against a local Docker Desktop or Docker Engine.
  • fd://: a socket inherited through systemd socket activation. Local only.

Everything the agent manages lives on the daemon’s machine, while the agent process itself stays where you started it. A few consequences are worth knowing:

  • Workspaces run on the daemon’s machine. Published ports bind to that machine’s interfaces, and workspace URLs route to it. When configuring DNS or firewalls, think in terms of the daemon host, not the agent host.
  • GPU, KVM, and other hardware features refer to the daemon’s machine. Device detection and driver setup run as containers on the daemon, so they see and configure the hardware that is actually next to the containers.
  • Host metrics come from the agent’s machine. CPU, memory, network, and disk figures on the host detail page are read from the machine running the agent process, not from the daemon’s machine. With a remote daemon these numbers describe the wrong box. Per-container metrics are collected through the Docker API and remain accurate.
  • The host name defaults to the agent’s machine. Set a deployment name (-D) that identifies the daemon machine so the hosts list stays readable.
  • Builds upload their context over the connection. Image builds stream the build context to the remote daemon, and logs, exec sessions, and attach sessions stream back. A slow link between agent and daemon makes all of these slower.
  • One agent manages one daemon. To manage several remote daemons, run one agent per daemon, each with its own DOCKER_HOST.