Integrations
Docker
sysg runs inside a container the same way it runs on a laptop: one binary, one
manifest, one supervisor. There are two useful shapes — sysg as the container's
init process, or sysg alongside an existing entrypoint — and in both you drive it
from the host with docker exec.
Put sysg in the image
The installer drops the binary in ~/.local/bin, which is not on PATH for a
container's ENTRYPOINT, so copy it somewhere that is:
RUN curl --proto '=https' --tlsv1.2 -fsSL https://sh.sysg.dev/ | sh \
&& install -m 0755 /root/.local/bin/sysg /usr/local/bin/sysg
sysg as PID 1
sysg init boots the manifest, reaps orphans, and tears the stack down in
reverse dependency order when the container is stopped — replacing a
tini + supervisord pairing with one binary.
FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates curl \
&& rm -rf /var/lib/apt/lists/*
RUN curl --proto '=https' --tlsv1.2 -fsSL https://sh.sysg.dev/ | sh \
&& install -m 0755 /root/.local/bin/sysg /usr/local/bin/sysg
COPY systemg.yaml /etc/systemg/systemg.yaml
ENTRYPOINT ["sysg", "init", "-c", "/etc/systemg/systemg.yaml"]
version: "2"
projects:
app:
services:
db:
command: "sleep 3000"
api:
command: "sleep 3000"
depends_on: [db]
$ docker build -t myapp .
$ docker run -d --name myapp myapp
docker stop sends SIGTERM to PID 1, sysg stops every service in reverse
dependency order, and the container's exit code is the teardown verdict — 0
when everything died, non-zero (SG0713) when
something survived.
$ docker stop myapp && docker inspect -f '{{.State.ExitCode}}' myapp
0
Do not add --init
sysg init must actually be PID 1. docker run --init inserts tini ahead of it
and sysg refuses to boot with
SG0711.
sysg beside an existing entrypoint
If the container already has an entrypoint of its own — a dev container, a CI image, an app image you don't want to restructure — start sysg from it and leave PID 1 alone:
ENTRYPOINT ["sh", "-c", "sysg --sys start -c /etc/systemg/systemg.yaml --daemonize && exec my-app"]
The supervisor still starts, restarts, and logs your services. What you give up
is shutdown: SIGTERM goes to the entrypoint, not to sysg, so docker stop
usually ends in SIGKILL (exit 137) with no ordered teardown. Call
sysg --sys stop --supervisor yourself if that matters.
Drive it from the host
The control channel is a unix socket inside the container, so docker exec is
how you reach it:
$ docker exec myapp sysg --sys status
$ docker exec myapp sysg --sys logs -s api --no-follow
$ docker exec myapp sysg --sys restart -s api
For scripts and agents, ask for stable output:
$ docker exec myapp sysg --sys status --format json | jq '.units[].state'
$ docker exec myapp sysg --sys status --plain
Why --sys
Compose
services:
app:
build: .
stop_grace_period: 30s
volumes:
- sysg-logs:/var/log/systemg
volumes:
sysg-logs:
$ docker compose exec app sysg --sys status
stop_grace_period should exceed your slowest service's shutdown, or Docker
kills the container mid-teardown. Mounting /var/log/systemg keeps service logs
after the container is gone; mount /var/lib/systemg too if you want supervisor
state to survive a recreate.
Running the CLI on the host
Reaching a container's supervisor from a host sysg — by sharing its socket over
a bind mount — is not supported. The client and supervisor disagree about PIDs
across the namespace boundary, and Docker Desktop cannot carry a unix socket
across its VM at all. Use docker exec.
See also
- Container-Init (PID 1) — what changes when
sysg is PID 1, and the
SG0711–SG0714diagnostics. - System Mode — what
--sysmeans outside a container.