dcd exposes a Docker Engine on the Device Connect mesh as a docker_host device. Agents and other devices can provision, start/stop, inspect, tail logs, exec, and run docker compose projects over JSON-RPC — on D2D (LAN, no portal) or Portal (multi-tenant NATS + registry).
License: Apache-2.0
- Provision containers via
provision_containerwith ports, volumes, env, labels - Lifecycle: start, stop, restart, remove
- Interact:
container_logs,exec_in_container - Images:
pull_image,list_images - Compose:
compose_up/compose_down(hostdocker composeCLI) - Events:
container_state_changedwhen container state transitions - D2D and Portal: same driver; configure messaging via CLI or env (see below)
- Bare metal or in-container: mount
docker.sockwhen running inside Docker - Sim mode:
--simfor CI and development without Docker
cd ~/src/dcd
python3.12 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"Requires Python 3.12 or 3.13 and Docker Engine API access (socket or DOCKER_HOST).
No portal credentials; Zenoh multicast discovery by default:
export DEVICE_CONNECT_ALLOW_INSECURE=true
dcd --sim --device-id docker-host-1 --tenant devWith a real Docker socket:
export DEVICE_CONNECT_ALLOW_INSECURE=true
dcd --device-id docker-host-1 --tenant devInvoke from another process using device-connect-agent-tools or a second driver with invoke_remote.
Provision a device credential in the portal (or dc-portalctl devices provision), then:
dcd \
--portal \
--portal-credentials ~/.config/device-connect/my-device.creds.json \
--nats-credentials-file ~/.config/device-connect/my-device.creds.json \
--device-id my-docker-hostPortal mode sets NATS URLs from the credential file and uses registry (discovery_mode=infra) unless overridden.
Force D2D while using portal-issued creds for NATS only:
dcd --portal --discovery-mode d2d ...The image runs dcd against the host Docker Engine by mounting docker.sock (the container does not run Docker-in-Docker). The entrypoint checks that the socket is present unless DCD_SIM=true.
docker build -t dcd:local .
docker run --rm -d \
--name dcd-driver \
-v /var/run/docker.sock:/var/run/docker.sock \
-e DEVICE_CONNECT_ALLOW_INSECURE=true \
-e DCD_DEVICE_ID=docker-host-edge \
dcd:localCompose on the same machine: examples/docker-compose.dcd.yml.
The repo root compose.yaml is a Topo template (x-topo metadata) for Arm64 edge boards:
topo clone https://github.com/ericvh/dcd.git
cd dcd
topo deploy --target pi@raspberrypi.localTopo builds the image, transfers it to the target, and starts the service with docker.sock mounted so dcd manages workloads on that host. Portal setup and security notes: deploy/topo.md.
| Function | Description |
|---|---|
get_status |
Driver + engine connectivity |
ping_docker |
Refresh Docker Engine metadata |
list_containers |
List containers (all_containers) |
get_container |
Inspect by ID or name |
provision_container |
Create container from spec dict |
start_container / stop_container / restart_container / remove_container |
Lifecycle |
container_logs |
Tail logs (follow=true streams via events) |
stop_container_logs |
Stop a log follow stream |
exec_in_container |
Run one-shot command (no interactive attach/TTY) |
pull_image / list_images |
Image operations |
compose_up / compose_down |
Docker Compose projects |
list_managed_containers |
Containers with deviceconnect.dev/managed=true |
Events:
container_state_changed—container_id,name,previous_state,state,imagecontainer_log_line—container_id,line,stream(whencontainer_logsusesfollow=true)
Interactive attach sessions are not supported; use exec_in_container or log follow instead.
{
"image": "nginx:alpine",
"name": "web-1",
"ports": [{"container_port": 80, "host_port": 8080}],
"environment": {"FOO": "bar"},
"labels": {"app": "demo"}
}Managed containers automatically receive deviceconnect.dev/managed=true and deviceconnect.dev/driver=dcd labels unless device_connect_labels is set to false in the spec.
| Variable | Description |
|---|---|
DCD_DEVICE_ID / DEVICE_ID |
Device Connect device id |
DCD_TENANT / TENANT |
Tenant namespace |
DCD_SIM |
Use simulated Docker backend |
DOCKER_HOST |
Remote engine URL |
DCD_STATE_POLL_HZ |
State poll rate for events (default 2.0, 0 disables) |
DEVICE_CONNECT_ALLOW_INSECURE |
D2D dev mode |
DEVICE_CONNECT_PORTAL / DCD_PORTAL |
Enable portal defaults |
NATS_CREDENTIALS_FILE |
Portal NATS JWT creds |
DEVICE_CONNECT_DISCOVERY_MODE |
d2d, p2p, or infra |
MESSAGING_URLS / NATS_URL |
Explicit broker URLs |
pytest -v
python tests/smoke_sim_runtime.py
ruff check src testsGitHub Actions runs on every push to main, pull request, and manual workflow_dispatch:
- Ruff lint and format check (
src,tests) - Unit tests on Python 3.12 and 3.13 (simulated Docker;
pytest -m "not docker") - Contract matrix — pinned
device-connect-edgeRPC/event surface - Docker integration — DinD job with real Engine (
pytest -m docker)
Workflow: .github/workflows/ci.yml
See DESIGN.md for architecture and the Topo vs Docker tradeoff analysis.
- tcd — Device Connect driver for Arm Topo (
topo_deployer); SSH deploy to edge boards - device-connect — edge SDK and portal
- topo — underlying CLI used by tcd