Docker & Network

Network topology#

graph TB subgraph host["HOST"] CLI["glci CLI"] Daemon["glci Daemon
talks to Docker via socket / TCP / SSH"] end subgraph pipeline["Per-pipeline network · glci-net-<pipelineID>"] Mock["glci-mock · port 39741
───
Runner API · Git HTTP
S3 cache · OCI registry"] subgraph shared_runner["glci-runner container (shared)"] R1["[[runners]] #1
token-A · tags: gpu"] R2["[[runners]] #2
token-B · tags: default"] RN["[[runners]] #N
…"] end subgraph project_runner["glci-runner-<project> container (optional)"] PR1["[[runners]] #1
per-project token · tags"] end end subgraph build["Per-build network · FF_NETWORK_PER_BUILD"] Job["Job container
user Docker image"] Svc["Service sidecars
postgres, redis, …"] end subgraph dind["Per-DinD-job network · glci-net-<pipelineID>-job-<jobID>"] DJob["DinD job container"] DinDSvc["docker:dind
TLS :2376 · plain :2375"] DMock["glci-mock
(attached to network)"] end CLI -->|"Unix socket
~/.glci/daemon.sock"| Daemon Daemon -.->|"docker exec -i
muxproto binary protocol"| Mock Daemon -.->|"config volume · SIGHUP"| shared_runner Daemon -.->|"config volume · SIGHUP"| project_runner R1 -->|"Docker DNS
glci-mock:39741"| Mock R2 -->|"Docker DNS
glci-mock:39741"| Mock RN -->|"Docker DNS"| Mock PR1 -->|"Docker DNS
glci-mock:39741"| Mock shared_runner -->|"Docker executor"| Job project_runner -->|"Docker executor"| Job shared_runner -->|"Docker executor
(DinD jobs)"| DJob Job -->|"host.docker.internal
host-gateway → :39741"| Mock Job <-.->|"DNS aliases"| Svc DJob -->|"DOCKER_HOST
tcp://docker:2376"| DinDSvc DinDSvc -->|"insecure-registries
pull / push"| DMock DMock -.-|"same container"| Mock style host fill:#f5f5f5,stroke:#666,stroke-width:1px style pipeline fill:#e8f4fd,stroke:#3b82f6,stroke-width:2px style shared_runner fill:#dbeafe,stroke:#60a5fa,stroke-width:1px,stroke-dasharray:5 5 style project_runner fill:#dbeafe,stroke:#60a5fa,stroke-width:1px,stroke-dasharray:5 5 style build fill:#ecfdf5,stroke:#10b981,stroke-width:2px style dind fill:#fef3c7,stroke:#f59e0b,stroke-width:2px

Three network layers exist at runtime:

NetworkScopeContainersPurpose
glci-net-{pipelineID}Per pipelineglci-mock, glci-runner, glci-runner-<project> (if any)Runners poll mock for jobs via Docker DNS (glci-mock:39741)
Per-build networkPer jobJob container, service sidecarsService DNS aliases (postgres, redis, etc.)
glci-net-{pipelineID}-job-{jobID}Per DinD jobDinD job, docker:dind, glci-mock (attached)Isolates DinD docker alias; gives DinD access to the embedded registry

How each component reaches the mock server:

Docker host configuration#

Configure the Docker daemon endpoint in ~/.glci/config.toml:

# ~/.glci/config.toml
[docker]
host = "tcp://remote-host:2375"   # or "ssh://remote-host"
container_socket = "/var/run/docker.sock"
privileged = true                  # default; set false to disable --privileged

Supported schemes for host:

The container_socket setting controls the Docker socket path mounted inside runner containers. This is auto-detected by default but may need manual configuration when the Docker daemon uses a non-standard socket path.

Set privileged = false to disable the --privileged flag on runner containers. This may be required on some security-hardened systems, but will break Docker-in-Docker pipelines.

Docker-in-Docker#

For CI pipelines that use Docker-in-Docker services (docker:dind), glci automatically:

Build layer cache#

glci does not inject BuildKit cache flags automatically. Use --cache-from / --cache-to in your .gitlab-ci.yml as you would on GitLab CI. Since the embedded registry persists across runs, type=registry cache references work without extra setup.

Network configuration#

Fine-tune how containers reach the mock server, registry, and host services:

# ~/.glci/config.toml
[network]
mock_server_bind = "0.0.0.0:0"         # mock server listen address (default)
mock_server_port = 39741                # host port for mock server (default); change requires daemon restart
container_host = "host.docker.internal" # hostname containers use for host (default)
host_gateway = "auto"                   # extra_hosts value for container_host (default)
registry_bind = "127.0.0.1:0"          # registry HTTPS listener (default)
registry_http_bind = "0.0.0.0:0"       # registry HTTP listener (default)
daemon_socket = "~/.glci/daemon.sock"   # Unix socket path (default)

[network.extra_hosts]
entries = ["internal-registry.corp:10.0.0.50"]  # additional /etc/hosts entries for containers

The container_host defaults to host.docker.internal, which resolves natively on Docker Desktop (macOS/Windows) and on Linux Docker 20.10+. On older Linux, glci adds an extra_hosts entry mapping it to the Docker bridge IP.

The mock_server_port is the host port that CI job containers use to reach the mock server via Docker’s host-gateway. This port must be available on every Docker host where glci runs. Changing it requires a full daemon restart (glci daemon stop && glci daemon start).

Use glci config --network to see all resolved network settings:

glci config --network

CI cache#

GitLab CI cache: keywords work the same as production, including key: files: (MD5-based), prefix:, and fallback_keys:. Entries persist across pipeline runs via daemon-managed disk storage on the glci-cache Docker volume.

[cache]
persistent = true       # default; set false to disable
max_size = "500MB"      # reject uploads when exceeded (default: 500MB)
ttl = "24h"             # entry expiry (default: 24h)
glci system cache clean     # wipe all CI cache entries

Setting persistent = false disables cross-pipeline caching entirely; each pipeline starts with cold caches.

System management#

# Disk usage of all glci resources
glci system df

# Remove unused containers, networks, volumes, and CI cache
glci system prune

# Remove everything including registry data and pipeline history
glci system prune --all

# Skip the confirmation prompt
glci system prune --force

# Manage daemon logs
glci system logs info       # show log file path and size
glci system logs clean      # remove the log file

# Inspect Docker resources
glci system docker containers
glci system docker networks
glci system docker volumes

glci system prune (without --all) removes stopped containers, unused networks, orphaned volumes, the bare repo cache, and the daemon log. Persistent daemon volumes (CI cache, registry storage, registry CA) are preserved.

glci system prune --all additionally removes registry data, pipeline history, and all persistent volumes. The daemon is automatically stopped and restarted if it was running.

Daemon#

The daemon runs automatically — you rarely need these commands:

glci daemon status    # show PID, uptime, active pipelines, commit
glci daemon stop      # graceful shutdown (waits for running jobs)
glci daemon start     # manually start
glci daemon logs      # show last 50 lines
glci daemon logs -F   # follow in real time
glci daemon logs -n 100   # show last 100 lines

The daemon auto-stops after 30 minutes of idle (configurable via [daemon] idle_timeout). After make install, the next CLI invocation automatically restarts the daemon if built from a different commit.

Configure daemon settings in ~/.glci/config.toml:

[daemon]
idle_timeout = "1h"       # default: 30m
socket = "~/.glci/daemon.sock"
log_file = "~/.glci/daemon.log"
max_log_size = "50MB"     # rotate when log exceeds this size (default: 50MB, "0" to disable)
max_log_files = 3         # rotated backups to keep (default: 3)

Log rotation happens at daemon startup. When daemon.log exceeds max_log_size, it is renamed to daemon.log.1 (shifting older backups up to .N), and a fresh log is started. Set max_log_size = "0" to disable rotation.

Crash recovery#

On startup the daemon checks for pipelines whose Docker containers survived the crash. If both the mock server and runner containers are still alive, the daemon resumes execution — re-attaching to running jobs and dispatching pending ones. Pipelines whose containers died are marked as failed. Orphaned containers are cleaned up automatically.

Version#

glci version
# glci  commit abc123def456...
# daemon commit abc123def456... (pid 12345, up 2h15m)
Esc