What makes a valid stack directory
A stack is any directory under the stacks root (ZOOK_STACKS_ROOT, default
/opt/stacks) that contains a compose.yaml. That is the only requirement for
discovery. No registration step, no Zook-specific config file needed in V1.
Minimum viable stack directory:
/opt/stacks/my-app/
├── compose.yaml # must exist; must define healthcheck: for every long-running service
└── .env # optional; Zook never writes to this
Compose file requirements
Every long-running service must have a healthcheck: block (or a HEALTHCHECK
in its Dockerfile). See deploy-contract.md for why.
Use ${VERSION} to reference the version that Zook injects:
services:
api:
image: ghcr.io/example/my-app:${VERSION}
healthcheck:
test: ["CMD", "/app/healthcheck"]
interval: 5s
timeout: 5s
retries: 10
postgres:
image: postgres:16-alpine
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 10
Playing with Zook locally
You do not need /opt/stacks to experiment. Point ZOOK_STACKS_ROOT at any
scratch directory.
# Create a scratch stacks root
mkdir -p /tmp/my-stacks/hello-app
# Write a minimal compose.yaml with a healthcheck
cat > /tmp/my-stacks/hello-app/compose.yaml <<'EOF'
services:
web:
image: nginx:${VERSION}-alpine
ports:
- "8080:80"
healthcheck:
test: ["CMD", "wget", "-q", "-O", "-", "http://localhost"]
interval: 5s
timeout: 5s
retries: 5
EOF
# Use the scratch root for all commands
export ZOOK_STACKS_ROOT=/tmp/my-stacks
# Discover stacks
zook list
# hello-app
# Deploy a version
zook deploy hello-app 1.27
# Check status
zook status
# STACK VERSION STATUS
# hello-app 1.27 healthy
# View releases
zook releases hello-app
# 1.27 2026-08-13T18:00:00Z success
# View the deploy log
zook logs hello-app
State and logs
After the first deploy, hello-app will have a .zook/ directory:
/tmp/my-stacks/hello-app/
├── compose.yaml
└── .zook/
├── state.json
└── logs/
└── 20260813T180000Z-1.27.log
To start fresh, delete .zook/:
rm -rf /tmp/my-stacks/hello-app/.zook
Gitignore recommendation
If your stack directory is version-controlled, add .zook/ to .gitignore:
# .gitignore
.zook/
state.json and logs are runtime artifacts — they belong on the server, not in
source control.
Adjusting the health timeout
If your images take longer than 60 seconds to become healthy (e.g. a cold JVM start), increase the timeout:
ZOOK_TIMEOUT=120 zook deploy my-app v2.0.0
Or export it for a whole session:
export ZOOK_TIMEOUT=120
zook deploy my-app v2.0.0