docker-for-agents
Use when containerising an agent with Docker Compose. Compose template with env_file, restart policy, healthcheck, volumes and log limits, a .dockerignore, secrets kept out of images, pinned tags, and
Files1
View on GitHubSKILL.mdiclone/skills/docker-for-agents/SKILL.md
GitHubDocker for agents — small, pinned, non-root containers
When to use
- The owner wants an agent, bot or tool isolated in a container, or runs several services on one server.
- A container restarts in a loop, fills the disk with logs, or leaks a secret into an image.
Docker is not bundled with iclone os: every command here is an example and docker compose --help plus the Compose spec are the truth. A plain systemd unit (server-agent-service) is lighter when one service is all you run.
Prerequisites
- Installing Docker needs
installSoftware; building, starting or removing containers needsterminal. Ask through the consent door (~/.iclone-os/bin/acp consent request installSoftware --reason "…"), name the host and the service, and wait for the owner's yes.
Quick reference
| Rule | Why |
|---|---|
image: <name>:<version> (or @sha256: digest), never latest |
same bits on every restart |
restart: unless-stopped + healthcheck |
comes back, and you can see when it is sick |
user: non-root, read_only: true, cap_drop: [ALL] |
a compromised agent cannot own the host |
env_file: 0600, outside git |
secrets never in the image or the compose file |
log max-size / max-file |
json logs otherwise grow until the disk is full |
Procedure
.dockerignorenext to the Dockerfile, at least:.git,.env*,*.log,node_modules,__pycache__,*.pem,*.key.- Dockerfile: pinned base image, dependencies first,
USERnon-root at the end. Never pass secrets withARGorENV: they stay in the layers (docker history). compose.yaml:
# example
services:
<service>:
image: <registry>/<service>:<version>
build: .
env_file: ./<service>.env
restart: unless-stopped
user: "10001:10001"
read_only: true
tmpfs: [/tmp]
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
volumes:
- <service>-data:/data
healthcheck:
test: ["CMD", "<health-command>"]
interval: 30s
timeout: 5s
retries: 3
logging:
driver: json-file
options: { max-size: "10m", max-file: "3" }
volumes:
<service>-data:
- Secrets file, then validate and start:
chmod 600 <service>.env && docker compose config --quiet && docker compose up -d --build # example
- Daily commands:
docker compose ps && docker compose logs -f --tail 200 <service> # example
- Update: bump the tag in
compose.yaml, then:
docker compose pull --ignore-buildable && docker compose up -d # example
- Stop:
docker compose downkeeps named volumes.down -vdeletes the data: owner's yes, afterbackups-restore.
Pitfalls
- Published ports skip ufw: Docker writes its own firewall rules. Publish only what must be public; for a reverse proxy on the same host, bind with
host_ipset to the loopback address. - A loop of restarts with no logs: the healthcheck command itself is missing in the image.
- A new named volume copies the owner of the image's mount dir; if
/datadoes not exist in the image it is root-owned anduser: "10001:10001"cannot write it. Create it in the Dockerfile owned by that uid (RUN mkdir /data && chown 10001:10001 /data) before the firstup. read_only: truebreaks programs that write into their install dir; give them a volume ortmpfs.- Disk full:
docker system df, then prune unused images with the owner's yes (server-troubleshooting).
Never
- Never mount
/var/run/docker.sockinto an agent container or run itprivileged: both equal root on the host. - Never add an untrusted user to the
dockergroup (same as root). - Never bake
.env, keys or tokens into an image or push such an image.
Verification
docker compose ps --format '{{.Name}} {{.Status}}' # example
Pass = every service shows Up with (healthy), and docker history <registry>/<service>:<version> shows no secret values.