Skip to main content
Version: 1.7.0

Stacks (Hub-Managed)

Hub Feature — v1.7.0+

Stacks are deployed and managed through Thresh Hub — the web dashboard and REST API. The CLI interacts with stacks indirectly through thresh node and thresh cluster commands.

Stacks let you define multi-service applications in a single JSON file and deploy them to fleet nodes through the Hub. The Hub resolves service dependencies, injects environment variables, and dispatches the deployment to online agents.

How Stacks Work​

Stack Definition Format​

Create a JSON file defining your services:

{
"name": "my-stack",
"services": {
"db": {
"image": "docker:postgres:16-alpine",
"ports": ["5432:5432"],
"env": {
"POSTGRES_DB": "myapp",
"POSTGRES_USER": "admin",
"POSTGRES_PASSWORD": "secret"
}
},
"api": {
"image": "docker:my-org/api:latest",
"ports": ["8080:8080"],
"depends_on": ["db"],
"env": {
"DATABASE_HOST": "${db.host}",
"DATABASE_PORT": "${db.port}"
}
},
"web": {
"image": "docker:my-org/frontend:latest",
"ports": ["3000:3000"],
"depends_on": ["api"]
}
},
"traefik": true
}

Service Fields​

FieldTypeDescription
imagestringOCI image reference — prefix with docker: for registry pulls
portsstring[]Port mappings in host:container format
envobjectEnvironment variables — supports ${service.host} / ${service.port} injection
depends_onstring[]Services that must start before this one
volumesstring[]Named volumes in name:mountPath format

Top-Level Fields​

FieldTypeDescription
namestringStack name (must be unique per account)
servicesobjectMap of service name → service definition
traefikboolAuto-deploy Traefik v3.3 as reverse proxy

Key Features​

  • Dependency ordering — depends_on is resolved via topological sort; db starts before api, api before web
  • Variable injection — ${db.host} and ${db.port} are resolved at deploy time so services discover each other automatically
  • Traefik auto-deploy — set "traefik": true to add dynamic routing and TLS termination
  • Rolling updates — update a single service image through the Hub UI without tearing down the entire stack
  • Volume persistence — stopping a stack preserves volumes; destroying removes them

Deploying Stacks via Hub UI​

  1. Log in to Thresh Hub at https://your-hub:7200
  2. Navigate to Stacks → Deploy New Stack
  3. Upload or paste your stack JSON definition
  4. Select the target node (or let the Hub choose an online node)
  5. Click Deploy — the Hub dispatches the stack to the agent

Deploying Stacks via Hub API​

# Authenticate
TOKEN=$(thresh auth token)

# Deploy a stack
curl -X POST https://your-hub:7200/api/stacks/deploy \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d @mystack.json

# List stacks
curl -H "Authorization: Bearer $TOKEN" \
https://your-hub:7200/api/stacks

# Get stack info
curl -H "Authorization: Bearer $TOKEN" \
https://your-hub:7200/api/stacks/my-stack

# Stop a stack (preserves volumes)
curl -X POST https://your-hub:7200/api/stacks/my-stack/stop \
-H "Authorization: Bearer $TOKEN"

# Destroy a stack (removes volumes)
curl -X DELETE https://your-hub:7200/api/stacks/my-stack \
-H "Authorization: Bearer $TOKEN"

CLI Workflow​

While stacks are managed through the Hub, the CLI provides the building blocks:

# 1. Authenticate with your Hub
thresh auth login --hub https://your-hub:7200

# 2. Check which nodes are available
thresh node list

# 3. Deploy individual blueprints to specific nodes
thresh node up thresh-node-1 python-dev --name ml-training

# 4. Check node status and metrics
thresh node info thresh-node-1
thresh node metrics thresh-node-1

# 5. Organize nodes into clusters
thresh cluster create production
thresh cluster add-node production thresh-node-1
thresh cluster info production

For multi-service stack deployments with dependency ordering, use the Hub UI or API as shown above.

Example: Full-Stack Web App​

{
"name": "webapp",
"services": {
"postgres": {
"image": "docker:postgres:16-alpine",
"ports": ["5432:5432"],
"volumes": ["pgdata:/var/lib/postgresql/data"],
"env": {
"POSTGRES_DB": "webapp",
"POSTGRES_USER": "app",
"POSTGRES_PASSWORD": "secret"
}
},
"redis": {
"image": "docker:redis:7-alpine",
"ports": ["6379:6379"]
},
"api": {
"image": "docker:my-org/api:v1.2",
"ports": ["8080:8080"],
"depends_on": ["postgres", "redis"],
"env": {
"DATABASE_URL": "postgres://app:secret@${postgres.host}:${postgres.port}/webapp",
"REDIS_URL": "redis://${redis.host}:${redis.port}"
}
},
"web": {
"image": "docker:my-org/frontend:v1.2",
"ports": ["3000:3000"],
"depends_on": ["api"],
"env": {
"API_URL": "http://${api.host}:${api.port}"
}
}
},
"traefik": true
}

Deploy order: postgres → redis → api → web → traefik

See Also​