Imported from yxtay/docker-stacks (
AGENTS.md). Install upstream withnpx skills add yxtay/docker-stacks. Copyright stays with the author.
AI Agent Instructions
Always review README.md and update it and AGENTS.md. AGENTS.md should not repeat information that is already in README.md and should include information relevant to agents only.
Agent Workflows
Pre-commit
Agents should ensure that pre-commit hooks are installed and run before submitting changes.
- Install:
pre-commit install - Run on all files:
pre-commit run --all-files
CI & Linting
- MegaLinter: If MegaLinter fails in CI, check the
megalinter-reports/directory (if available in artifacts) or logs for specific failures. - Renovate: Be aware that Renovate is configured to automerge minor and digest updates.
Conventions
- Commits should follow conventional commits style.
- Formatting is enforced via pre-commit hooks for YAML, Shell scripts (shfmt), and Markdown.
Docker Stacks & Portainer
-
For Docker Compose files (managed via Portainer):
-
Do not set
container_name. -
Use
exposeinstead ofportsfor port configuration. -
Avoid unnecessary quoting in
compose.yaml; use double quotes only when strictly required (e.g., URLs with colons). -
Every service should have a
healthcheck. Only add one if the container image does not already define a healthcheck. Specify only thetestcommand, leavinginterval,timeout,retriesas defaults. -
Prefer the service's built-in health check command when available (e.g.,
redis-cli ping,pg_isready,/dozzle healthcheck,/beszel health). Fall back tocurl -fsSLorwget -qO-for HTTP checks. -
Prefer
ghcr.iooverdocker.ioregistry. -
Use host bind mounts under
/apps/<service-name>/instead of named volumes. -
All stacks join the
caddyexternal network for Caddy integration. -
Caddy reverse proxy labels follow this pattern:
labels: caddy: "*.{$$DOMAIN}" caddy.import: reverse_proxy <service-name> <service-name>:<port> -
Use
depends_onwithcondition: service_healthyorcondition: service_startedwhen a service requires another to be ready. -
Images must support
linux/arm64. Prefer version tags if they are still maintained (release within 3 months compared tolatesttag). Otherwise, uselatesttag. Digests will be added by Renovate bot.
-
-
Security hardening (apply to all services where possible):
-
Add
security_opt: [no-new-privileges:true]to every service. -
Add
cap_drop: [ALL]and explicitlycap_addonly required capabilities:- LinuxServer.io images:
CHOWN, DAC_OVERRIDE, SETGID, SETUID - PostgreSQL/Redis:
CHOWN, DAC_OVERRIDE, SETGID, SETUID - Network services (ports < 1024):
NET_BIND_SERVICE - VPN/firewall:
NET_ADMIN, NET_RAW - FUSE mounts:
CHOWN, DAC_OVERRIDE, SYS_ADMINwithsecurity_opt: [apparmor:unconfined]anddevices: [/dev/fuse:/dev/fuse:rwm]
- LinuxServer.io images:
-
Add
read_only: truewithtmpfs: [/run:exec]. Only add/tmpto tmpfs if the service actually writes temp files (test first). Mount writable paths as volumes. -
LinuxServer.io images:
PUID/PGIDhas no effect underread_only: true(container runs as UID 911). Images handle ownership of volumes mounted to/configautomatically. -
Add resource limits to every service:
deploy: resources: limits: cpus: 1 memory: 4g pids: 512
-
-
Environment variables:
- Use YAML anchors (
&envs) for sharedPUID/PGID/TZin stacks with multiple LinuxServer.io containers. - Mark required variables with
${VAR:?}(fail-fast if unset). - Mark optional variables with
${VAR:-default}.
- Use YAML anchors (
-
Volume mount propagation:
- Use
rslavefor mounts that receive FUSE unmounts from host (e.g.,/mnt/remote:/mnt/remote:rslavein arr containers). - Use
rsharedfor mounts that propagate FUSE mounts to other containers (e.g., rclone service mounting to/mnt/remote:rshared).
- Use
-
Caddy labels:
- Protected services:
caddy.import: reverse_proxy_auth <name> <name>:<port> - Public services:
caddy.import: reverse_proxy <name> <name>:<port> - API path bypass (tinyauth):
tinyauth.apps.<name>.path.allow: \/api
- Protected services:
-
Network patterns:
- All web services join external
caddynetwork. - Services needing Docker API access use a dedicated
socket-proxyinternal network (never mount docker.sock directly in app containers). - Use
network_mode: hostonly for services requiring host network access (home automation, firewall bouncers, system monitors).
- All web services join external
-
Key ordering in compose files:
- Top-level:
services,volumes,networks,secrets,configs. - Service-level (grouped by concern):
- Identity:
image,build,pull_policy,platform,profiles - Execution:
entrypoint,command,working_dir,user,init,tty,stdin_open - Dependencies:
depends_on,extends - Configuration:
environment,env_file,secrets,configs - Runtime:
cap_add,cap_drop,security_opt,devices,privileged,read_only,shm_size,ulimits,gpus,group_add,sysctls,pid,ipc,uts,userns_mode - Storage:
volumes,tmpfs - Networking:
ports,expose,hostname,networks,network_mode,extra_hosts,dns - Lifecycle:
healthcheck,restart,deploy,stop_grace_period,stop_signal,post_start,pre_stop - Metadata:
labels,annotations,logging
- Identity:
- Top-level:
